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
>

Reply via email to