Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fix SHA1 hashing logic for codeCache and add a --sha1 flag to jrubyc …

…to do the same thing.
  • Loading branch information...
commit 440f043b5f47176e7d4b7afd4ecaf793e3230f30 1 parent a1e0c4d
@headius headius authored
View
89 lib/ruby/site_ruby/shared/jruby/compiler.rb
@@ -1,5 +1,6 @@
require 'optparse'
require 'fileutils'
+require 'digest/sha1'
require 'jruby'
require 'jruby/compiler/java_class'
@@ -11,46 +12,59 @@ module JRuby::Compiler
JavaFile = java.io.File
MethodSignatureNode = org.jruby.ast.java_signature.MethodSignatureNode
DEFAULT_PREFIX = ""
+
+ def default_options
+ {
+ :basedir => Dir.pwd,
+ :prefix => DEFAULT_PREFIX,
+ :target => Dir.pwd,
+ :java => false,
+ :javac => false,
+ :classpath => [],
+ :javac_options => [],
+ :sha1 => false,
+ :handles => false
+ }
+ end
+ module_function :default_options
def compile_argv(argv)
- basedir = Dir.pwd
- prefix = DEFAULT_PREFIX
- target = Dir.pwd
- java = false
- javac = false
- classpath = []
- javac_options = []
-
- opt_parser = OptionParser.new("", 24, ' ') do |opts|
+ options = default_options
+
+ OptionParser.new("", 24, ' ') do |opts|
opts.banner = "jrubyc [options] (FILE|DIRECTORY)"
opts.separator ""
opts.on("-d", "--dir DIR", "Use DIR as the root of the compiled package and filename") do |dir|
- basedir = dir
+ options[:basedir] = dir
end
opts.on("-p", "--prefix PREFIX", "Prepend PREFIX to the file path and package. Default is no prefix.") do |pre|
- prefix = pre
+ options[:prefix] = pre
end
opts.on("-t", "--target TARGET", "Output files to TARGET directory") do |tgt|
- target = tgt
+ options[:target] = tgt
end
opts.on("-J OPTION", "Pass OPTION to javac for javac compiles") do |tgt|
- javac_options << tgt
+ options[:javac_options] << tgt
end
opts.on("--java", "Generate .java classes to accompany the script") do
- java = true
+ options[:java] = true
end
opts.on("--javac", "Generate and compile .java classes to accompany the script") do
- javac = true
+ options[:javac] = true
end
opts.on("-c", "--classpath CLASSPATH", "Add a jar to the classpath for building") do |cp|
- classpath.concat cp.split(':')
+ options[:classpath].concat cp.split(':')
+ end
+
+ opts.on("--sha1", "Compile to a class named using the SHA1 hash of the source file") do
+ options[:sha1] = true
end
opts.parse!(argv)
@@ -60,15 +74,31 @@ def compile_argv(argv)
raise "No files or directories specified"
end
- compile_files(argv, basedir, prefix, target, java, javac, javac_options, classpath)
+ compile_files_with_options(argv, options)
end
module_function :compile_argv
+ # deprecated, but retained for backward compatibility
def compile_files(filenames, basedir = Dir.pwd, prefix = DEFAULT_PREFIX, target = Dir.pwd, java = false, javac = false, javac_options = [], classpath = [])
+ compile_files_with_options(
+ filenames,
+ :basedir => basedir,
+ :prefix => prefix,
+ :target => target,
+ :java => java,
+ :javac => javac,
+ :javac_options => javac_options,
+ :classpath => classpath,
+ :sha1 => false
+ )
+ end
+ module_function :compile_files
+
+ def compile_files_with_options(filenames, options = default_options)
runtime = JRuby.runtime
- unless File.exist? target
- raise "Target dir not found: #{target}"
+ unless File.exist? options[:target]
+ raise "Target dir not found: #{options[:target]}"
end
files = []
@@ -78,17 +108,22 @@ def compile_files(filenames, basedir = Dir.pwd, prefix = DEFAULT_PREFIX, target
begin
file = File.open(filename)
- pathname = Mangler.mangle_filename_for_classpath(filename, basedir, prefix)
+ if options[:sha1]
+ pathname = "ruby.jit.FILE_" + Digest::SHA1.hexdigest(File.read(filename)).upcase
+ puts File.read(filename)
+ else
+ pathname = Mangler.mangle_filename_for_classpath(filename, options[:basedir], options[:prefix])
+ end
inspector = org.jruby.compiler.ASTInspector.new
source = file.read
node = runtime.parse_file(BAIS.new(source.to_java_bytes), filename, nil)
- if java || javac
+ if options[:java] || options[:javac]
ruby_script = JavaGenerator.generate_java(node, filename)
ruby_script.classes.each do |cls|
- java_dir = File.join(target, cls.package.gsub('.', '/'))
+ java_dir = File.join(options[:target], cls.package.gsub('.', '/'))
FileUtils.mkdir_p java_dir
@@ -106,11 +141,11 @@ def compile_files(filenames, basedir = Dir.pwd, prefix = DEFAULT_PREFIX, target
inspector.inspect(node)
- asmCompiler = BytecodeCompiler.new(pathname, filename)
+ asmCompiler = BytecodeCompiler.new(pathname.gsub(".", "/"), filename)
compiler = ASTCompiler.new
compiler.compile_root(node, asmCompiler, inspector)
- asmCompiler.write_class(JavaFile.new(target))
+ asmCompiler.write_class(JavaFile.new(options[:target]))
end
0
@@ -142,13 +177,13 @@ def compile_files(filenames, basedir = Dir.pwd, prefix = DEFAULT_PREFIX, target
end
end
- if javac
- javac_string = JavaGenerator.generate_javac(files, javac_options, classpath, target)
+ if options[:javac]
+ javac_string = JavaGenerator.generate_javac(files, options)
puts javac_string
system javac_string
end
errors
end
- module_function :compile_files
+ module_function :compile_files_with_options
end
View
930 lib/ruby/site_ruby/shared/jruby/compiler/java_class.rb
@@ -1,637 +1,637 @@
module JRuby::Compiler
- class RubyScript
- BASE_IMPORTS = [
- "org.jruby.Ruby",
- "org.jruby.RubyObject",
- "org.jruby.javasupport.util.RuntimeHelpers",
- "org.jruby.runtime.builtin.IRubyObject",
- "org.jruby.javasupport.JavaUtil",
- "org.jruby.RubyClass"
- ]
+ module JavaGenerator
+ module_function
- def initialize(script_name, imports = BASE_IMPORTS)
- @classes = []
- @script_name = script_name
- @imports = imports
- @requires = []
- @package = ""
- end
+ def generate_java(node, script_name = nil)
+ walker = ClassNodeWalker.new(script_name)
- attr_accessor :classes, :imports, :script_name, :requires, :package
+ node.accept(walker)
- def add_import(name)
- @imports << name
+ walker.script
end
- def add_require(require)
- @requires << require
- end
+ def generate_javac(files, options)
+ files_string = files.join(' ')
+ jruby_jar, = ['jruby.jar', 'jruby-complete.jar'].select do |jar|
+ File.exist? "#{ENV_JAVA['jruby.home']}/lib/#{jar}"
+ end
+ classpath_string = options[:classpath].size > 0 ? options[:classpath].join(":") : "."
+ compile_string = "javac #{options[:javac_options].join(' ')} -d #{options[:target]} -cp #{ENV_JAVA['jruby.home']}/lib/#{jruby_jar}:#{classpath_string} #{files_string}"
- def new_class(name, annotations = [])
- cls = RubyClass.new(name, imports, script_name, annotations, requires, package)
- @classes << cls
- cls
+ compile_string
end
+ end
- def to_s
- str = ""
- @classes.each do |cls|
- str << cls.to_s
+ module VisitorBuilder
+ def visit(name, &block)
+ define_method :"visit_#{name}_node" do |node|
+ log "entering: #{node.node_type}"
+ with_node(node) do
+ instance_eval(&block)
+ end
end
- str
end
- end
- class RubyClass
- def initialize(name, imports = [], script_name = nil, annotations = [], requires = [], package = "")
- @name = name
- @imports = imports
- @script_name = script_name
- @methods = []
- @annotations = annotations
- @interfaces = []
- @requires = requires
- @package = package
- @has_constructor = false;
+ def visit_default(&block)
+ define_method :method_missing do |name, node|
+ super unless name.to_s =~ /^visit/
+
+ with_node(node) do
+ block.call
+ end
+ end
end
+ end
- attr_accessor :methods, :name, :script_name, :annotations, :interfaces, :requires, :package, :sourcefile
+ class ClassNodeWalker
+ AST = org.jruby.ast
- def constructor?
- @has_constructor
- end
+ include AST::visitor::NodeVisitor
- def new_method(name, java_signature = nil, annotations = [])
- is_constructor = name == "initialize"
- @has_constructor ||= is_constructor
+ import AST::NodeType
+ import org.jruby.parser.JavaSignatureParser
+ import java.io.ByteArrayInputStream
- if is_constructor
- method = RubyConstructor.new(self, java_signature, annotations)
- else
- method = RubyMethod.new(self, name, java_signature, annotations)
- end
+ extend VisitorBuilder
- methods << method
- method
- end
+ attr_accessor :class_stack, :method_stack, :signature, :script, :annotations, :node
- def add_interface(ifc)
- @interfaces << ifc
+ def initialize(script_name = nil)
+ @script = RubyScript.new(script_name)
+ @class_stack = []
+ @method_stack = []
+ @signature = nil
+ @annotations = []
+ @name = nil
+ @node = nil
end
- def interface_string
- if @interfaces.size > 0
- "implements " + @interfaces.join('.')
- else
- ""
+ def add_imports(nodes)
+ nodes.each do |n|
+ @script.add_import(name_or_value(n))
end
end
- def static_init
- return <<JAVA
- static {
-#{requires_string}
- RubyClass metaclass = __ruby__.getClass(\"#{name}\");
- metaclass.setRubyStaticAllocator(#{name}.class);
- if (metaclass == null) throw new NoClassDefFoundError(\"Could not load Ruby class: #{name}\");
- __metaclass__ = metaclass;
- }
-JAVA
+ def set_signature(name)
+ @signature = name
end
- def annotations_string
- annotations.map do |a|
- "@" + a
- end.join("\n")
+ def add_annotation(nodes)
+ nodes.each do
+ name = name_or_value(nodes[0])
+ @annotations << name
+ end
end
- def methods_string
- methods.map(&:to_s).join("\n")
+ def add_interface(*ifc_nodes)
+ ifc_nodes.
+ map {|ifc| defined?(ifc.name) ? ifc.name : ifc.value}.
+ each {|ifc| current_class.add_interface(ifc)}
end
- def requires_string
- if requires.size == 0
- source = File.read script_name
- source_chunks = source.unpack("a32000" * (source.size / 32000 + 1))
- source_chunks.each do |chunk|
- chunk.gsub!(/([\\"])/, '\\\\\1')
- chunk.gsub!("\n", "\\n\" +\n \"")
- end
- source_line = source_chunks.join("\")\n .append(\"");
+ def new_class(name)
+ cls = @script.new_class(name, @annotations)
+ @annotations = []
- " String source = new StringBuilder(\"#{source_line}\").toString();\n __ruby__.executeScript(source, \"#{script_name}\");"
- else
- requires.map do |r|
- " __ruby__.getLoadService().lockAndRequire(\"#{r}\");"
- end.join("\n")
- end
+ class_stack.push(cls)
end
- def package_string
- if package.empty?
- ""
- else
- "package #{package};"
- end
+ def current_class
+ class_stack[0]
end
- def constructor_string
- str = <<JAVA
- /**
- * Standard Ruby object constructor, for construction-from-Ruby purposes.
- * Generally not for user consumption.
- *
- * @param ruby The JRuby instance this object will belong to
- * @param metaclass The RubyClass representing the Ruby class of this object
- */
- private #{name}(Ruby ruby, RubyClass metaclass) {
- super(ruby, metaclass);
- }
-
- /**
- * A static method used by JRuby for allocating instances of this object
- * from Ruby. Generally not for user comsumption.
- *
- * @param ruby The JRuby instance this object will belong to
- * @param metaclass The RubyClass representing the Ruby class of this object
- */
- public static IRubyObject __allocate__(Ruby ruby, RubyClass metaClass) {
- return new #{name}(ruby, metaClass);
- }
-JAVA
+ def pop_class
+ class_stack.pop
+ @signature = nil
+ @annotations = []
+ end
- unless @has_constructor
- str << <<JAVA
-
- /**
- * Default constructor. Invokes this(Ruby, RubyClass) with the classloader-static
- * Ruby and RubyClass instances assocated with this class, and then invokes the
- * no-argument 'initialize' method in Ruby.
- *
- * @param ruby The JRuby instance this object will belong to
- * @param metaclass The RubyClass representing the Ruby class of this object
- */
- public #{name}() {
- this(__ruby__, __metaclass__);
- RuntimeHelpers.invoke(__ruby__.getCurrentContext(), this, "initialize");
- }
-JAVA
- end
+ def new_method(name)
+ method = current_class.new_method(name, @signature, @annotations)
+ @signature = nil
+ @annotations = []
- str
+ method_stack.push(method)
end
- def to_s
- class_string = <<JAVA
-#{package_string}
+ def new_static_method(name)
+ method = current_class.new_method(name, @signature, @annotations)
+ method.static = true
+ @signature = nil
+ @annotations = []
-#{imports_string}
+ method_stack.push(method)
+ end
-#{annotations_string}
-public class #{name} extends RubyObject #{interface_string} {
- private static final Ruby __ruby__ = Ruby.getGlobalRuntime();
- private static final RubyClass __metaclass__;
+ def current_method
+ method_stack[0]
+ end
-#{static_init}
-#{constructor_string}
-#{methods_string}
-}
-JAVA
+ def pop_method
+ method_stack.pop
+ end
- class_string
+ def build_signature(signature)
+ if signature.kind_of? String
+ bytes = signature.to_java_bytes
+ return JavaSignatureParser.parse(ByteArrayInputStream.new(bytes))
+ else
+ raise "java_signature must take a literal string"
+ end
end
- def imports_string
- @imports.map do |import|
- "import #{import};"
- end.join("\n")
+ def build_args_signature(params)
+ sig = ["Object"]
+ param_strings = params.child_nodes.map do |param|
+ if param.respond_to? :type_node
+ type_node = param.type_node
+ next name_or_value(type_node)
+ end
+ raise 'unknown signature element: ' + param.to_s
+ end
+ sig.concat(param_strings)
+
+ sig
end
- end
- class RubyMethod
- def initialize(ruby_class, name, java_signature = nil, annotations = [])
- @ruby_class = ruby_class
- @name = name
- @java_signature = java_signature
- @static = false
- @args = []
- @annotations = annotations
+ def add_requires(*requires)
+ requires.each {|r| @script.add_require(name_or_value(r))}
end
- attr_accessor :args, :name, :java_signature, :static, :annotations
+ def set_package(package)
+ @script.package = name_or_value(package)
+ end
- def constructor?
- false
+ def name_or_value(node)
+ return node.name if defined? node.name
+ return node.value if defined? node.value
+ raise "unknown node :" + node.to_s
end
- def to_s
- declarator_string do
- <<-JAVA
-#{conversion_string(var_names)}
- IRubyObject ruby_result = RuntimeHelpers.invoke(__ruby__.getCurrentContext(), #{static ? '__metaclass__' : 'this'}, \"#{name}\"#{passed_args});
- #{return_string}
- JAVA
+ def with_node(node)
+ begin
+ old, @node = @node, node
+ yield
+ ensure
+ @node = old
end
end
- def declarator_string(&body)
- <<JAVA
- #{annotations_string}
- #{modifier_string} #{return_type} #{java_name}(#{declared_args}) {
-#{body.call}
- }
-JAVA
+ def error(message)
+ long_message = "#{node.position}: #{message}"
+ raise long_message
end
- def annotations_string
- annotations.map { |a| "@" + a }.join("\n")
+ def log(str)
+ puts "[jrubyc] #{str}" if $VERBOSE
end
- def conversion_string(var_names)
- var_names.map { |a| " IRubyObject ruby_#{a} = JavaUtil.convertJavaToRuby(__ruby__, #{a});"}.join("\n")
- end
+ visit :args do
+ # Duby-style arg specification, only pre supported for now
+ if node.pre && node.pre.child_nodes.find {|pre_arg| pre_arg.respond_to? :type_node}
+ current_method.java_signature = build_args_signature(node.pre)
+ end
+ node.pre && node.pre.child_nodes.each do |pre_arg|
+ current_method.args << pre_arg.name
+ end
+ node.opt_args && node.opt_args.child_nodes.each do |pre_arg|
+ current_method.args << pre_arg.name
+ end
+ node.post && node.post.child_nodes.each do |post_arg|
+ current_method.args << post_arg.name
+ end
+ if node.rest_arg >= 0
+ current_method.args << node.rest_arg_node.name
+ end
+ if node.block
+ current_method.args << node.block.name
+ end
- # FIXME: We should allow all valid modifiers
- def modifier_string
- modifiers = {}
- java_signature.modifiers.each {|m| modifiers[m.to_s] = m.to_s}
- is_static = static || modifiers["static"]
- static_str = is_static ? ' static' : ''
- abstract_str = modifiers["abstract"] ? ' abstract' : ''
- final_str = modifiers["final"] ? ' final' : ''
- native_str = modifiers["native"] ? ' native' : ''
- synchronized_str = modifiers["synchronized"] ? ' synchronized' : ''
- # only make sense for fields
- #is_transient = modifiers["transient"]
- #is_volatile = modifiers["volatile"]
- strictfp_str = modifiers["strictfp"] ? ' strictfp' : ''
- visibilities = modifiers.keys.to_a.grep(/public|private|protected/)
- if visibilities.size > 0
- visibility_str = "#{visibilities[0]}"
- else
- visibility_str = 'public'
+ # if method still has no signature, generate one
+ unless current_method.java_signature
+ args_string = current_method.args.map{|a| "Object #{a}"}.join(",")
+ sig_string = "Object #{current_method.name}(#{args_string})"
+ current_method.java_signature = build_signature(sig_string)
end
-
- "#{visibility_str}#{static_str}#{final_str}#{abstract_str}#{strictfp_str}#{native_str}#{synchronized_str}"
end
- def typed_args
- return @typed_args if @typed_args
-
- i = 0;
- @typed_args = java_signature.parameters.map do |a|
- type = a.type.name
- if a.variable_name
- var_name = a.variable_name
- else
- var_name = args[i]
- i+=1
- end
+ visit :class do
+ new_class(node.cpath.name)
+ node.body_node.accept(self)
+ pop_class
+ end
- {:name => var_name, :type => type}
- end
+ visit :defn do
+ new_method(node.name)
+ node.args_node.accept(self)
+ pop_method
end
- def declared_args
- @declared_args ||= typed_args.map { |a| "#{a[:type]} #{a[:name]}" }.join(', ')
+ visit :defs do
+ new_static_method(node.name)
+ node.args_node.accept(self)
+ pop_method
end
- def var_names
- @var_names ||= typed_args.map {|a| a[:name]}
+ visit :fcall do
+ case node.name
+ when 'java_import'
+ add_imports node.args_node.child_nodes
+ when 'java_signature'
+ set_signature build_signature(node.args_node.child_nodes[0].value)
+ when 'java_annotation'
+ add_annotation(node.args_node.child_nodes)
+ when 'java_implements'
+ add_interface(*node.args_node.child_nodes)
+ when "java_require"
+ add_requires(*node.args_node.child_nodes)
+ when "java_package"
+ set_package(*node.args_node.child_nodes)
+ end
end
- def passed_args
- return @passed_args if @passed_args
+ visit :block do
+ node.child_nodes.each {|n| n.accept self}
+ end
- @passed_args = var_names.map {|a| "ruby_#{a}"}.join(', ')
- @passed_args = ', ' + @passed_args if args.size > 0
+ visit :newline do
+ node.next_node.accept(self)
end
- def return_type
- if java_signature
- java_signature.return_type
- else
- raise "no java_signature has been set for method #{name}"
- end
+ visit :nil do
end
- def return_string
- if java_signature
- if return_type.void?
- "return;"
- else
- "return (#{return_type.wrapper_name})ruby_result.toJava(#{return_type.name}.class);"
- end
- else
- raise "no java_signature has been set for method #{name}"
- end
+ visit :root do
+ node.body_node.accept(self)
end
- def java_name
- if java_signature
- java_signature.name
- else
- raise "no java_signature has been set for method #{name}"
- end
+ visit_default do |node|
+ # ignore other nodes
end
end
+
+ class RubyScript
+ BASE_IMPORTS = [
+ "org.jruby.Ruby",
+ "org.jruby.RubyObject",
+ "org.jruby.javasupport.util.RuntimeHelpers",
+ "org.jruby.runtime.builtin.IRubyObject",
+ "org.jruby.javasupport.JavaUtil",
+ "org.jruby.RubyClass"
+ ]
- class RubyConstructor < RubyMethod
- def initialize(ruby_class, java_signature = nil, annotations = [])
- super(ruby_class, 'initialize', java_signature, annotations)
+ def initialize(script_name, imports = BASE_IMPORTS)
+ @classes = []
+ @script_name = script_name
+ @imports = imports
+ @requires = []
+ @package = ""
end
- def constructor?
- true
+ attr_accessor :classes, :imports, :script_name, :requires, :package
+
+ def add_import(name)
+ @imports << name
end
- def java_name
- @ruby_class.name
+ def add_require(require)
+ @requires << require
end
- def return_type
- ''
+ def new_class(name, annotations = [])
+ cls = RubyClass.new(name, imports, script_name, annotations, requires, package)
+ @classes << cls
+ cls
end
def to_s
- declarator_string do
- <<-JAVA
- this(__ruby__, __metaclass__);
-#{conversion_string(var_names)}
- RuntimeHelpers.invoke(__ruby__.getCurrentContext(), this, \"initialize\"#{passed_args});
- JAVA
+ str = ""
+ @classes.each do |cls|
+ str << cls.to_s
end
+ str
end
end
- module VisitorBuilder
- def visit(name, &block)
- define_method :"visit_#{name}_node" do |node|
- log "entering: #{node.node_type}"
- with_node(node) do
- instance_eval(&block)
- end
- end
+ class RubyClass
+ def initialize(name, imports = [], script_name = nil, annotations = [], requires = [], package = "")
+ @name = name
+ @imports = imports
+ @script_name = script_name
+ @methods = []
+ @annotations = annotations
+ @interfaces = []
+ @requires = requires
+ @package = package
+ @has_constructor = false;
end
- def visit_default(&block)
- define_method :method_missing do |name, node|
- super unless name.to_s =~ /^visit/
+ attr_accessor :methods, :name, :script_name, :annotations, :interfaces, :requires, :package, :sourcefile
- with_node(node) do
- block.call
- end
- end
+ def constructor?
+ @has_constructor
end
- end
-
- class ClassNodeWalker
- AST = org.jruby.ast
-
- include AST::visitor::NodeVisitor
- import AST::NodeType
- import org.jruby.parser.JavaSignatureParser
- import java.io.ByteArrayInputStream
+ def new_method(name, java_signature = nil, annotations = [])
+ is_constructor = name == "initialize"
+ @has_constructor ||= is_constructor
- extend VisitorBuilder
+ if is_constructor
+ method = RubyConstructor.new(self, java_signature, annotations)
+ else
+ method = RubyMethod.new(self, name, java_signature, annotations)
+ end
- attr_accessor :class_stack, :method_stack, :signature, :script, :annotations, :node
+ methods << method
+ method
+ end
- def initialize(script_name = nil)
- @script = RubyScript.new(script_name)
- @class_stack = []
- @method_stack = []
- @signature = nil
- @annotations = []
- @name = nil
- @node = nil
+ def add_interface(ifc)
+ @interfaces << ifc
end
- def add_imports(nodes)
- nodes.each do |n|
- @script.add_import(name_or_value(n))
+ def interface_string
+ if @interfaces.size > 0
+ "implements " + @interfaces.join('.')
+ else
+ ""
end
end
- def set_signature(name)
- @signature = name
+ def static_init
+ return <<JAVA
+ static {
+#{requires_string}
+ RubyClass metaclass = __ruby__.getClass(\"#{name}\");
+ metaclass.setRubyStaticAllocator(#{name}.class);
+ if (metaclass == null) throw new NoClassDefFoundError(\"Could not load Ruby class: #{name}\");
+ __metaclass__ = metaclass;
+ }
+JAVA
end
- def add_annotation(nodes)
- nodes.each do
- name = name_or_value(nodes[0])
- @annotations << name
- end
+ def annotations_string
+ annotations.map do |a|
+ "@" + a
+ end.join("\n")
end
- def add_interface(*ifc_nodes)
- ifc_nodes.
- map {|ifc| defined?(ifc.name) ? ifc.name : ifc.value}.
- each {|ifc| current_class.add_interface(ifc)}
+ def methods_string
+ methods.map(&:to_s).join("\n")
end
- def new_class(name)
- cls = @script.new_class(name, @annotations)
- @annotations = []
+ def requires_string
+ if requires.size == 0
+ source = File.read script_name
+ source_chunks = source.unpack("a32000" * (source.size / 32000 + 1))
+ source_chunks.each do |chunk|
+ chunk.gsub!(/([\\"])/, '\\\\\1')
+ chunk.gsub!("\n", "\\n\" +\n \"")
+ end
+ source_line = source_chunks.join("\")\n .append(\"");
- class_stack.push(cls)
+ " String source = new StringBuilder(\"#{source_line}\").toString();\n __ruby__.executeScript(source, \"#{script_name}\");"
+ else
+ requires.map do |r|
+ " __ruby__.getLoadService().lockAndRequire(\"#{r}\");"
+ end.join("\n")
+ end
end
- def current_class
- class_stack[0]
+ def package_string
+ if package.empty?
+ ""
+ else
+ "package #{package};"
+ end
end
- def pop_class
- class_stack.pop
- @signature = nil
- @annotations = []
+ def constructor_string
+ str = <<JAVA
+ /**
+ * Standard Ruby object constructor, for construction-from-Ruby purposes.
+ * Generally not for user consumption.
+ *
+ * @param ruby The JRuby instance this object will belong to
+ * @param metaclass The RubyClass representing the Ruby class of this object
+ */
+ private #{name}(Ruby ruby, RubyClass metaclass) {
+ super(ruby, metaclass);
+ }
+
+ /**
+ * A static method used by JRuby for allocating instances of this object
+ * from Ruby. Generally not for user comsumption.
+ *
+ * @param ruby The JRuby instance this object will belong to
+ * @param metaclass The RubyClass representing the Ruby class of this object
+ */
+ public static IRubyObject __allocate__(Ruby ruby, RubyClass metaClass) {
+ return new #{name}(ruby, metaClass);
+ }
+JAVA
+
+ unless @has_constructor
+ str << <<JAVA
+
+ /**
+ * Default constructor. Invokes this(Ruby, RubyClass) with the classloader-static
+ * Ruby and RubyClass instances assocated with this class, and then invokes the
+ * no-argument 'initialize' method in Ruby.
+ *
+ * @param ruby The JRuby instance this object will belong to
+ * @param metaclass The RubyClass representing the Ruby class of this object
+ */
+ public #{name}() {
+ this(__ruby__, __metaclass__);
+ RuntimeHelpers.invoke(__ruby__.getCurrentContext(), this, "initialize");
+ }
+JAVA
+ end
+
+ str
end
- def new_method(name)
- method = current_class.new_method(name, @signature, @annotations)
- @signature = nil
- @annotations = []
+ def to_s
+ class_string = <<JAVA
+#{package_string}
+
+#{imports_string}
- method_stack.push(method)
- end
+#{annotations_string}
+public class #{name} extends RubyObject #{interface_string} {
+ private static final Ruby __ruby__ = Ruby.getGlobalRuntime();
+ private static final RubyClass __metaclass__;
- def new_static_method(name)
- method = current_class.new_method(name, @signature, @annotations)
- method.static = true
- @signature = nil
- @annotations = []
+#{static_init}
+#{constructor_string}
+#{methods_string}
+}
+JAVA
- method_stack.push(method)
+ class_string
end
- def current_method
- method_stack[0]
+ def imports_string
+ @imports.map do |import|
+ "import #{import};"
+ end.join("\n")
end
+ end
- def pop_method
- method_stack.pop
+ class RubyMethod
+ def initialize(ruby_class, name, java_signature = nil, annotations = [])
+ @ruby_class = ruby_class
+ @name = name
+ @java_signature = java_signature
+ @static = false
+ @args = []
+ @annotations = annotations
end
- def build_signature(signature)
- if signature.kind_of? String
- bytes = signature.to_java_bytes
- return JavaSignatureParser.parse(ByteArrayInputStream.new(bytes))
- else
- raise "java_signature must take a literal string"
- end
+ attr_accessor :args, :name, :java_signature, :static, :annotations
+
+ def constructor?
+ false
end
- def build_args_signature(params)
- sig = ["Object"]
- param_strings = params.child_nodes.map do |param|
- if param.respond_to? :type_node
- type_node = param.type_node
- next name_or_value(type_node)
- end
- raise 'unknown signature element: ' + param.to_s
+ def to_s
+ declarator_string do
+ <<-JAVA
+#{conversion_string(var_names)}
+ IRubyObject ruby_result = RuntimeHelpers.invoke(__ruby__.getCurrentContext(), #{static ? '__metaclass__' : 'this'}, \"#{name}\"#{passed_args});
+ #{return_string}
+ JAVA
end
- sig.concat(param_strings)
-
- sig
end
- def add_requires(*requires)
- requires.each {|r| @script.add_require(name_or_value(r))}
+ def declarator_string(&body)
+ <<JAVA
+ #{annotations_string}
+ #{modifier_string} #{return_type} #{java_name}(#{declared_args}) {
+#{body.call}
+ }
+JAVA
end
- def set_package(package)
- @script.package = name_or_value(package)
+ def annotations_string
+ annotations.map { |a| "@" + a }.join("\n")
end
- def name_or_value(node)
- return node.name if defined? node.name
- return node.value if defined? node.value
- raise "unknown node :" + node.to_s
+ def conversion_string(var_names)
+ var_names.map { |a| " IRubyObject ruby_#{a} = JavaUtil.convertJavaToRuby(__ruby__, #{a});"}.join("\n")
end
- def with_node(node)
- begin
- old, @node = @node, node
- yield
- ensure
- @node = old
+ # FIXME: We should allow all valid modifiers
+ def modifier_string
+ modifiers = {}
+ java_signature.modifiers.each {|m| modifiers[m.to_s] = m.to_s}
+ is_static = static || modifiers["static"]
+ static_str = is_static ? ' static' : ''
+ abstract_str = modifiers["abstract"] ? ' abstract' : ''
+ final_str = modifiers["final"] ? ' final' : ''
+ native_str = modifiers["native"] ? ' native' : ''
+ synchronized_str = modifiers["synchronized"] ? ' synchronized' : ''
+ # only make sense for fields
+ #is_transient = modifiers["transient"]
+ #is_volatile = modifiers["volatile"]
+ strictfp_str = modifiers["strictfp"] ? ' strictfp' : ''
+ visibilities = modifiers.keys.to_a.grep(/public|private|protected/)
+ if visibilities.size > 0
+ visibility_str = "#{visibilities[0]}"
+ else
+ visibility_str = 'public'
end
+
+ "#{visibility_str}#{static_str}#{final_str}#{abstract_str}#{strictfp_str}#{native_str}#{synchronized_str}"
end
- def error(message)
- long_message = "#{node.position}: #{message}"
- raise long_message
- end
-
- def log(str)
- puts "[jrubyc] #{str}" if $VERBOSE
- end
+ def typed_args
+ return @typed_args if @typed_args
- visit :args do
- # Duby-style arg specification, only pre supported for now
- if node.pre && node.pre.child_nodes.find {|pre_arg| pre_arg.respond_to? :type_node}
- current_method.java_signature = build_args_signature(node.pre)
- end
- node.pre && node.pre.child_nodes.each do |pre_arg|
- current_method.args << pre_arg.name
- end
- node.opt_args && node.opt_args.child_nodes.each do |pre_arg|
- current_method.args << pre_arg.name
- end
- node.post && node.post.child_nodes.each do |post_arg|
- current_method.args << post_arg.name
- end
- if node.rest_arg >= 0
- current_method.args << node.rest_arg_node.name
- end
- if node.block
- current_method.args << node.block.name
- end
+ i = 0;
+ @typed_args = java_signature.parameters.map do |a|
+ type = a.type.name
+ if a.variable_name
+ var_name = a.variable_name
+ else
+ var_name = args[i]
+ i+=1
+ end
- # if method still has no signature, generate one
- unless current_method.java_signature
- args_string = current_method.args.map{|a| "Object #{a}"}.join(",")
- sig_string = "Object #{current_method.name}(#{args_string})"
- current_method.java_signature = build_signature(sig_string)
+ {:name => var_name, :type => type}
end
end
- visit :class do
- new_class(node.cpath.name)
- node.body_node.accept(self)
- pop_class
+ def declared_args
+ @declared_args ||= typed_args.map { |a| "#{a[:type]} #{a[:name]}" }.join(', ')
end
- visit :defn do
- new_method(node.name)
- node.args_node.accept(self)
- pop_method
+ def var_names
+ @var_names ||= typed_args.map {|a| a[:name]}
end
- visit :defs do
- new_static_method(node.name)
- node.args_node.accept(self)
- pop_method
+ def passed_args
+ return @passed_args if @passed_args
+
+ @passed_args = var_names.map {|a| "ruby_#{a}"}.join(', ')
+ @passed_args = ', ' + @passed_args if args.size > 0
end
- visit :fcall do
- case node.name
- when 'java_import'
- add_imports node.args_node.child_nodes
- when 'java_signature'
- set_signature build_signature(node.args_node.child_nodes[0].value)
- when 'java_annotation'
- add_annotation(node.args_node.child_nodes)
- when 'java_implements'
- add_interface(*node.args_node.child_nodes)
- when "java_require"
- add_requires(*node.args_node.child_nodes)
- when "java_package"
- set_package(*node.args_node.child_nodes)
+ def return_type
+ if java_signature
+ java_signature.return_type
+ else
+ raise "no java_signature has been set for method #{name}"
end
end
- visit :block do
- node.child_nodes.each {|n| n.accept self}
+ def return_string
+ if java_signature
+ if return_type.void?
+ "return;"
+ else
+ "return (#{return_type.wrapper_name})ruby_result.toJava(#{return_type.name}.class);"
+ end
+ else
+ raise "no java_signature has been set for method #{name}"
+ end
end
- visit :newline do
- node.next_node.accept(self)
+ def java_name
+ if java_signature
+ java_signature.name
+ else
+ raise "no java_signature has been set for method #{name}"
+ end
end
+ end
- visit :nil do
+ class RubyConstructor < RubyMethod
+ def initialize(ruby_class, java_signature = nil, annotations = [])
+ super(ruby_class, 'initialize', java_signature, annotations)
end
- visit :root do
- node.body_node.accept(self)
+ def constructor?
+ true
end
- visit_default do |node|
- # ignore other nodes
+ def java_name
+ @ruby_class.name
end
- end
-
- module JavaGenerator
- module_function
-
- def generate_java(node, script_name = nil)
- walker = ClassNodeWalker.new(script_name)
-
- node.accept(walker)
- walker.script
+ def return_type
+ ''
end
- def generate_javac(files, options, classpath, target)
- files_string = files.join(' ')
- jruby_jar, = ['jruby.jar', 'jruby-complete.jar'].select do |jar|
- File.exist? "#{ENV_JAVA['jruby.home']}/lib/#{jar}"
+ def to_s
+ declarator_string do
+ <<-JAVA
+ this(__ruby__, __metaclass__);
+#{conversion_string(var_names)}
+ RuntimeHelpers.invoke(__ruby__.getCurrentContext(), this, \"initialize\"#{passed_args});
+ JAVA
end
- classpath_string = classpath.size > 0 ? classpath.join(":") : "."
- compile_string = "javac #{options.join(' ')} -d #{target} -cp #{ENV_JAVA['jruby.home']}/lib/#{jruby_jar}:#{classpath_string} #{files_string}"
-
- compile_string
end
end
end
View
6 spec/java_integration/jrubyc/java/basic_spec.rb
@@ -32,9 +32,9 @@ def generate(script)
files = %w[Foo.java Bar.java]
javac = JRuby::Compiler::JavaGenerator.generate_javac(
files,
- [],
- ENV_JAVA['java.class.path'].split(RbConfig::CONFIG['PATH_SEPARATOR']),
- '/tmp')
+ :javac_options => [],
+ :classpath => ENV_JAVA['java.class.path'].split(RbConfig::CONFIG['PATH_SEPARATOR']),
+ :target => '/tmp')
javac.should match /javac/
javac.should match /jruby\w*\.jar/
View
7 src/org/jruby/compiler/JITCompiler.java
@@ -188,12 +188,11 @@ public static String getHashForBytes(byte[] bytes) {
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
sha1.update(bytes);
byte[] digest = sha1.digest();
- char[] digestChars = new char[digest.length * 2];
+ StringBuilder builder = new StringBuilder();
for (int i = 0; i < digest.length; i++) {
- digestChars[i * 2] = Character.forDigit(digest[i] & 0xF, 16);
- digestChars[i * 2 + 1] = Character.forDigit((digest[i] & 0xF0) >> 4, 16);
+ builder.append(Integer.toString( ( digest[i] & 0xff ) + 0x100, 16).substring( 1 ));
}
- return new String(digestChars).toUpperCase(Locale.ENGLISH);
+ return builder.toString().toUpperCase(Locale.ENGLISH);
} catch (NoSuchAlgorithmException nsae) {
throw new RuntimeException(nsae);
}
View
3  src/org/jruby/compiler/impl/BaseBodyCompiler.java
@@ -67,6 +67,7 @@
import org.jruby.runtime.builtin.InstanceVariables;
import org.jruby.util.ByteList;
import org.jruby.util.JavaNameMangler;
+import org.jruby.util.SafePropertyAccessor;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
import static org.objectweb.asm.Opcodes.*;
@@ -2571,7 +2572,7 @@ public void defineNewMethod(String name, int methodArity, StaticScope scope,
CompilerCallback receiver, ASTInspector inspector, boolean root, String filename, int line) {
// TODO: build arg list based on number of args, optionals, etc
String newMethodName;
- if (root && Boolean.getBoolean("jruby.compile.toplevel")) {
+ if (root && SafePropertyAccessor.getBoolean("jruby.compile.toplevel", false)) {
newMethodName = name;
} else {
String mangledName = JavaNameMangler.mangleStringForCleanJavaIdentifier(name);
Please sign in to comment.
Something went wrong with that request. Please try again.