Skip to content

Commit

Permalink
[ji] at last - deal with Ruby - Java method conflicts with interface …
Browse files Browse the repository at this point in the history
…impl using a block

we're now add an internal "impl" method for each prescribed abstract interface method

this is expected to resolve conflicting issues (e.g using Java 8 streams) such as #3475
kares committed Apr 18, 2016
1 parent cf11355 commit 79bb8d1
Showing 2 changed files with 60 additions and 9 deletions.
53 changes: 46 additions & 7 deletions core/src/main/java/org/jruby/javasupport/Java.java
Original file line number Diff line number Diff line change
@@ -65,9 +65,10 @@
import org.jruby.javasupport.binding.Initializer;
import org.jruby.javasupport.proxy.JavaProxyClass;
import org.jruby.javasupport.proxy.JavaProxyConstructor;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.load.Library;
@@ -995,18 +996,14 @@ public final IRubyObject call(ThreadContext context, IRubyObject self, RubyModul

}

final static class ProcToInterface extends org.jruby.internal.runtime.methods.DynamicMethod {
static final class ProcToInterface extends org.jruby.internal.runtime.methods.DynamicMethod {

ProcToInterface(final RubyClass singletonClass) {
super(singletonClass, PUBLIC);
}

@Override // method_missing impl :
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
if ( ! ( self instanceof RubyProc ) ) {
throw context.runtime.newTypeError("interface impl method_missing for block used with non-Proc object");
}
final RubyProc proc = (RubyProc) self;
final IRubyObject[] newArgs;
switch( args.length ) {
case 1 : newArgs = IRubyObject.NULL_ARRAY; break;
@@ -1015,14 +1012,56 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
default : newArgs = new IRubyObject[ args.length - 1 ];
System.arraycopy(args, 1, newArgs, 0, newArgs.length);
}
return proc.call(context, newArgs);
return callProc(context, self, newArgs);
}

private IRubyObject callProc(ThreadContext context, IRubyObject self, IRubyObject[] procArgs) {
if ( ! ( self instanceof RubyProc ) ) {
throw context.runtime.newTypeError("interface impl method_missing for block used with non-Proc object");
}
return ((RubyProc) self).call(context, procArgs);
}

@Override
public DynamicMethod dup() {
return this;
}

final ConcreteMethod getConcreteMethod() { return new ConcreteMethod(); }

final class ConcreteMethod extends org.jruby.internal.runtime.methods.JavaMethod {

ConcreteMethod() {
super(ProcToInterface.this.implementationClass, Visibility.PUBLIC);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule klazz, String name, Block block) {
return ProcToInterface.this.callProc(context, self, IRubyObject.NULL_ARRAY);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule klazz, String name, IRubyObject arg0, Block block) {
return ProcToInterface.this.callProc(context, self, new IRubyObject[]{arg0});
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule klazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
return ProcToInterface.this.callProc(context, self, new IRubyObject[]{arg0, arg1});
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule klazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
return ProcToInterface.this.callProc(context, self, new IRubyObject[]{arg0, arg1, arg2});
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule klazz, String name, IRubyObject[] args, Block block) {
return ProcToInterface.this.callProc(context, self, args);
}

}

}

private static RubyModule getProxyUnderClass(final ThreadContext context,
16 changes: 14 additions & 2 deletions core/src/main/java/org/jruby/javasupport/JavaUtil.java
Original file line number Diff line number Diff line change
@@ -37,13 +37,14 @@
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ReflectPermission;
import static java.lang.Character.isLetter;
import static java.lang.Character.isLowerCase;
import static java.lang.Character.isUpperCase;
import static java.lang.Character.isDigit;
import static java.lang.Character.toLowerCase;

import java.lang.reflect.ReflectPermission;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.security.AccessController;
@@ -242,7 +243,18 @@ public static <T> T convertProcToInterface(ThreadContext context, RubyBasicObjec
// Proc implementing an interface, pull in the catch-all code that lets the proc get invoked
// no matter what method is called on the interface
final RubyClass singletonClass = rubyObject.getSingletonClass();
singletonClass.addMethod("method_missing", new Java.ProcToInterface(singletonClass));
final Java.ProcToInterface procToIface = new Java.ProcToInterface(singletonClass);
singletonClass.addMethod("method_missing", procToIface);
// similar to Iface.impl { ... } - bind interface method(s) to avoid Java-Ruby conflicts
// ... e.g. calling a Ruby implemented Predicate#test should not dispatch to Kernel#test
final Java.ProcToInterface.ConcreteMethod implMethod = procToIface.getConcreteMethod();
// getMethods for interface returns all methods (including ones from super-interfaces)
for ( Method method : targetType.getMethods() ) {
if ( Modifier.isAbstract(method.getModifiers()) ) {
singletonClass.addMethodInternal(method.getName(), implMethod);
}
}

}
JavaObject javaObject = (JavaObject) Helpers.invoke(context, rubyObject, "__jcreate_meta!");
return (T) javaObject.getValue();

0 comments on commit 79bb8d1

Please sign in to comment.