I like it. A couple of things: - I think in many/most cases we could determine the args/types, and type of callback method (simple/full) from the method signature.
- Should have a field for visibility. - Would be nice to include aliases as well, e.g., @JRubyMethod(name = "a", aliases = new String[] {"b", "c"} ); Or maybe this would be a separate annotation, @JRubyAlias or similar... I especially like your idea of building in parameter coercion rules. I've been thinking about a similar "behind-the-scenes" coercion scheme for dealing with lightweight objects, should we choose to go there. -Bill On 8/4/07, Charles Oliver Nutter <[EMAIL PROTECTED]> wrote: > > 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 >