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