Skip to content

Commit

Permalink
Merge branch 'jruby-1_7'
Browse files Browse the repository at this point in the history
* jruby-1_7:
  fix compilation error left-over from 7774be5
  some more var-args tests - make sure var-arg (overload) methods are covered
  move ALLOCATOR into a static field
  guess the ivar list size (most objects have only @ variables)
  private can be static
  handle Ruby super -> Java constructor with var-args correctly (fixes cases in #2375)
  mark generated proxy class constructor with @java.lang.SafeVarargs if super is var-args
  time to start sharing the generic (handles var-args) argument toJava conversion
  use (and cache) the more Java [array] version of the API to retrieve proxy constructors
  avoid deprecated JavaMethod.create + make sub-class helpers final
  do the toJava conversion on site instead of constant indirection esp. in loops

Conflicts:
	core/src/main/java/org/jruby/RubyModule.java
	core/src/main/java/org/jruby/java/invokers/ConstructorInvoker.java
	core/src/main/java/org/jruby/java/invokers/InstanceMethodInvoker.java
	core/src/main/java/org/jruby/java/invokers/SingletonMethodInvoker.java
	core/src/main/java/org/jruby/java/invokers/StaticMethodInvoker.java
	test/test_higher_javasupport.rb
kares committed Apr 7, 2015
2 parents be01825 + aaf1681 commit 487938c
Showing 19 changed files with 806 additions and 540 deletions.
18 changes: 10 additions & 8 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -1497,11 +1497,12 @@ public IRubyObject removeInstanceVariable(String name) {
@Override
public List<Variable<IRubyObject>> getInstanceVariableList() {
Map<String, VariableAccessor> ivarAccessors = metaClass.getVariableAccessorsForRead();
ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>();
ArrayList<Variable<IRubyObject>> list = new ArrayList<Variable<IRubyObject>>(ivarAccessors.size());
for (Map.Entry<String, VariableAccessor> entry : ivarAccessors.entrySet()) {
Object value = entry.getValue().get(this);
if (value == null || !(value instanceof IRubyObject) || !IdUtil.isInstanceVariable(entry.getKey())) continue;
list.add(new VariableEntry<IRubyObject>(entry.getKey(), (IRubyObject)value));
final String key = entry.getKey();
final Object value = entry.getValue().get(this);
if (value == null || !(value instanceof IRubyObject) || !IdUtil.isInstanceVariable(key)) continue;
list.add(new VariableEntry<IRubyObject>(key, (IRubyObject) value));
}
return list;
}
@@ -1513,11 +1514,12 @@ public List<Variable<IRubyObject>> getInstanceVariableList() {
@Override
public List<String> getInstanceVariableNameList() {
Map<String, VariableAccessor> ivarAccessors = metaClass.getVariableAccessorsForRead();
ArrayList<String> list = new ArrayList<String>();
ArrayList<String> list = new ArrayList<String>(ivarAccessors.size());
for (Map.Entry<String, VariableAccessor> entry : ivarAccessors.entrySet()) {
Object value = entry.getValue().get(this);
if (value == null || !(value instanceof IRubyObject) || !IdUtil.isInstanceVariable(entry.getKey())) continue;
list.add(entry.getKey());
final String key = entry.getKey();
final Object value = entry.getValue().get(this);
if (value == null || !(value instanceof IRubyObject) || !IdUtil.isInstanceVariable(key)) continue;
list.add(key);
}
return list;
}
374 changes: 187 additions & 187 deletions core/src/main/java/org/jruby/RubyModule.java

Large diffs are not rendered by default.

83 changes: 36 additions & 47 deletions core/src/main/java/org/jruby/java/invokers/ConstructorInvoker.java
Original file line number Diff line number Diff line change
@@ -54,26 +54,11 @@ protected boolean isMemberVarArgs(Member member) {
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
JavaProxy proxy = castJavaProxy(self);
JavaConstructor constructor = (JavaConstructor) findCallable(self, name, args, args.length);

int len = args.length;
final Object[] convertedArgs;
JavaConstructor constructor = (JavaConstructor) findCallable(self, name, args, len);
if (constructor.isVarArgs()) {
len = constructor.getArity() - 1;
convertedArgs = new Object[len + 1];
for (int i = 0; i < len && i < args.length; i++) {
convertedArgs[i] = convertArg(args[i], constructor, i);
}
convertedArgs[len] = convertVarArgs(args, constructor);
} else {
convertedArgs = new Object[len];
for (int i = 0; i < len && i < args.length; i++) {
convertedArgs[i] = convertArg(args[i], constructor, i);
}
}
final Object[] convertedArgs = convertArguments(constructor, args);
proxy.setObject( constructor.newInstanceDirect(context, convertedArgs) );

proxy.setObject(constructor.newInstanceDirect(context, convertedArgs));

return self;
}

@@ -84,7 +69,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
JavaConstructor constructor = (JavaConstructor) findCallableArityZero(self, name);

proxy.setObject(constructor.newInstanceDirect(context));

return self;
}

@@ -93,10 +78,11 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0});
JavaProxy proxy = castJavaProxy(self);
JavaConstructor constructor = (JavaConstructor) findCallableArityOne(self, name, arg0);
Object cArg0 = convertArg(arg0, constructor, 0);
final Class<?>[] paramTypes = constructor.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);

proxy.setObject(constructor.newInstanceDirect(context, cArg0));

return self;
}

@@ -105,8 +91,9 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1});
JavaProxy proxy = castJavaProxy(self);
JavaConstructor constructor = (JavaConstructor) findCallableArityTwo(self, name, arg0, arg1);
Object cArg0 = convertArg(arg0, constructor, 0);
Object cArg1 = convertArg(arg1, constructor, 1);
final Class<?>[] paramTypes = constructor.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);

proxy.setObject(constructor.newInstanceDirect(context, cArg0, cArg1));

@@ -118,9 +105,10 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1, arg2});
JavaProxy proxy = castJavaProxy(self);
JavaConstructor constructor = (JavaConstructor) findCallableArityThree(self, name, arg0, arg1, arg2);
Object cArg0 = convertArg(arg0, constructor, 0);
Object cArg1 = convertArg(arg1, constructor, 1);
Object cArg2 = convertArg(arg2, constructor, 2);
final Class<?>[] paramTypes = constructor.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = arg2.toJava(paramTypes[2]);

proxy.setObject(constructor.newInstanceDirect(context, cArg0, cArg1, cArg2));

@@ -138,17 +126,18 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
IRubyObject[] intermediate = new IRubyObject[len + 1];
System.arraycopy(args, 0, intermediate, 0, len);
intermediate[len] = RubyProc.newProc(context.runtime, block, block.type);

JavaConstructor constructor = (JavaConstructor) findCallable(self, name, intermediate, len + 1);
final Class<?>[] paramTypes = constructor.getParameterTypes();
for (int i = 0; i < len + 1; i++) {
convertedArgs[i] = convertArg(intermediate[i], constructor, i);
convertedArgs[i] = intermediate[i].toJava(paramTypes[i]);
}

proxy.setObject(constructor.newInstanceDirect(context, convertedArgs));

return self;
} else {
return call(context, self, clazz, name, args);
}
return call(context, self, clazz, name, args);
}

@Override
@@ -158,14 +147,14 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz

RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaConstructor constructor = (JavaConstructor) findCallableArityOne(self, name, proc);
Object cArg0 = convertArg(proc, constructor, 0);
final Class<?>[] paramTypes = constructor.getParameterTypes();
Object cArg0 = proc.toJava(paramTypes[0]);

proxy.setObject(constructor.newInstanceDirect(context, cArg0));

return self;
} else {
return call(context, self, clazz, name);
}
return call(context, self, clazz, name);
}

@Override
@@ -175,15 +164,15 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz

RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaConstructor constructor = (JavaConstructor) findCallableArityTwo(self, name, arg0, proc);
Object cArg0 = convertArg(arg0, constructor, 0);
Object cArg1 = convertArg(proc, constructor, 1);
final Class<?>[] paramTypes = constructor.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = proc.toJava(paramTypes[1]);

proxy.setObject(constructor.newInstanceDirect(context, cArg0, cArg1));

return self;
} else {
return call(context, self, clazz, name, arg0);
}
return call(context, self, clazz, name, arg0);
}

@Override
@@ -193,16 +182,16 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz

RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaConstructor constructor = (JavaConstructor) findCallableArityThree(self, name, arg0, arg1, proc);
Object cArg0 = convertArg(arg0, constructor, 0);
Object cArg1 = convertArg(arg1, constructor, 1);
Object cArg2 = convertArg(proc, constructor, 2);
final Class<?>[] paramTypes = constructor.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = proc.toJava(paramTypes[2]);

proxy.setObject(constructor.newInstanceDirect(context, cArg0, cArg1, cArg2));

return self;
} else {
return call(context, self, clazz, name, arg0, arg1);
}
return call(context, self, clazz, name, arg0, arg1);
}

@Override
@@ -212,16 +201,16 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz

RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaConstructor constructor = (JavaConstructor) findCallableArityFour(self, name, arg0, arg1, arg2, proc);
Object cArg0 = convertArg(arg0, constructor, 0);
Object cArg1 = convertArg(arg1, constructor, 1);
Object cArg2 = convertArg(arg2, constructor, 2);
Object cArg3 = convertArg(proc, constructor, 3);
final Class<?>[] paramTypes = constructor.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = arg2.toJava(paramTypes[2]);
Object cArg3 = proc.toJava(paramTypes[3]);

proxy.setObject(constructor.newInstanceDirect(context, cArg0, cArg1, cArg2, cArg3));

return self;
} else {
return call(context, self, clazz, name, arg0, arg1, arg2);
}
return call(context, self, clazz, name, arg0, arg1, arg2);
}
}
Original file line number Diff line number Diff line change
@@ -23,124 +23,113 @@ public InstanceMethodInvoker(RubyModule host, Method method) {
@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
JavaProxy proxy = castJavaProxy(self);

int len = args.length;
final Object[] convertedArgs;
JavaMethod method = (JavaMethod)findCallable(self, name, args, len);
if (method.isVarArgs()) {
len = method.getArity() - 1;
convertedArgs = new Object[len + 1];
for (int i = 0; i < len && i < args.length; i++) {
convertedArgs[i] = convertArg(args[i], method, i);
}
convertedArgs[len] = convertVarArgs(args, method);
} else {
convertedArgs = new Object[len];
for (int i = 0; i < len && i < args.length; i++) {
convertedArgs[i] = convertArg(args[i], method, i);
}
}
return method.invokeDirect(context, proxy.getObject(), convertedArgs);
JavaMethod method = (JavaMethod) findCallable(self, name, args, args.length);
return method.invokeDirect( context, proxy.getObject(), convertArguments(method, args) );
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY);
JavaProxy proxy = castJavaProxy(self);
JavaMethod method = (JavaMethod)findCallableArityZero(self, name);
JavaMethod method = (JavaMethod) findCallableArityZero(self, name);
return method.invokeDirect(context, proxy.getObject());
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0});
JavaProxy proxy = castJavaProxy(self);
JavaMethod method = (JavaMethod)findCallableArityOne(self, name, arg0);
Object cArg0 = convertArg(arg0, method, 0);
JavaMethod method = (JavaMethod) findCallableArityOne(self, name, arg0);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
return method.invokeDirect(context, proxy.getObject(), cArg0);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1});
JavaProxy proxy = castJavaProxy(self);
JavaMethod method = (JavaMethod)findCallableArityTwo(self, name, arg0, arg1);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
JavaMethod method = (JavaMethod) findCallableArityTwo(self, name, arg0, arg1);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
return method.invokeDirect(context, proxy.getObject(), cArg0, cArg1);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1, arg2});
JavaProxy proxy = castJavaProxy(self);
JavaMethod method = (JavaMethod)findCallableArityThree(self, name, arg0, arg1, arg2);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
Object cArg2 = convertArg(arg2, method, 2);
JavaMethod method = (JavaMethod) findCallableArityThree(self, name, arg0, arg1, arg2);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = arg2.toJava(paramTypes[2]);
return method.invokeDirect(context, proxy.getObject(), cArg0, cArg1, cArg2);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
if (block.isGiven()) {
if ( block.isGiven() ) {
JavaProxy proxy = castJavaProxy(self);
int len = args.length;
final int len = args.length;
// these extra arrays are really unfortunate; split some of these paths out to eliminate?
Object[] convertedArgs = new Object[len + 1];
IRubyObject[] intermediate = new IRubyObject[len + 1];
System.arraycopy(args, 0, intermediate, 0, len);
intermediate[len] = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallable(self, name, intermediate, len + 1);

JavaMethod method = (JavaMethod) findCallable(self, name, intermediate, len + 1);
final Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < len + 1; i++) {
convertedArgs[i] = convertArg(intermediate[i], method, i);
convertedArgs[i] = intermediate[i].toJava(paramTypes[i]);
}

return method.invokeDirect(context, proxy.getObject(), convertedArgs);
} else {
return call(context, self, clazz, name, args);
}
return call(context, self, clazz, name, args);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (block.isGiven()) {
JavaProxy proxy = castJavaProxy(self);
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityOne(self, name, proc);
Object cArg0 = convertArg(proc, method, 0);
JavaMethod method = (JavaMethod) findCallableArityOne(self, name, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = proc.toJava(paramTypes[0]);
return method.invokeDirect(context, proxy.getObject(), cArg0);
} else {
return call(context, self, clazz, name);
}
return call(context, self, clazz, name);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
if (block.isGiven()) {
JavaProxy proxy = castJavaProxy(self);
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityTwo(self, name, arg0, proc);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(proc, method, 1);
JavaMethod method = (JavaMethod) findCallableArityTwo(self, name, arg0, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = proc.toJava(paramTypes[1]);
return method.invokeDirect(context, proxy.getObject(), cArg0, cArg1);
} else {
return call(context, self, clazz, name, arg0);
}
return call(context, self, clazz, name, arg0);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
if (block.isGiven()) {
JavaProxy proxy = castJavaProxy(self);
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityThree(self, name, arg0, arg1, proc);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
Object cArg2 = convertArg(proc, method, 2);
JavaMethod method = (JavaMethod) findCallableArityThree(self, name, arg0, arg1, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = proc.toJava(paramTypes[2]);
return method.invokeDirect(context, proxy.getObject(), cArg0, cArg1, cArg2);
} else {
return call(context, self, clazz, name, arg0, arg1);
}
return call(context, self, clazz, name, arg0, arg1);
}

@Override
@@ -149,13 +138,13 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
JavaProxy proxy = castJavaProxy(self);
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityFour(self, name, arg0, arg1, arg2, proc);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
Object cArg2 = convertArg(arg2, method, 2);
Object cArg3 = convertArg(proc, method, 3);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = arg2.toJava(paramTypes[2]);
Object cArg3 = proc.toJava(paramTypes[3]);
return method.invokeDirect(context, proxy.getObject(), cArg0, cArg1, cArg2, cArg3);
} else {
return call(context, self, clazz, name, arg0, arg1, arg2);
}
return call(context, self, clazz, name, arg0, arg1, arg2);
}
}
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/java/invokers/MethodInvoker.java
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ public abstract class MethodInvoker extends RubyToJavaInvoker {

@Override
protected final JavaCallable createCallable(Ruby runtime, Member member) {
return JavaMethod.create(runtime, (Method) member);
return new JavaMethod(runtime, (Method) member);
}

@Override
@@ -41,12 +41,12 @@ protected final JavaCallable[][] createCallableArrayArray(int size) {
}

@Override
protected Class[] getMemberParameterTypes(Member member) {
protected final Class[] getMemberParameterTypes(Member member) {
return ((Method) member).getParameterTypes();
}

@Override
protected boolean isMemberVarArgs(Member member) {
protected final boolean isMemberVarArgs(Member member) {
return ((Method) member).isVarArgs();
}

47 changes: 32 additions & 15 deletions core/src/main/java/org/jruby/java/invokers/RubyToJavaInvoker.java
Original file line number Diff line number Diff line change
@@ -162,31 +162,48 @@ protected AccessibleObject[] getAccessibleObjects() {

protected abstract boolean isMemberVarArgs(Member member);

static Object convertArg(IRubyObject arg, JavaCallable method, int index) {
return arg.toJava(method.getParameterTypes()[index]);
//final int getMemberArity(Member member) {
// return getMemberParameterTypes(member).length;
//}

public static Object[] convertArguments(final ParameterTypes method, final IRubyObject[] args) {
final Class<?>[] paramTypes = method.getParameterTypes();
final Object[] javaArgs; final int len = args.length;

if ( method.isVarArgs() ) {
final int last = paramTypes.length - 1;
javaArgs = new Object[ last + 1 ];
for ( int i = 0; i < last; i++ ) {
javaArgs[i] = args[i].toJava(paramTypes[i]);
}
javaArgs[ last ] = convertVarArgumentsOnly(paramTypes[ last ], last, args);
}
else {
javaArgs = new Object[len];
for ( int i = 0; i < len; i++ ) {
javaArgs[i] = args[i].toJava(paramTypes[i]);
}
}
return javaArgs;
}

static Object convertVarArgs(IRubyObject[] args, JavaCallable method) {
final Class[] types = method.getParameterTypes();
final Class<?> varArrayType = types[types.length - 1];
final int varStart = types.length - 1;
private static Object convertVarArgumentsOnly(final Class<?> varArrayType,
final int varStart, final IRubyObject[] args) {
final int varCount = args.length - varStart;

if ( args.length == 0 ) {
if ( args.length == 0 || varCount <= 0 ) {
return Array.newInstance(varArrayType.getComponentType(), 0);
}

final Object varArgs;
if ( varCount == 1 && args[varStart] instanceof ArrayJavaProxy ) {
// we may have a pre-created array to pass; try that first
varArgs = args[varStart].toJava(varArrayType);
return args[varStart].toJava(varArrayType);
}
else {
final Class compType = varArrayType.getComponentType();
varArgs = Array.newInstance(compType, varCount);
for ( int i = 0; i < varCount; i++ ) {
Array.set(varArgs, i, args[varStart + i].toJava(compType));
}

final Class<?> compType = varArrayType.getComponentType();
final Object varArgs = Array.newInstance(compType, varCount);
for ( int i = 0; i < varCount; i++ ) {
Array.set(varArgs, i, args[varStart + i].toJava(compType));
}
return varArgs;
}
Original file line number Diff line number Diff line change
@@ -27,39 +27,25 @@ public SingletonMethodInvoker(Object singleton, RubyClass host, Method method) {

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
int len = args.length;
final Object[] convertedArgs;
JavaMethod method = (JavaMethod)findCallable(self, name, args, len);
if (method.isVarArgs()) {
len = method.getArity() - 1;
convertedArgs = new Object[len + 1];
for (int i = 0; i < len && i < args.length; i++) {
convertedArgs[i] = convertArg(args[i], method, i);
}
convertedArgs[len] = convertVarArgs(args, method);
} else {
convertedArgs = new Object[len];
for (int i = 0; i < len && i < args.length; i++) {
convertedArgs[i] = convertArg(args[i], method, i);
}
}
return method.invokeDirect(context, singleton, convertedArgs);
JavaMethod method = (JavaMethod) findCallable(self, name, args, args.length);
return method.invokeDirect( context, singleton, convertArguments(method, args) );
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY);
JavaMethod method = (JavaMethod)findCallableArityZero(self, name);
JavaMethod method = (JavaMethod) findCallableArityZero(self, name);

return method.invokeDirect(context, singleton);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0});
JavaMethod method = (JavaMethod)findCallableArityOne(self, name, arg0);
JavaMethod method = (JavaMethod) findCallableArityOne(self, name, arg0);
if (method.isVarArgs()) return call(context, self, clazz, name, new IRubyObject[] {arg0});
Object cArg0 = convertArg(arg0, method, 0);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);

return method.invokeDirect(context, singleton, cArg0);
}
@@ -68,19 +54,21 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1});
JavaMethod method = (JavaMethod)findCallableArityTwo(self, name, arg0, arg1);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);

return method.invokeDirect(context, singleton, cArg0, cArg1);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1, arg2});
JavaMethod method = (JavaMethod)findCallableArityThree(self, name, arg0, arg1, arg2);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
Object cArg2 = convertArg(arg2, method, 2);
JavaMethod method = (JavaMethod) findCallableArityThree(self, name, arg0, arg1, arg2);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = arg2.toJava(paramTypes[2]);

return method.invokeDirect(context, singleton, cArg0, cArg1, cArg2);
}
@@ -94,72 +82,73 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
IRubyObject[] intermediate = new IRubyObject[len + 1];
System.arraycopy(args, 0, intermediate, 0, len);
intermediate[len] = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallable(self, name, intermediate, len + 1);

JavaMethod method = (JavaMethod) findCallable(self, name, intermediate, len + 1);
final Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < len + 1; i++) {
convertedArgs[i] = convertArg(intermediate[i], method, i);
convertedArgs[i] = intermediate[i].toJava(paramTypes[i]);
}

return method.invokeDirect(context, singleton, convertedArgs);
} else {
return call(context, self, clazz, name, args);
}
return call(context, self, clazz, name, args);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (block.isGiven()) {
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityOne(self, name, proc);
Object cArg0 = convertArg(proc, method, 0);
JavaMethod method = (JavaMethod) findCallableArityOne(self, name, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = proc.toJava(paramTypes[0]);

return method.invokeDirect(context, singleton, cArg0);
} else {
return call(context, self, clazz, name);
}
return call(context, self, clazz, name);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
if (block.isGiven()) {
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityTwo(self, name, arg0, proc);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(proc, method, 1);
JavaMethod method = (JavaMethod) findCallableArityTwo(self, name, arg0, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = proc.toJava(paramTypes[1]);

return method.invokeDirect(context, singleton, cArg0, cArg1);
} else {
return call(context, self, clazz, name, arg0);
}
return call(context, self, clazz, name, arg0);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
if (block.isGiven()) {
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityThree(self, name, arg0, arg1, proc);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
Object cArg2 = convertArg(proc, method, 2);
JavaMethod method = (JavaMethod) findCallableArityThree(self, name, arg0, arg1, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = proc.toJava(paramTypes[2]);

return method.invokeDirect(context, singleton, cArg0, cArg1, cArg2);
} else {
return call(context, self, clazz, name, arg0, arg1);
}
return call(context, self, clazz, name, arg0, arg1);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
if (block.isGiven()) {
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityFour(self, name, arg0, arg1, arg2, proc);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
Object cArg2 = convertArg(arg2, method, 2);
Object cArg3 = convertArg(proc, method, 3);
JavaMethod method = (JavaMethod) findCallableArityFour(self, name, arg0, arg1, arg2, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = arg2.toJava(paramTypes[2]);
Object cArg3 = proc.toJava(paramTypes[3]);

return method.invokeDirect(context, singleton, cArg0, cArg1, cArg2, cArg3);
} else {
return call(context, self, clazz, name, arg0, arg1, arg2);
}
return call(context, self, clazz, name, arg0, arg1, arg2);
}
}
80 changes: 37 additions & 43 deletions core/src/main/java/org/jruby/java/invokers/StaticMethodInvoker.java
Original file line number Diff line number Diff line change
@@ -23,60 +23,48 @@ public StaticMethodInvoker(RubyClass host, Method method) {

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
int len = args.length;
final Object[] convertedArgs;
JavaMethod method = (JavaMethod)findCallable(self, name, args, len);
if (method.isVarArgs()) {
len = method.getArity() - 1;
convertedArgs = new Object[len + 1];
for (int i = 0; i < len && i < args.length; i++) {
convertedArgs[i] = convertArg(args[i], method, i);
}
convertedArgs[len] = convertVarArgs(args, method);
} else {
convertedArgs = new Object[len];
for (int i = 0; i < len && i < args.length; i++) {
convertedArgs[i] = convertArg(args[i], method, i);
}
}
return method.invokeStaticDirect(context, convertedArgs);
JavaMethod method = (JavaMethod) findCallable(self, name, args, args.length);
return method.invokeStaticDirect( context, convertArguments(method, args) );
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY);
JavaMethod method = (JavaMethod)findCallableArityZero(self, name);
JavaMethod method = (JavaMethod) findCallableArityZero(self, name);

return method.invokeStaticDirect(context);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0});
JavaMethod method = (JavaMethod)findCallableArityOne(self, name, arg0);
JavaMethod method = (JavaMethod) findCallableArityOne(self, name, arg0);
if (method.isVarArgs()) return call(context, self, clazz, name, new IRubyObject[] {arg0});
Object cArg0 = convertArg(arg0, method, 0);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);

return method.invokeStaticDirect(context, cArg0);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1});
JavaMethod method = (JavaMethod)findCallableArityTwo(self, name, arg0, arg1);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
JavaMethod method = (JavaMethod) findCallableArityTwo(self, name, arg0, arg1);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);

return method.invokeStaticDirect(context, cArg0, cArg1);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
if (javaVarargsCallables != null) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1, arg2});
JavaMethod method = (JavaMethod)findCallableArityThree(self, name, arg0, arg1, arg2);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
Object cArg2 = convertArg(arg2, method, 2);
JavaMethod method = (JavaMethod) findCallableArityThree(self, name, arg0, arg1, arg2);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = arg2.toJava(paramTypes[2]);

return method.invokeStaticDirect(context, cArg0, cArg1, cArg2);
}
@@ -90,9 +78,11 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
IRubyObject[] intermediate = new IRubyObject[len + 1];
System.arraycopy(args, 0, intermediate, 0, len);
intermediate[len] = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallable(self, name, intermediate, len + 1);

JavaMethod method = (JavaMethod) findCallable(self, name, intermediate, len + 1);
final Class<?>[] paramTypes = method.getParameterTypes();
for (int i = 0; i < len + 1; i++) {
convertedArgs[i] = convertArg(intermediate[i], method, i);
convertedArgs[i] = intermediate[i].toJava(paramTypes[i]);
}

return method.invokeStaticDirect(context, convertedArgs);
@@ -104,8 +94,9 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (block.isGiven()) {
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityOne(self, name, proc);
Object cArg0 = convertArg(proc, method, 0);
JavaMethod method = (JavaMethod) findCallableArityOne(self, name, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = proc.toJava(paramTypes[0]);

return method.invokeStaticDirect(context, cArg0);
}
@@ -116,9 +107,10 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
if (block.isGiven()) {
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityTwo(self, name, arg0, proc);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(proc, method, 1);
JavaMethod method = (JavaMethod) findCallableArityTwo(self, name, arg0, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = proc.toJava(paramTypes[1]);

return method.invokeStaticDirect(context, cArg0, cArg1);
}
@@ -129,10 +121,11 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
if (block.isGiven()) {
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityThree(self, name, arg0, arg1, proc);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
Object cArg2 = convertArg(proc, method, 2);
JavaMethod method = (JavaMethod) findCallableArityThree(self, name, arg0, arg1, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = proc.toJava(paramTypes[2]);

return method.invokeStaticDirect(context, cArg0, cArg1, cArg2);

@@ -144,11 +137,12 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
if (block.isGiven()) {
RubyProc proc = RubyProc.newProc(context.runtime, block, block.type);
JavaMethod method = (JavaMethod)findCallableArityFour(self, name, arg0, arg1, arg2, proc);
Object cArg0 = convertArg(arg0, method, 0);
Object cArg1 = convertArg(arg1, method, 1);
Object cArg2 = convertArg(arg2, method, 2);
Object cArg3 = convertArg(proc, method, 3);
JavaMethod method = (JavaMethod) findCallableArityFour(self, name, arg0, arg1, arg2, proc);
final Class<?>[] paramTypes = method.getParameterTypes();
Object cArg0 = arg0.toJava(paramTypes[0]);
Object cArg1 = arg1.toJava(paramTypes[1]);
Object cArg2 = arg2.toJava(paramTypes[2]);
Object cArg3 = proc.toJava(paramTypes[3]);

return method.invokeStaticDirect(context, cArg0, cArg1, cArg2, cArg3);
}
Original file line number Diff line number Diff line change
@@ -96,7 +96,7 @@ private static class NewMethod extends org.jruby.internal.runtime.methods.JavaMe
newMethod = clazz.searchMethod("new");
}

private boolean needsCreate(IRubyObject proxy) {
private static boolean needsCreate(IRubyObject proxy) {
return ((JavaProxy) proxy).object == null;
}

39 changes: 22 additions & 17 deletions core/src/main/java/org/jruby/java/proxies/InterfaceJavaProxy.java
Original file line number Diff line number Diff line change
@@ -21,37 +21,42 @@
* @author headius
*/
public class InterfaceJavaProxy extends JavaProxy {

public InterfaceJavaProxy(Ruby runtime, RubyClass klazz) {
super(runtime, klazz);
}

private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new InterfaceJavaProxy(runtime, klazz);
}
};

public static RubyClass createInterfaceJavaProxy(ThreadContext context) {
Ruby runtime = context.runtime;

RubyClass ifcJavaProxy = runtime.defineClass(
"InterfaceJavaProxy",
runtime.getJavaSupport().getJavaProxyClass(), new ObjectAllocator() {
public IRubyObject allocate(Ruby runtime, RubyClass klazz) {
return new InterfaceJavaProxy(runtime, klazz);
}
});

RubyClass javaIfcExtender = runtime.defineClass(
"JavaInterfaceExtender", runtime.getObject(), runtime.getObject().getAllocator());
javaIfcExtender.defineAnnotatedMethods(JavaInterfaceExtender.class);

return ifcJavaProxy;
final Ruby runtime = context.runtime;
RubyClass InterfaceJavaProxy = runtime.defineClass(
"InterfaceJavaProxy", runtime.getJavaSupport().getJavaProxyClass(), ALLOCATOR
);

RubyClass JavaInterfaceExtended = runtime.defineClass(
"JavaInterfaceExtender", runtime.getObject(), runtime.getObject().getAllocator()
);
JavaInterfaceExtended.defineAnnotatedMethods(JavaInterfaceExtender.class);

return InterfaceJavaProxy;
}

public static class JavaInterfaceExtender {
@JRubyMethod(visibility = Visibility.PRIVATE)
public static IRubyObject initialize(ThreadContext context, IRubyObject self, IRubyObject javaClassName, Block block) {
Ruby runtime = context.runtime;

self.getInstanceVariables().setInstanceVariable("@java_class", JavaClass.forNameVerbose(runtime, javaClassName.asJavaString()));
self.getInstanceVariables().setInstanceVariable("@block", RubyProc.newProc(runtime, block, block.type));

return runtime.getNil();
self.getInternalVariables().getInternalVariable("@block");

return context.nil;
}

@JRubyMethod
38 changes: 25 additions & 13 deletions core/src/main/java/org/jruby/javasupport/Java.java
Original file line number Diff line number Diff line change
@@ -40,7 +40,6 @@

import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -111,6 +110,7 @@
import org.jruby.util.cli.Options;
import org.jruby.util.collections.IntHashMap;
import static org.jruby.java.dispatch.CallableSelector.newCallableCache;
import static org.jruby.java.invokers.RubyToJavaInvoker.convertArguments;

@JRubyModule(name = "Java")
public class Java implements Library {
@@ -725,6 +725,7 @@ public static IRubyObject concrete_proxy_inherited(final IRubyObject clazz, fina
return context.nil;
}

// called for Ruby sub-classes of a Java class
private static void setupJavaSubclass(final ThreadContext context, final RubyClass subclass) {

subclass.getInstanceVariables().setInstanceVariable("@java_proxy_class", context.nil);
@@ -756,12 +757,8 @@ public IRubyObject call(final ThreadContext context, IRubyObject self, RubyModul
}

final int argsLength = args.length;
final RubyArray constructors = ((JavaProxyClass) proxyClass).constructors();
ArrayList<JavaProxyConstructor> forArity = new ArrayList<JavaProxyConstructor>(constructors.size());
for ( int i = 0; i < constructors.size(); i++ ) {
JavaProxyConstructor constructor = (JavaProxyConstructor) constructors.eltInternal(i);
if ( constructor.getArity() == argsLength ) forArity.add(constructor);
}
final JavaProxyConstructor[] constructors = ((JavaProxyClass) proxyClass).getConstructors();
ArrayList<JavaProxyConstructor> forArity = findCallablesForArity(argsLength, constructors);

if ( forArity.size() == 0 ) {
throw context.runtime.newArgumentError("wrong number of arguments for constructor");
@@ -776,18 +773,33 @@ public IRubyObject call(final ThreadContext context, IRubyObject self, RubyModul
throw context.runtime.newArgumentError("wrong number of arguments for constructor");
}

final Object[] javaArgs = new Object[argsLength];
Class[] parameterTypes = matching.getParameterTypes();
for ( int i = 0; i < argsLength; i++ ) {
javaArgs[i] = args[i].toJava(parameterTypes[i]);
}

final Object[] javaArgs = convertArguments(matching, args);
JavaObject newObject = matching.newInstance(self, javaArgs);

return JavaUtilities.set_java_object(self, self, newObject);
}
});
}

// NOTE: move to RubyToJavaInvoker for re-use ?!
static <T extends ParameterTypes> ArrayList<T> findCallablesForArity(final int arity, final T[] callables) {
final ArrayList<T> forArity = new ArrayList<T>(callables.length);
for ( int i = 0; i < callables.length; i++ ) {
final T callable = callables[i];
final int callableArity = callable.getArity();

if ( callableArity == arity ) forArity.add(callable);
// for arity 2 :
// - callable arity 1 ([]...) is OK
// - callable arity 2 (arg1, []...) is OK
// - callable arity 3 (arg1, arg2, []...) is OK
else if ( callable.isVarArgs() && callableArity - 1 <= arity ) {
forArity.add(callable);
}
}
return forArity;
}

// package scheme 2: separate module for each full package name, constructed
// from the camel-cased package segments: Java::JavaLang::Object,
private static void addToJavaPackageModule(RubyModule proxyClass) {
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/javasupport/ParameterTypes.java
Original file line number Diff line number Diff line change
@@ -36,4 +36,6 @@ public interface ParameterTypes {
Class<?>[] getParameterTypes();
Class<?>[] getExceptionTypes();
boolean isVarArgs();

int getArity();
}
25 changes: 13 additions & 12 deletions core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java
Original file line number Diff line number Diff line change
@@ -160,14 +160,19 @@ public Class[] getInterfaces() {
return result;
}

private transient JavaProxyConstructor[] constructors;

public JavaProxyConstructor[] getConstructors() {
JavaProxyConstructor[] constructors = this.constructors;
if ( constructors != null ) return constructors;

final Ruby runtime = getRuntime();
final Constructor[] constructors = proxyClass.getConstructors();
JavaProxyConstructor[] result = new JavaProxyConstructor[constructors.length];
for ( int i = 0; i < constructors.length; i++ ) {
result[i] = new JavaProxyConstructor(runtime, this, constructors[i]);
final Constructor[] ctors = proxyClass.getConstructors();
constructors = new JavaProxyConstructor[ ctors.length ];
for ( int i = 0; i < ctors.length; i++ ) {
constructors[i] = new JavaProxyConstructor(runtime, this, ctors[i]);
}
return result;
return this.constructors = constructors;
}

public JavaProxyConstructor getConstructor(final Class[] args)
@@ -407,7 +412,7 @@ public IRubyObject do_invoke(final IRubyObject[] args) {
}
}

private int getArity() {
public final int getArity() {
return getParameterTypes().length;
}

@@ -734,21 +739,17 @@ public RubyObject superclass() {

@JRubyMethod
public RubyArray methods() {
return toRubyArray(getMethods());
return toRubyArray( getMethods() );
}

@JRubyMethod
public RubyArray interfaces() {
return toRubyArray(getInterfaces());
}

private RubyArray constructors;

@JRubyMethod
public final RubyArray constructors() {
final RubyArray constructors = this.constructors;
if ( constructors != null ) return constructors;
return this.constructors = toRubyArray( getConstructors() );
return toRubyArray( getConstructors() );
}

public final String nameOnInspection() {
Original file line number Diff line number Diff line change
@@ -28,6 +28,10 @@

package org.jruby.javasupport.proxy;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
@@ -56,6 +60,7 @@
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.GeneratorAdapter;
@@ -256,7 +261,7 @@ private ClassWriter beginProxyClass(final String className,
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);

// start class
cw.visit(Opcodes.V1_3, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER,
cw.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER,
toInternalClassName(className), /*signature*/ null,
toInternalClassName(superClass),
interfaceNamesForProxyClass(interfaces));
@@ -346,7 +351,7 @@ private void generateProxyMethod(Type selfType, Type superType,
if (!md.generateProxyMethod()) return;

org.objectweb.asm.commons.Method m = md.getMethod();
Type[] ex = toType(md.getExceptions());
Type[] ex = toTypes(md.getExceptions());

String field_name = "__mth$" + md.getName() + md.scrambledSignature();

@@ -363,8 +368,8 @@ private void generateProxyMethod(Type selfType, Type superType,
clazzInit.putStatic(selfType, field_name, PROXY_METHOD_TYPE);

org.objectweb.asm.commons.Method sm = new org.objectweb.asm.commons.Method(
"__super$" + m.getName(), m.getReturnType(), m
.getArgumentTypes());
"__super$" + m.getName(), m.getReturnType(), m.getArgumentTypes()
);

//
// construct the proxy method
@@ -373,8 +378,7 @@ private void generateProxyMethod(Type selfType, Type superType,
ex, cw);

ga.loadThis();
ga.getField(selfType, INVOCATION_HANDLER_FIELD_NAME,
INVOCATION_HANDLER_TYPE);
ga.getField(selfType, INVOCATION_HANDLER_FIELD_NAME, INVOCATION_HANDLER_TYPE);

// if the method is extending something, then we have
// to test if the handler is initialized...
@@ -396,7 +400,7 @@ private void generateProxyMethod(Type selfType, Type superType,

if (m.getArgumentTypes().length == 0) {
// load static empty array
ga.getStatic(JAVA_PROXY_TYPE, "NO_ARGS", Type.getType(Object[].class));
ga.getStatic(JAVA_PROXY_TYPE, "NO_ARGS", toType(Object[].class));
} else {
// box arguments
ga.loadArgArray();
@@ -422,16 +426,15 @@ private void generateProxyMethod(Type selfType, Type superType,
ga.visitTryCatchBlock(before, after, rethrow, "java/lang/Error");
ga.visitTryCatchBlock(before, after, rethrow, "java/lang/RuntimeException");

Type thr = Type.getType(Throwable.class);
Type thr = toType(Throwable.class);
Label handler = ga.mark();
Type udt = Type.getType(UndeclaredThrowableException.class);
Type udt = toType(UndeclaredThrowableException.class);
int loc = ga.newLocal(thr);
ga.storeLocal(loc, thr);
ga.newInstance(udt);
ga.dup();
ga.loadLocal(loc, thr);
ga.invokeConstructor(udt, org.objectweb.asm.commons.Method
.getMethod("void <init>(java.lang.Throwable)"));
ga.invokeConstructor(udt, org.objectweb.asm.commons.Method.getMethod("void <init>(java.lang.Throwable)"));
ga.throwException();

ga.visitTryCatchBlock(before, after, handler, "java/lang/Throwable");
@@ -464,24 +467,26 @@ private Class[] generateConstructor(Type selfType, Constructor constructor, Clas
String name1 = "<init>";
String signature = null;
Class[] superConstructorExceptions = constructor.getExceptionTypes();
boolean superConstructorVarArgs = constructor.isVarArgs();

org.objectweb.asm.commons.Method super_m = new org.objectweb.asm.commons.Method(
name1, Type.VOID_TYPE, toType(superConstructorParameterTypes));
name1, Type.VOID_TYPE, toTypes(superConstructorParameterTypes));
org.objectweb.asm.commons.Method m = new org.objectweb.asm.commons.Method(
name1, Type.VOID_TYPE, toType(newConstructorParameterTypes));
name1, Type.VOID_TYPE, toTypes(newConstructorParameterTypes));

GeneratorAdapter ga = new GeneratorAdapter(access, m, signature,
toType(superConstructorExceptions), cw);
String[] exceptionNames = toInternalNames( superConstructorExceptions );
MethodVisitor mv = cw.visitMethod(access, m.getName(), m.getDescriptor(), signature, exceptionNames);
// marking with @SafeVarargs so that we can correctly detect proxied var-arg consturctors :
if ( superConstructorVarArgs ) mv.visitAnnotation(Type.getDescriptor(VarArgs.class), true);
GeneratorAdapter ga = new GeneratorAdapter(access, m, mv);

ga.loadThis();
ga.loadArgs(0, superConstructorParameterTypes.length);
ga.invokeConstructor(Type.getType(constructor.getDeclaringClass()),
super_m);
ga.invokeConstructor(toType(constructor.getDeclaringClass()), super_m);

ga.loadThis();
ga.loadArg(superConstructorParameterTypes.length);
ga.putField(selfType, INVOCATION_HANDLER_FIELD_NAME,
INVOCATION_HANDLER_TYPE);
ga.putField(selfType, INVOCATION_HANDLER_FIELD_NAME, INVOCATION_HANDLER_TYPE);

// do a void return
ga.returnValue();
@@ -490,6 +495,14 @@ private Class[] generateConstructor(Type selfType, Constructor constructor, Clas
return newConstructorParameterTypes;
}

static boolean isVarArgs(final Constructor<?> ctor) {
return ctor.isVarArgs() || ctor.getAnnotation(VarArgs.class) != null;
}

//static boolean isVarArgs(final Method method) {
// return method.isVarArgs() || method.getAnnotation(VarArgs.class) != null;
//}

private static String toInternalClassName(Class clazz) {
return toInternalClassName(clazz.getName());
}
@@ -498,12 +511,25 @@ private static String toInternalClassName(String name) {
return name.replace('.', '/');
}

private static Type[] toType(Class[] parameterTypes) {
Type[] result = new Type[parameterTypes.length];
for (int i = 0; i < result.length; i++) {
result[i] = Type.getType(parameterTypes[i]);
private static Type toType(Class clazz) {
return Type.getType(clazz);
}

private static Type[] toTypes(Class[] params) {
Type[] types = new Type[params.length];
for (int i = 0; i < types.length; i++) {
types[i] = Type.getType(params[i]);
}
return result;
return types;
}

private static String[] toInternalNames(final Class[] params) {
if (params == null) return null;
String[] names = new String[params.length];
for (int i = 0; i < names.length; ++i) {
names[i] = Type.getType(params[i]).getInternalName();
}
return names;
}

private static Map<MethodKey, MethodData> collectMethods(
@@ -776,4 +802,12 @@ private static String proxyPackageName(Class clazz) {
return "org.jruby.proxy." + clazzName.substring(0, idx);
}

/**
* Variable arguments marker for generated constructor.
* @note could have used @SafeVarargs but it's Java 7+
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.METHOD})
public static @interface VarArgs {}

}
Original file line number Diff line number Diff line change
@@ -59,7 +59,8 @@
public class JavaProxyConstructor extends JavaProxyReflectionObject implements ParameterTypes {

private final Constructor<?> proxyConstructor;
private final Class<?>[] apparentParameterTypes;
private final Class<?>[] actualParameterTypes;
private final boolean actualVarArgs;

private final JavaProxyClass declaringProxyClass;

@@ -79,23 +80,22 @@ public static RubyClass createJavaProxyConstructorClass(Ruby runtime, RubyModule
super(runtime, runtime.getJavaSupport().getJavaProxyConstructorClass());
this.declaringProxyClass = proxyClass;
this.proxyConstructor = constructor;

Class<?>[] parameterTypes = constructor.getParameterTypes();
final int len = parameterTypes.length - 1;
System.arraycopy(parameterTypes, 0, apparentParameterTypes = new Class<?>[len], 0, len);
final int len = parameterTypes.length - 1; // last argument is our invocation handler
// see JavaProxyClassFactory's generateConstructor ...
System.arraycopy(parameterTypes, 0, actualParameterTypes = new Class<?>[len], 0, len);
this.actualVarArgs = JavaProxyClassFactory.isVarArgs(proxyConstructor);
}

public final Class<?>[] getParameterTypes() {
return apparentParameterTypes;
return actualParameterTypes;
}

public final Class<?>[] getExceptionTypes() {
return proxyConstructor.getExceptionTypes();
}

public final boolean isVarArgs() {
return proxyConstructor.isVarArgs();
}
public final boolean isVarArgs() { return actualVarArgs; }

@JRubyMethod(name = "declaring_class")
public JavaProxyClass getDeclaringClass() {
@@ -106,7 +106,7 @@ public final Object newInstance(Object[] args, JavaProxyInvocationHandler handle
throws IllegalArgumentException, InstantiationException,
IllegalAccessException, InvocationTargetException {
final int len = args.length;
if ( len != apparentParameterTypes.length ) {
if ( len != actualParameterTypes.length ) {
throw new IllegalArgumentException("wrong number of parameters");
}

@@ -148,6 +148,11 @@ public RubyString inspect() {
return getRuntime().newString( str.toString() );
}

@Override
public String toString() {
return inspect().toString();
}

@JRubyMethod
public final RubyArray argument_types() {
return toRubyArray(getParameterTypes());
@@ -203,29 +208,31 @@ public Object invoke(Object proxy, JavaProxyMethod proxyMethod, Object[] nargs)
final String name = proxyMethod.getName();
final DynamicMethod method = metaClass.searchMethod(name);

final IRubyObject result = invokeRuby(method, proxyMethod, metaClass, name, nargs);

final Class<?> returnType = proxyMethod.getReturnType();
return returnType == void.class ? null : result.toJava( returnType );
}

private IRubyObject invokeRuby(final DynamicMethod method, final JavaProxyMethod proxyMethod,
final RubyClass metaClass, final String name, final Object[] nargs) {
final IRubyObject[] newArgs = new IRubyObject[nargs.length];
for ( int i = nargs.length; --i >= 0; ) {
newArgs[i] = JavaUtil.convertJavaToUsableRubyObject(runtime, nargs[i]);
}

final int arity = method.getArity().getValue();

final IRubyObject result;
if ( arity < 0 || arity == newArgs.length ) {
final ThreadContext context = runtime.getCurrentContext();
result = method.call(context, self, metaClass, name, newArgs);
return method.call(context, self, metaClass, name, newArgs);
}
else if ( proxyMethod.hasSuperImplementation() ) {
if ( proxyMethod.hasSuperImplementation() ) {
final ThreadContext context = runtime.getCurrentContext();
final RubyClass superClass = metaClass.getSuperClass();
result = Helpers.invokeAs(context, superClass, self, name, newArgs, Block.NULL_BLOCK);
}
else {
throw runtime.newArgumentError(newArgs.length, arity);
return Helpers.invokeAs(context, superClass, self, name, newArgs, Block.NULL_BLOCK);
}

final Class<?> returnType = proxyMethod.getReturnType();
return returnType == void.class ? null : result.toJava( returnType );
throw runtime.newArgumentError(newArgs.length, arity);
}

}
159 changes: 157 additions & 2 deletions test/jruby/test_higher_javasupport.rb
Original file line number Diff line number Diff line change
@@ -95,10 +95,165 @@ def test_class_methods_differing_only_on_argument_type

Character = java.lang.Character
def test_constants
assert_equal(9223372036854775807, Long::MAX_VALUE)
assert_equal 9223372036854775807, Long::MAX_VALUE
assert(! defined? Character::Y_DATA) # Known private field in Character

# class definition with "_" constant causes error
assert_nothing_raised { org.jruby.javasupport.test.ConstantHolder }
org.jruby.javasupport.test.VariableArguments
assert_equal '_', org.jruby.javasupport.test.VariableArguments::_LEADING_UNDERSCORE
end

VarArgsCtor = org.jruby.javasupport.test.VariableArguments

def test_varargs_constructor
var_ar = VarArgsCtor.new
assert_equal nil, var_ar.constants

var_ar = VarArgsCtor.new '0'
assert_equal '0', var_ar.constants[0]

var_ar = org.jruby.javasupport.test.VariableArguments.new '0', '1'
assert_equal '1', var_ar.constants[1]

assert_raises(NameError) do
org.jruby.javasupport.test.VariableArguments.new '0', 1
end
end

class RubyVarArgsCtor1 < VarArgsCtor
def initialize(a1, a2); super(a1, a2) end
end

class RubyVarArgsCtor2 < VarArgsCtor
def initialize(); super(nil) end
end

class RubyVarArgsCtor3 < VarArgsCtor
def initialize(); super() end
end

class RubyVarArgsCtor4 < VarArgsCtor
# implicit initialize()
end

VarArgOnly = org.jruby.javasupport.test.VariableArguments::VarArgOnly

class RubyVarArgsOnlyCtor1 < VarArgOnly
# implicit initialize()
end

class RubyVarArgsOnlyCtor2 < VarArgOnly
def initialize(); end
end

class RubyVarArgsOnlyCtor3 < VarArgOnly
def initialize(*args); super end
end

class RubyVarArgsOnlyCtor4 < VarArgOnly
def initialize(*args)
super(args.to_java)
end
end

StringVarArgOnly = org.jruby.javasupport.test.VariableArguments::StringVarArgOnly

class RubyVarArgsOnlyCtor5 < StringVarArgOnly
# NOTE: do not work (so far) due component type mismatch :
#def initialize(*args); super end
#def initialize(*args); super(*args) end
def initialize(*args)
super(args.to_java(:String))
end
end

def test_varargs_constructor_in_ruby_subclass
var_args = RubyVarArgsCtor1.new 'foo', 'bar'
assert_equal 'foo', var_args.constants[0]
assert_equal 'bar', var_args.constants[1]

var_args = RubyVarArgsCtor2.new
assert_equal nil, var_args.constants[0]

var_args = RubyVarArgsCtor3.new
assert_equal nil, var_args.constants

var_args = RubyVarArgsCtor4.new
assert_equal nil, var_args.constants

#

var_args = RubyVarArgsOnlyCtor1.new
assert_equal 0, var_args.constants.length

var_args = RubyVarArgsOnlyCtor2.new
assert_equal 0, var_args.constants.length

var_args = RubyVarArgsOnlyCtor3.new
assert_equal 0, var_args.constants.length

var_args = RubyVarArgsOnlyCtor3.new('1')
assert_equal 1, var_args.constants.length

var_args = RubyVarArgsOnlyCtor4.new
assert_equal 0, var_args.constants.length

var_args = RubyVarArgsOnlyCtor4.new(1)
assert_equal 1, var_args.constants.length

var_args = RubyVarArgsOnlyCtor4.new(1, 2)
assert_equal 2, var_args.constants.length

var_args = RubyVarArgsOnlyCtor5.new
assert_equal 0, var_args.constants.length

var_args = RubyVarArgsOnlyCtor5.new('1')
assert_equal 1, var_args.constants.length
end

def test_varargs_overloaded_method
var_args = VarArgsCtor.new 'foo'
var_args.setConstants 'bar'
assert_equal 'bar', var_args.constants[0]

var_args.setConstants 'foo,bar' # (String)
assert_equal 'foo', var_args.constants[0]
assert_equal 'bar', var_args.constants[1]

var_args = RubyVarArgsOnlyCtor2.new
var_args.setConstants 'foo', 'bar' # (String, String)
assert_equal 'foo', var_args.constants[0]
assert_equal 'bar', var_args.constants[1]

var_args.setConstants 'foo', 'bar', 'baz' # (String, String...)
assert_equal 'bar', var_args.constants[0]
assert_equal 'baz', var_args.constants[1]
assert_equal 'foo', var_args.constants[2]

var_args.setConstants '1', '2', '3', '4', '5'
assert_equal '2', var_args.constants[0]
assert_equal '1', var_args.constants[4]
end

def test_varargs_only_method
a = java.util.Arrays.asList(1)
assert_equal 1, a[0]
a = java.util.Arrays.asList('1', '2', '3')
assert_equal '1', a[0]
assert_equal '3', a[2]
a = java.util.Arrays.asList('1', 2, 3.0, 4)
assert_equal '1', a[0]
assert_equal 2, a[1]
a = java.util.Arrays.asList([1, 2])
assert_equal [1, 2], a[0]
a = java.util.Arrays.asList([1, 2], [3])
assert_equal [1, 2], a[0]
assert_equal [3], a[1]
a = java.util.Arrays.asList([1, 2].to_java)
assert_equal 1, a[0]
assert_equal 2, a[1]
a = java.util.Arrays.asList
assert_equal 0, a.size
end

java_import org.jruby.javasupport.test.Room
33 changes: 0 additions & 33 deletions test/org/jruby/javasupport/test/ConstantHolder.java

This file was deleted.

17 changes: 8 additions & 9 deletions test/org/jruby/javasupport/test/Room.java
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@
*
* Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -30,22 +30,21 @@

public class Room {
private final String name;

public Room(String name) {
this.name = name;
}

public boolean equals(Object obj) {
if (! (obj instanceof Room))
return false;
Room that = (Room) obj;
return name.equals(that.name);
if (! (obj instanceof Room)) return false;
Room that = (Room) obj;
return name.equals(that.name);
}

public String toString() {
return name;
}

public int hashCode() {
return name.hashCode();
}
104 changes: 104 additions & 0 deletions test/org/jruby/javasupport/test/VariableArguments.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
/***** BEGIN LICENSE BLOCK *****
* Version: EPL 1.0/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Common Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.eclipse.org/legal/cpl-v10.html
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the EPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the EPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****/
package org.jruby.javasupport.test;

import java.util.ArrayList;

public class VariableArguments {

public static final String _LEADING_UNDERSCORE = "_";

public Object[] constants;
protected final String[] arguments;

public VariableArguments() {
constants = arguments = null;
}

public VariableArguments(String... args) {
constants = arguments = args;
}

public String[] getArgs() { return arguments; }

public void setConstants(String constants) {
this.constants = constants.split(",");
}

public void setConstants(String const1, String const2) {
this.constants = new String[] { const1, const2 };
}

public void setConstants(String c, String... constants) {
this.constants = new String[constants.length + 1];
this.constants[ constants.length ] = c;
System.arraycopy(constants, 0, this.constants, 0, constants.length);
}

public void setConstants(String... constants) {
this.constants = constants;
}

public static class VarArgOnly extends VariableArguments {

public VarArgOnly(Object... constants) {
super( strArgs(constants) );
this.constants = constants;
}

private static String[] strArgs(final Object... constants) {
ArrayList<String> args = new ArrayList<String>();
for ( Object constant : constants ) {
if ( constant instanceof String ) args.add((String) constant);
}
return args.toArray( new String[ args.size() ] );
}

}

public static class StringVarArgOnly extends VariableArguments {

public StringVarArgOnly(String... constants) {
super(constants);
}

}

public static class SingleArg extends VariableArguments {

public SingleArg(String constants) {
super(constants + "_single");
}

public SingleArg(String... constants) {
super(constants);
}

}

}

0 comments on commit 487938c

Please sign in to comment.