Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 7774be525ac7
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 10476d1c1e41
Choose a head ref
  • 10 commits
  • 18 files changed
  • 1 contributor

Commits on Apr 7, 2015

  1. Copy the full SHA
    1a605a2 View commit details
  2. Copy the full SHA
    da4c16a View commit details
  3. Copy the full SHA
    1e56da4 View commit details
  4. Copy the full SHA
    4e1fe96 View commit details
  5. Copy the full SHA
    03ed29a View commit details
  6. handle Ruby super -> Java constructor with var-args correctly (fixes …

    …cases in #2375)
    
    ... since we're storing an annotation "flag" on the gen constructor thus can support this
    should be safe to land on JRuby 1.7 since users should not assume raising behaviour !
    kares committed Apr 7, 2015
    Copy the full SHA
    5fa2cb1 View commit details
  7. private can be static

    kares committed Apr 7, 2015
    Copy the full SHA
    8364d46 View commit details
  8. Copy the full SHA
    d06ab37 View commit details
  9. Copy the full SHA
    23dea57 View commit details
  10. Copy the full SHA
    10476d1 View commit details
18 changes: 10 additions & 8 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -1398,11 +1398,12 @@ public IRubyObject removeInstanceVariable(String name) {
// TODO: must override in RubyModule to pick up constants
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;
}
@@ -1413,11 +1414,12 @@ public List<Variable<IRubyObject>> getInstanceVariableList() {
// TODO: must override in RubyModule to pick up constants
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;
}
80 changes: 34 additions & 46 deletions core/src/main/java/org/jruby/java/invokers/ConstructorInvoker.java
Original file line number Diff line number Diff line change
@@ -54,26 +54,10 @@ 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);
}
}

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

final Object[] convertedArgs = convertArguments(constructor, args);
proxy.setObject( constructor.newInstanceDirect(convertedArgs) );
return self;
}

@@ -93,7 +77,8 @@ 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(cArg0));

@@ -105,8 +90,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(cArg0, cArg1));

@@ -118,9 +104,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(cArg0, cArg1, cArg2));

@@ -138,17 +125,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(convertedArgs));

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

@Override
@@ -158,14 +146,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(cArg0));

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

@Override
@@ -175,15 +163,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(cArg0, cArg1));

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

@Override
@@ -193,16 +181,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(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 +200,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(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(proxy.getObject(), convertedArgs);
JavaMethod method = (JavaMethod) findCallable(self, name, args, args.length);
return method.invokeDirect( 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(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(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(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(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(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(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(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(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(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(singleton, convertedArgs);
JavaMethod method = (JavaMethod) findCallable(self, name, args, args.length);
return method.invokeDirect( 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(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(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(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(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(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(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(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(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(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(convertedArgs);
JavaMethod method = (JavaMethod) findCallable(self, name, args, args.length);
return method.invokeStaticDirect( 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();
}

@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(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(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(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(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(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(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(cArg0, cArg1, cArg2);
}
@@ -143,11 +136,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(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
@@ -36,7 +36,6 @@
import java.io.IOException;
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 {
@@ -724,6 +724,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);
@@ -755,12 +756,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");
@@ -775,18 +772,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);
}

}
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);
}

}

}
159 changes: 157 additions & 2 deletions test/test_higher_javasupport.rb
Original file line number Diff line number Diff line change
@@ -79,10 +79,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