From bc8907687fe7bd5b1fe8cde530264e03c503508c Mon Sep 17 00:00:00 2001 From: Charles Oliver Nutter Date: Fri, 15 Jan 2010 00:36:09 -0600 Subject: [PATCH] Add prototype "real class" generation to jrubyc --java. --- bin/jrubyc | 2 +- lib/ruby/site_ruby/shared/jruby/compiler.rb | 420 ++++++++++++++++++ lib/ruby/site_ruby/shared/jruby/jrubyc.rb | 100 +---- .../jruby/java/addons/KernelJavaAddons.java | 6 + 4 files changed, 428 insertions(+), 100 deletions(-) create mode 100644 lib/ruby/site_ruby/shared/jruby/compiler.rb diff --git a/bin/jrubyc b/bin/jrubyc index 64ac3494ea9..68025f20d01 100755 --- a/bin/jrubyc +++ b/bin/jrubyc @@ -2,7 +2,7 @@ require 'jruby/jrubyc' -status = JRubyCompiler::compile_argv(ARGV) +status = JRuby::Compiler::compile_argv(ARGV) if (status != 0) puts "Compilation FAILED: #{status} error(s) encountered" diff --git a/lib/ruby/site_ruby/shared/jruby/compiler.rb b/lib/ruby/site_ruby/shared/jruby/compiler.rb new file mode 100644 index 00000000000..42e9c76646c --- /dev/null +++ b/lib/ruby/site_ruby/shared/jruby/compiler.rb @@ -0,0 +1,420 @@ +require 'optparse' +require 'jruby' + +module JRuby::Compiler + BAIS = java.io.ByteArrayInputStream + Mangler = org.jruby.util.JavaNameMangler + BytecodeCompiler = org.jruby.compiler.impl.StandardASMCompiler + ASTCompiler = org.jruby.compiler.ASTCompiler + JavaFile = java.io.File + + def compile_argv(argv) + basedir = Dir.pwd + prefix = "" + target = Dir.pwd + java = false + + opt_parser = 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 + end + + opts.on("-p", "--prefix PREFIX", "Prepend PREFIX to the file path and package. Default is no prefix.") do |pre| + prefix = pre + end + + opts.on("-t", "--target TARGET", "Output files to TARGET directory") do |tgt| + target = tgt + end + + opts.on("-j", "--java", "Generate normal Java classes to accompany the script") do + java = true + end + + opts.parse!(argv) + end + + if (argv.length == 0) + raise "No files or directories specified" + end + + compile_files(argv, basedir, prefix, target, java) + end + module_function :compile_argv + + def compile_files(filenames, basedir = Dir.pwd, prefix = "ruby", target = Dir.pwd, java = false) + runtime = JRuby.runtime + + unless File.exist? target + raise "Target dir not found: #{target}" + end + + files = [] + + # The compilation code + compile_proc = proc do |filename| + begin + file = File.open(filename) + + classpath = Mangler.mangle_filename_for_classpath(filename, basedir, prefix) + puts "Compiling #{filename} to class #{classpath}" + + inspector = org.jruby.compiler.ASTInspector.new + + source = file.read + node = runtime.parse_file(BAIS.new(source.to_java_bytes), filename, nil) + + inspector.inspect(node) + + asmCompiler = BytecodeCompiler.new(classpath, filename) + compiler = ASTCompiler.new + compiler.compile_root(node, asmCompiler, inspector) + + asmCompiler.write_class(JavaFile.new(target)) + + if java + ruby_script = process_script(node, filename) + ruby_script.classes.each do |cls| + puts "Generating Java class #{cls.name} to #{cls.name}.java" + java_src = cls.name + ".java"; + files << java_src + + File.open(java_src, 'w') do |f| + f.write(cls.to_s) + end + end + end + + 0 + rescue Exception + puts "Failure during compilation of file #{filename}:\n#{$!}" + puts $!.backtrace + 1 + ensure + file.close unless file.nil? + end + end + + errors = 0 + # Process all the file arguments + Dir[*filenames].each do |filename| + unless File.exists? filename + puts "Error -- file not found: #{filename}" + errors += 1 + next + end + + if (File.directory?(filename)) + puts "Compiling all in '#{File.expand_path(filename)}'..." + Dir.glob(filename + "/**/*.rb").each { |filename| + errors += compile_proc[filename] + } + else + errors += compile_proc[filename] + end + end + + if java + files_string = files.join(' ') + compile_string = "javac -cp #{ENV_JAVA['jruby.home']}/lib/jruby.jar:. #{files_string}" + puts compile_string + system compile_string + end + + errors + end + module_function :compile_files + + 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" + ] + + def initialize(script_name, imports = BASE_IMPORTS) + @classes = [] + @script_name = script_name + @imports = imports + end + + attr_accessor :classes, :imports, :script_name + + def add_import(name) + @imports << name + end + + def new_class(name) + cls = RubyClass.new(name, imports, script_name) + @classes << cls + cls + end + + def to_s + @classes.each do |cls| + str << cls.to_s + end + str + end + end + + class RubyClass + def initialize(name, imports = [], script_name = nil) + @name = name + @imports = imports + @script_name = script_name + @methods = [] + end + + attr_accessor :methods, :name, :script_name + + def new_method(name, java_signature = nil, java_name = nil) + method = RubyMethod.new(name, java_signature, java_name) + methods << method + method + end + + def to_s + class_string = imports_string + + class_string << "public class #{name} extends RubyObject {\n" + class_string << " private static final Ruby __ruby__ = Ruby.getGlobalRuntime();\n" + class_string << " private static final RubyClass __metaclass__;\n" + + static_init = " static {\n" + if script_name + static_init << " __ruby__.getLoadService().lockAndRequire(\"#{script_name}\");\n" + end + static_init << " RubyClass metaclass = __ruby__.getClass(\"#{name}\");\n" + static_init << " if (metaclass == null) throw new NoClassDefFoundError(\"Could not load Ruby class: #{name}\");\n" + static_init << " __metaclass__ = metaclass;\n" + static_init << " }\n" + + class_string << static_init + + class_string << <= 0 + current_method.args << node.rest_arg_node.name + end + if node.block + current_method.args << node.block.name + end + when NodeType::BLOCKNODE + node.child_nodes.each {|n| n.accept self} + when NodeType::CLASSNODE + new_class(node.cpath.name) + node.body_node.accept(self) + pop_class + when NodeType::DEFNNODE + new_method(node.name) + node.args_node.accept(self) + pop_method + when NodeType::DEFSNODE + new_static_method(node.name) + node.args_node.accept(self) + pop_method + when NodeType::FCALLNODE + case node.name + when 'java_import' + add_import node.args_node.child_nodes[0].value + when 'java_signature' + set_signature build_signature(node.args_node.child_nodes[0]) + when 'java_name' + set_name node.args_node.child_nodes[0].value + end + when NodeType::NEWLINENODE + node.next_node.accept(self) + when NodeType::ROOTNODE + node.body_node.accept(self) + else + puts 'unknown: ' + args[0].node_type.to_s + end + else + super + end + end + end + + def process_script(node, script_name = nil) + walker = ClassNodeWalker.new(script_name) + + node.accept(walker) + + walker.script + end + module_function :process_script +end \ No newline at end of file diff --git a/lib/ruby/site_ruby/shared/jruby/jrubyc.rb b/lib/ruby/site_ruby/shared/jruby/jrubyc.rb index 623224e8e19..c84b9ef50dc 100644 --- a/lib/ruby/site_ruby/shared/jruby/jrubyc.rb +++ b/lib/ruby/site_ruby/shared/jruby/jrubyc.rb @@ -1,99 +1 @@ -require 'optparse' -require 'jruby' - -module JRubyCompiler - BAIS = java.io.ByteArrayInputStream - Mangler = org.jruby.util.JavaNameMangler - BytecodeCompiler = org.jruby.compiler.impl.StandardASMCompiler - ASTCompiler = org.jruby.compiler.ASTCompiler - JavaFile = java.io.File - - module_function - def compile_argv(argv) - basedir = Dir.pwd - prefix = "" - target = Dir.pwd - - opt_parser = 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 - end - - opts.on("-p", "--prefix PREFIX", "Prepend PREFIX to the file path and package. Default is no prefix.") do |pre| - prefix = pre - end - - opts.on("-t", "--target TARGET", "Output files to TARGET directory") do |tgt| - target = tgt - end - - opts.parse!(argv) - end - - if (argv.length == 0) - raise "No files or directories specified" - end - - compile_files(argv, basedir, prefix, target) - end - - def compile_files(filenames, basedir = Dir.pwd, prefix = "ruby", target = Dir.pwd) - runtime = JRuby.runtime - - unless File.exist? target - raise "Target dir not found: #{target}" - end - - # The compilation code - compile_proc = proc do |filename| - begin - file = File.open(filename) - - classpath = Mangler.mangle_filename_for_classpath(filename, basedir, prefix) - puts "Compiling #{filename} to class #{classpath}" - - inspector = org.jruby.compiler.ASTInspector.new - - source = file.read - node = runtime.parse_file(BAIS.new(source.to_java_bytes), filename, nil) - - inspector.inspect(node) - - asmCompiler = BytecodeCompiler.new(classpath, filename) - compiler = ASTCompiler.new - compiler.compile_root(node, asmCompiler, inspector) - - asmCompiler.write_class(JavaFile.new(target)) - 0 - rescue Exception - puts "Failure during compilation of file #{filename}:\n#{$!}" - 1 - ensure - file.close unless file.nil? - end - end - - errors = 0 - # Process all the file arguments - Dir[*filenames].each do |filename| - unless File.exists? filename - puts "Error -- file not found: #{filename}" - errors += 1 - next - end - - if (File.directory?(filename)) - puts "Compiling all in '#{File.expand_path(filename)}'..." - Dir.glob(filename + "/**/*.rb").each { |filename| - errors += compile_proc[filename] - } - else - errors += compile_proc[filename] - end - end - errors - end -end +require 'jruby/compiler' diff --git a/src/org/jruby/java/addons/KernelJavaAddons.java b/src/org/jruby/java/addons/KernelJavaAddons.java index 755fd7204e6..52b5b3eb16f 100644 --- a/src/org/jruby/java/addons/KernelJavaAddons.java +++ b/src/org/jruby/java/addons/KernelJavaAddons.java @@ -58,6 +58,12 @@ public static IRubyObject to_java(ThreadContext context, IRubyObject fromObject, } } + @JRubyMethod(rest = true) + public static IRubyObject java_signature(IRubyObject recv, IRubyObject[] args) { + // empty stub for now + return recv.getRuntime().getNil(); + } + private static JavaClass getTargetType(ThreadContext context, Ruby runtime, IRubyObject type) { JavaClass targetType;