Attached is a simple patch that adds enough basic method annotation support to JRuby to eliminate manually binding zero, one, two, and three-parameter non-static "fast" methods.

Example here for Fixnum#plus:

    @JRubyMethod(name = "+", required = 1)
    public IRubyObject plus(IRubyObject other) {

Once so annotated, a call to RubyModule.defineAnnotatedMethods(Class, CallbackFactory) is all that's required to define all methods and bind them to the appropriate names.

Currently, the JRubyMethod annotation only has two fields, "name" and "required" for required arguments. I would envision it containing something more like the following:

- name
- require args
- optional args
- rest arg (boolean)
- frame required (default true)
- scope required (default false? no Java methods require scope right now)
- caller frame required (binding, block_given?, eval, etc)
- caller scope required (binding, local_variables, eval, etc)

And then in the future, possible also:

- annotated parameter list with coercion rules (allowing multiple method impls to be bound to specific parameter types with appropriate coercion)

There's also another interesting aspect to using annotations with the compiler: no need to emit method-binding code into the main code stream. All that would be required would be to emit the actual compiled code with annotations and then when it's bound to a specific class use the automated mechanism. Much simpler, and easier for compilation.

Anyway, comments are welcome. I'm quite interested in getting something like this moving.

- Charlie
Index: nbproject/project.xml
===================================================================
--- nbproject/project.xml       (revision 4079)
+++ nbproject/project.xml       (working copy)
@@ -64,13 +64,13 @@
             <compilation-unit>
                 <package-root>src</package-root>
                 <classpath 
mode="compile">lib/bsf.jar:lib/junit.jar:lib/asm-2.2.3.jar:lib/asm-commons-2.2.3.jar:lib/jline-0.9.91.jar:lib/backport-util-concurrent.jar:lib/ant.jar:lib/asm-util-2.2.3.jar</classpath>
-                <source-level>1.4</source-level>
+                <source-level>1.5</source-level>
             </compilation-unit>
             <compilation-unit>
                 <package-root>test</package-root>
                 <unit-tests/>
                 <classpath 
mode="compile">lib/bsf.jar:lib/junit.jar:src</classpath>
-                <source-level>1.4</source-level>
+                <source-level>1.5</source-level>
             </compilation-unit>
         </java-data>
     </configuration>
Index: src/org/jruby/RubyModule.java
===================================================================
--- src/org/jruby/RubyModule.java       (revision 4079)
+++ src/org/jruby/RubyModule.java       (working copy)
@@ -35,6 +35,7 @@
  ***** END LICENSE BLOCK *****/
 package org.jruby;
 
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -43,6 +44,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import org.jruby.anno.JRubyMethod;
 import org.jruby.internal.runtime.methods.AliasMethod;
 import org.jruby.internal.runtime.methods.DynamicMethod;
 import org.jruby.internal.runtime.methods.FullFunctionCallbackMethod;
@@ -581,6 +583,32 @@
                 Visibility.PRIVATE : Visibility.PUBLIC;
         addMethod(name, new FullFunctionCallbackMethod(this, method, 
visibility));
     }
+    
+    public void defineAnnotatedMethods(Class clazz, CallbackFactory 
callbackFactory) {
+        Method[] methods = clazz.getDeclaredMethods();
+        for (Method method: methods) {
+            JRubyMethod jrubyMethod = method.getAnnotation(JRubyMethod.class);
+            
+            if (jrubyMethod == null) continue;
+            
+            switch (jrubyMethod.required()) {
+            case 0:
+                defineFastMethod(jrubyMethod.name(), 
callbackFactory.getFastMethod(method.getName()));
+                break;
+            case 1:
+                defineFastMethod(jrubyMethod.name(), 
callbackFactory.getFastMethod(method.getName(), IRubyObject.class));
+                break;
+            case 2:
+                defineFastMethod(jrubyMethod.name(), 
callbackFactory.getFastMethod(method.getName(), IRubyObject.class, 
IRubyObject.class));
+                break;
+            case 3:
+                defineFastMethod(jrubyMethod.name(), 
callbackFactory.getFastMethod(method.getName(), IRubyObject.class, 
IRubyObject.class, IRubyObject.class));
+                break;
+            default:
+                throw new RuntimeException("Invalid number of required args 
for annotated method");
+            }
+        }
+    }
 
     public void defineFastMethod(String name, Callback method) {
         Visibility visibility = name.equals("initialize") ?
Index: src/org/jruby/anno/JRubyMethod.java
===================================================================
--- src/org/jruby/anno/JRubyMethod.java (revision 0)
+++ src/org/jruby/anno/JRubyMethod.java (revision 0)
@@ -0,0 +1,26 @@
+/*
+ * JRubyMethod.java
+ * 
+ * Created on Aug 4, 2007, 3:07:36 PM
+ * 
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package org.jruby.anno;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author headius
+ */
[EMAIL PROTECTED](RetentionPolicy.RUNTIME)
[EMAIL PROTECTED](ElementType.METHOD)
+public @interface JRubyMethod {
+    String name();
+    int required() default 0;
+}
Index: src/org/jruby/RubyFixnum.java
===================================================================
--- src/org/jruby/RubyFixnum.java       (revision 4079)
+++ src/org/jruby/RubyFixnum.java       (working copy)
@@ -36,6 +36,7 @@
 package org.jruby;
 
 import java.math.BigInteger;
+import org.jruby.anno.JRubyMethod;
 import org.jruby.runtime.Arity;
 import org.jruby.runtime.CallbackFactory;
 import org.jruby.runtime.ClassIndex;
@@ -66,10 +67,6 @@
         fixnum.defineFastMethod("to_sym", 
callbackFactory.getFastMethod("to_sym"));
 
         fixnum.defineFastMethod("-@", callbackFactory.getFastMethod("uminus"));
-        fixnum.defineFastMethod("+", callbackFactory.getFastMethod("plus", 
RubyKernel.IRUBY_OBJECT));
-        fixnum.defineFastMethod("-", callbackFactory.getFastMethod("minus", 
RubyKernel.IRUBY_OBJECT));
-        fixnum.defineFastMethod("*", callbackFactory.getFastMethod("mul", 
RubyKernel.IRUBY_OBJECT));
-        fixnum.defineFastMethod("/", 
callbackFactory.getFastMethod("div_slash", RubyKernel.IRUBY_OBJECT));
         fixnum.defineFastMethod("div", 
callbackFactory.getFastMethod("div_div", RubyKernel.IRUBY_OBJECT));
         fixnum.defineFastMethod("%", callbackFactory.getFastMethod("mod", 
RubyKernel.IRUBY_OBJECT));
         fixnum.defineFastMethod("modulo", callbackFactory.getFastMethod("mod", 
RubyKernel.IRUBY_OBJECT));
@@ -99,6 +96,8 @@
         fixnum.defineFastMethod("size", callbackFactory.getFastMethod("size"));
         fixnum.defineFastMethod("zero?", 
callbackFactory.getFastMethod("zero_p"));
         
+        fixnum.defineAnnotatedMethods(RubyFixnum.class, callbackFactory);
+        
         fixnum.dispatcher = callbackFactory.createDispatcher(fixnum);
 
         return fixnum;
@@ -257,6 +256,7 @@
     /** fix_plus
      * 
      */
+    @JRubyMethod(name = "+", required = 1)
     public IRubyObject plus(IRubyObject other) {
         if (other instanceof RubyFixnum) {
             long otherValue = ((RubyFixnum) other).value;
@@ -278,6 +278,7 @@
     /** fix_minus
      * 
      */
+    @JRubyMethod(name = "-", required = 1)
     public IRubyObject minus(IRubyObject other) {
         if (other instanceof RubyFixnum) {
             long otherValue = ((RubyFixnum) other).value;
@@ -297,6 +298,7 @@
     /** fix_mul
      * 
      */
+    @JRubyMethod(name = "*", required = 1)
     public IRubyObject mul(IRubyObject other) {
         if (other instanceof RubyFixnum) {
             long otherValue = ((RubyFixnum) other).value;
@@ -331,6 +333,7 @@
         return idiv(other, "div");
        }
        
+    @JRubyMethod(name = "/", required = 1)
     public IRubyObject div_slash(IRubyObject other) {
         return idiv(other, "/");
     }
Index: default.build.properties
===================================================================
--- default.build.properties    (revision 4079)
+++ default.build.properties    (working copy)
@@ -15,4 +15,4 @@
 test.results.dir=${build.dir}/test-results
 html.test.results.dir=${test.results.dir}/html
 html.test.coverage.results.dir=${test.results.dir}/html-coverage
-javac.version=1.4
+javac.version=1.5

---------------------------------------------------------------------
To unsubscribe from this list please visit:

    http://xircles.codehaus.org/manage_email

Reply via email to