[jruby~main:db1154ce] Add a demonstration of how to bind method_missing in a simple JRuby exten

  • From: nicksieger@kenai.com
  • To: commits@jruby.kenai.com
  • Subject: [jruby~main:db1154ce] Add a demonstration of how to bind method_missing in a simple JRuby exten
  • Date: Fri, 5 Mar 2010 15:19:36 +0000

Project:    jruby
Repository: main
Revision:   db1154ce778712d78945eb505cfd952d876add09
Author:     nicksieger
Date:       2010-03-05 15:18:51 UTC
Link:       

Log Message:
------------
Some cleanup for jrubyc --java logic.
Don't use attr_accessor without a frame on the stack, since it tries to 
access current visibility.
Add a demonstration of how to bind method_missing in a simple JRuby extension.


Revisions:
----------
2808aa841ed3f918013c11447b39952407a97e79
63d644ab681cefaac863626841cb08b6e03de5cd
db1154ce778712d78945eb505cfd952d876add09


Modified Paths:
---------------
lib/ruby/site_ruby/shared/jruby/compiler.rb
src/org/jruby/RubyJRuby.java


Added Paths:
------------
src/org/jruby/demo/ext/MethodMissing.java


Diffs:
------
diff --git a/lib/ruby/site_ruby/shared/jruby/compiler.rb 
b/lib/ruby/site_ruby/shared/jruby/compiler.rb
index d66988f..022d7cd 100644
--- a/lib/ruby/site_ruby/shared/jruby/compiler.rb
+++ b/lib/ruby/site_ruby/shared/jruby/compiler.rb
@@ -206,36 +206,51 @@ module JRuby::Compiler
       end
     end
 
-    def to_s
-      imps_string = imports_string
-      ifc_string = interface_string
+    def static_init
+      return <<JAVA
+  static {
+    __ruby__.getLoadService().lockAndRequire(\"#{script_name}\");
+    RubyClass metaclass = __ruby__.getClass(\"#{name}\");
+    metaclass.setClassAllocator(#{name}.class);
+    if (metaclass == null) throw new NoClassDefFoundError(\"Could not load 
Ruby class: #{name}\");
+    __metaclass__ = metaclass;
+  }
+JAVA
+    end
 
-      anno_string = annotations.map {|a| "@#{a.shift}(" + (a[0] || []).map 
{|k,v| "#{k} = #{format_anno_value(v)}"}.join(',') + ")"}.join("\n")
-      class_string = "#{imps_string}\n\n#{anno_string}\npublic class #{name} 
extends RubyObject #{ifc_string} {\n"
-      class_string << "  private static final Ruby __ruby__ = 
Ruby.getGlobalRuntime();\n"
-      class_string << "  private static final RubyClass __metaclass__;\n"
+    def annotations_string
+      annotations.map do |a|
+        params = (a[0] || []).map do |k,v|
+          "#{k} = #{format_anno_value(v)}"
+        end.join(',')
 
-      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 << "    metaclass.setClassAllocator(#{name}.class);\n"
-      static_init << "    if (metaclass == null) throw new 
NoClassDefFoundError(\"Could not load Ruby class: #{name}\");\n"
-      static_init << "        __metaclass__ = metaclass;\n"
-      static_init << "  }\n"
+        "@#{a.shift}(#{params})"
+      end.join("\n")
+    end
+
+    def methods_string
+      methods.map(&:to_s).join("\n")
+    end
+
+    def to_s
+      class_string = <<JAVA
+#{imports_string}
 
-      class_string << static_init
+#{annotations_string}
+public class #{name} extends RubyObject #{interface_string} {
+  private static final Ruby __ruby__ = Ruby.getGlobalRuntime();
+  private static final RubyClass __metaclass__;
+
+#{static_init}
 
-      class_string << <<EOJ
   public #{name}() {
     super(__ruby__, __metaclass__);
   }
-EOJ
-      
-      class_string << methods.map(&:to_s).join("\n\n")
-      class_string << "\n}"
 
+#{methods_string}
+}
+JAVA
+      
       class_string
     end
 
@@ -269,17 +284,15 @@ EOJ
     end
 
     def to_s
-      signature = java_signature
-
-      if signature.parameter_list.size != args.size
+      if java_signature.parameter_list.size != args.size
         raise "signature and method argument counts do not match"
       end
 
-      ret = signature.return_type
+      ret = java_signature.return_type
 
       var_names = []
       i = 0;
-      args_string = signature.parameter_list.map do |a|
+      args_string = java_signature.parameter_list.map do |a|
         type = a.type.name
         if a.variable_name
           var_name = a.variable_name
@@ -293,16 +306,16 @@ EOJ
       end.join(', ')
 
       passed_args = var_names.map {|a| "ruby_#{a}"}.join(', ')
-      passed_args = ', ' + passed_args if signature.parameter_list.size > 0
+      passed_args = ', ' + passed_args if java_signature.parameter_list.size 
> 0
 
       conv_string = var_names.map {|a| '    IRubyObject ruby_' + a + ' = 
JavaUtil.convertJavaToRuby(__ruby__, ' + a + ');'}.join("\n")
 
       anno_string = annotations.map {|a| "  @#{a.shift}(" + (a[0] || []).map 
{|k,v| "#{k} = #{format_anno_value(v)}"}.join(',') + ")"}.join("\n")
 
-      java_name = signature.name
+      java_name = java_signature.name
 
       if ret.void?
-        ret_string = ""
+        ret_string = "return;"
       else
         ret_string = "return 
(#{ret.wrapper_name})ruby_result.toJava(#{ret.name}.class);"
       end
@@ -319,9 +332,39 @@ EOJ
     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
+    end
+
+    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
+
   class ClassNodeWalker
-    include org.jruby.ast.visitor.NodeVisitor
-    import org.jruby.ast.NodeType
+    AST = org.jruby.ast
+    
+    include AST::visitor::NodeVisitor
+    
+    import AST::NodeType
+    import org.jruby.parser.JavaSignatureParser
+    import java.io.ByteArrayInputStream
+    
+    extend VisitorBuilder
+
+    attr_accessor :class_stack, :method_stack, :signature, :script, 
:annotations, :node
 
     def initialize(script_name = nil)
       @script = RubyScript.new(script_name)
@@ -330,10 +373,9 @@ EOJ
       @signature = nil
       @annotations = []
       @name = nil
+      @node = nil
     end
 
-    attr_accessor :class_stack, :method_stack, :signature, :script, 
:annotations
-
     def add_import(name)
       @script.add_import(name)
     end
@@ -420,8 +462,9 @@ EOJ
     end
 
     def build_signature(signature_args)
-      if org.jruby.ast.StrNode === signature_args
-        sig_node = 
org.jruby.parser.JavaSignatureParser.parse(java.io.ByteArrayInputStream.new(signature_args.value.to_java_bytes))
+      if AST::StrNode === signature_args
+        bytes = signature_args.value.to_java_bytes
+        sig_node = JavaSignatureParser.parse(ByteArrayInputStream.new(bytes))
 
         sig_node
       else
@@ -444,80 +487,108 @@ EOJ
     end
 
     def name_or_value(node)
-      return node.name if node.respond_to? :name
-      return node.value if node.respond_to? :value
+      return node.name if defined? node.name
+      return node.value if defined? node.value
       raise "unknown node :" + node.to_s
     end
 
-    def method_missing(name, *args)
-      if name.to_s =~ /^visit/
-        node = args[0]
-        puts "* entering: #{node.node_type}" if $VERBOSE
-        case node.node_type
-        when NodeType::ARGSNODE
-          # 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
-        when NodeType::BLOCKNODE
-          node.child_nodes.each {|n| n.accept self}
-        when NodeType::CALLNODE
-          case node.name
-          when '+@'
-            add_annotation(node.receiver_node,
-              *(node.args_node ? node.args_node.child_nodes : []))
-          end
-        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_annotation'
-            add_annotation(*node.args_node.child_nodes)
-          when 'java_implements'
-            add_interface(*node.args_node.child_nodes)
-          end
-        when NodeType::NEWLINENODE
-          node.next_node.accept(self)
-        when NodeType::NILNODE
-          # nothing
-        when NodeType::ROOTNODE
-          node.body_node.accept(self)
-        else
-          puts 'unknown: ' + args[0].node_type.to_s
-        end
-      else
-        super
+    def with_node(node)
+      begin
+        old, @node = @node, node
+        yield
+      ensure
+        @node = old
+      end
+    end
+
+    def error(message)
+      long_message =  "#{node.position}: #{message}"
+      raise long_message
+    end
+
+    def log(str)
+      puts "[jrubyc] #{str}" if $VERBOSE
+    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
+    end
+
+    visit :call do
+      case node.name
+      when '+@'
+        add_annotation(node.receiver_node,
+          *(node.args_node ? node.args_node.child_nodes : []))
+      end
+    end
+
+    visit :class do
+      new_class(node.cpath.name)
+      node.body_node.accept(self)
+      pop_class
+    end
+
+    visit :defn do
+      new_method(node.name)
+      node.args_node.accept(self)
+      pop_method
+    end
+
+    visit :defs do
+      new_static_method(node.name)
+      node.args_node.accept(self)
+      pop_method
+    end
+
+    visit :fcall do
+      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_annotation'
+        add_annotation(*node.args_node.child_nodes)
+      when 'java_implements'
+        add_interface(*node.args_node.child_nodes)
       end
     end
+
+    visit :block do
+      node.child_nodes.each {|n| n.accept self}
+    end
+
+    visit :newline do
+      node.next_node.accept(self)
+    end
+
+    visit :nil do
+    end
+
+    visit :root do
+      node.body_node.accept(self)
+    end
+
+    visit_default do
+      error "unknown node encountered: #{node.node_type.to_s}"
+    end
   end
 
   def process_script(node, script_name = nil)
diff --git a/src/org/jruby/RubyJRuby.java b/src/org/jruby/RubyJRuby.java
index 54e710d..c18feda 100644
--- a/src/org/jruby/RubyJRuby.java
+++ b/src/org/jruby/RubyJRuby.java
@@ -88,7 +88,9 @@ public class RubyJRuby {
 
         RubyClass compiledScriptClass = 
jrubyModule.defineClassUnder("CompiledScript",runtime.getObject(), 
runtime.getObject().getAllocator());
 
-        compiledScriptClass.attr_accessor(context, new 
IRubyObject[]{runtime.newSymbol("name"), runtime.newSymbol("class_name"), 
runtime.newSymbol("original_script"), runtime.newSymbol("code")});
+        for (String name : new String[] {"name", "class_name", 
"original_script", "code"}) {
+            compiledScriptClass.addReadAttribute(context, name);
+        }
         
compiledScriptClass.defineAnnotatedMethods(JRubyCompiledScript.class);
 
         RubyClass threadLocalClass = 
jrubyModule.defineClassUnder("ThreadLocal", runtime.getObject(), 
JRubyThreadLocal.ALLOCATOR);
diff --git a/src/org/jruby/demo/ext/MethodMissing.java 
b/src/org/jruby/demo/ext/MethodMissing.java
new file mode 100644
index 0000000..065ef3a
--- /dev/null
+++ b/src/org/jruby/demo/ext/MethodMissing.java
@@ -0,0 +1,34 @@
+package org.jruby.demo.ext;
+
+import org.jruby.Ruby;
+import org.jruby.RubyClass;
+import org.jruby.RubyObject;
+import org.jruby.anno.JRubyMethod;
+import org.jruby.runtime.ObjectAllocator;
+import org.jruby.runtime.builtin.IRubyObject;
+
+/**
+ * This is a demonstration of how to bind method_missing in a JRuby extension
+ */
+public class MethodMissing extends RubyObject {
+    public static void init(Ruby ruby) {
+        RubyClass mm = ruby.defineClass("MethodMissing", ruby.getObject(), 
new ObjectAllocator() {
+            public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
+                return new MethodMissing(runtime, klazz);
+            }
+        });
+        mm.defineAnnotatedMethods(MethodMissing.class);
+    }
+
+    public MethodMissing(Ruby runtime, RubyClass klazz) {
+        super(runtime, klazz);
+    }
+
+    @JRubyMethod(rest = true)
+    public IRubyObject method_missing(IRubyObject[] args) {
+        for (IRubyObject arg : args) {
+            System.out.println(arg);
+        }
+        return getRuntime().getNil();
+    }
+}




[jruby~main:db1154ce] Add a demonstration of how to bind method_missing in a simple JRuby exten

nicksieger 03/05/2010
  • Mysql
  • Glassfish
  • Jruby
  • Rails
  • Nblogo
Terms of Use; Privacy Policy;
© 2010, Oracle Corporation and/or its affiliates
(revision 20120518.3c65429)
 
 
Close
loading
Please Confirm
Close