From ab2ccf6b043d71f2774d5f3136c1b3643077efbd Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Thu, 1 Oct 2020 22:00:50 -0400 Subject: [PATCH 01/49] WIP: first demo of 0-arg ctor callable from java getClass().newInstance() --- .../jruby/java/proxies/ConcreteJavaProxy.java | 1 + .../main/java/org/jruby/javasupport/Java.java | 38 ++++- .../proxy/InternalJavaProxyHelper.java | 21 +++ .../javasupport/proxy/JavaProxyClass.java | 1 + .../proxy/JavaProxyClassFactory.java | 158 +++++++++++++++++- .../proxy/JavaProxyConstructor.java | 48 +++++- .../proxy/JavaProxyInvocationHandler.java | 8 + 7 files changed, 252 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index 5b07f1a525b..33e39bebe13 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -37,6 +37,7 @@ public static RubyClass createConcreteJavaProxy(final ThreadContext context) { return ConcreteJavaProxy; } + ///jcreates site private static final class InitializeMethod extends org.jruby.internal.runtime.methods.JavaMethod { private final CallSite jcreateSite = MethodIndex.getFunctionalCallSite("__jcreate!"); diff --git a/core/src/main/java/org/jruby/javasupport/Java.java b/core/src/main/java/org/jruby/javasupport/Java.java index a2e02d3f7e7..0d27a1716bf 100644 --- a/core/src/main/java/org/jruby/javasupport/Java.java +++ b/core/src/main/java/org/jruby/javasupport/Java.java @@ -50,6 +50,7 @@ import java.math.BigInteger; import java.util.Arrays; import java.util.HashSet; +import java.util.Iterator; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -632,7 +633,8 @@ public final IRubyObject call(ThreadContext context, IRubyObject self, RubyModul final JavaProxyConstructor matching; switch (constructors.length) { - case 1: matching = matchConstructor0ArityOne(context, constructors, arg0); break; + case 1: + case 2: matching = matchConstructor0ArityOne(context, constructors, arg0); break; default: matching = matchConstructorArityOne(context, constructors, arg0); } @@ -640,25 +642,33 @@ public final IRubyObject call(ThreadContext context, IRubyObject self, RubyModul return JavaUtilities.set_java_object(self, self, newObject); } - @Override + @Override //TODO: ensure both sides are good public final IRubyObject call(final ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) { + if (((JavaProxy) self).getObject() == null ) + { final int arity = args.length; final JavaProxyConstructor[] constructors = getProxyClass(self).getConstructors(); final JavaProxyConstructor matching; switch (constructors.length) { - case 1: matching = matchConstructor0(context, constructors, arity, args); break; + case 1:// IS this logic sound? or should we be more careful + case 2: matching = matchConstructor0(context, constructors, arity, args); break; default: matching = matchConstructor(context, constructors, arity, args); } JavaObject newObject = matching.newInstance(self, args); return JavaUtilities.set_java_object(self, self, newObject); + } else + { + return (IRubyObject) ((JavaProxy)self).dataGetStruct(); + } } - // assumes only 1 constructor exists! + // assumes only 1 *Ruby* constructor exists! (Filters out nonruby) private JavaProxyConstructor matchConstructor0ArityOne(final ThreadContext context, final JavaProxyConstructor[] constructors, final IRubyObject arg0) { - JavaProxyConstructor forArity = checkCallableForArity(1, constructors, 0); + int index = constructors[0].isExportable() ? 1 : 0; + JavaProxyConstructor forArity = checkCallableForArity(1, constructors, index); if ( forArity == null ) { throw context.runtime.newArgumentError("wrong number of arguments for constructor"); @@ -677,7 +687,8 @@ private JavaProxyConstructor matchConstructor0ArityOne(final ThreadContext conte // assumes only 1 constructor exists! private JavaProxyConstructor matchConstructor0(final ThreadContext context, final JavaProxyConstructor[] constructors, final int arity, final IRubyObject[] args) { - JavaProxyConstructor forArity = checkCallableForArity(arity, constructors, 0); + int index = constructors[0].isExportable() ? 1 : 0; + JavaProxyConstructor forArity = checkCallableForArity(arity, constructors, index); if ( forArity == null ) { throw context.runtime.newArgumentError("wrong number of arguments for constructor"); @@ -696,6 +707,13 @@ private JavaProxyConstructor matchConstructor0(final ThreadContext context, private JavaProxyConstructor matchConstructorArityOne(final ThreadContext context, final JavaProxyConstructor[] constructors, final IRubyObject arg0) { ArrayList forArity = findCallablesForArity(1, constructors); + + // remove java-only methods + Iterator iter = forArity.iterator(); + while (iter.hasNext()) { + if(iter.next().isExportable()) + iter.remove(); + } if ( forArity.size() == 0 ) { throw context.runtime.newArgumentError("wrong number of arguments for constructor"); @@ -716,10 +734,16 @@ private JavaProxyConstructor matchConstructor(final ThreadContext context, final JavaProxyConstructor[] constructors, final int arity, final IRubyObject... args) { ArrayList forArity = findCallablesForArity(arity, constructors); + // remove java-only methods + Iterator iter = forArity.iterator(); + while (iter.hasNext()) { + if(iter.next().isExportable()) + iter.remove(); + } + if ( forArity.size() == 0 ) { throw context.runtime.newArgumentError("wrong number of arguments for constructor"); } - final JavaProxyConstructor matching = CallableSelector.matchingCallableArityN( context.runtime, this, forArity.toArray(new JavaProxyConstructor[forArity.size()]), args ); diff --git a/core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxyHelper.java b/core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxyHelper.java index be986480323..6d1dc45f507 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxyHelper.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxyHelper.java @@ -28,6 +28,12 @@ package org.jruby.javasupport.proxy; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.jruby.javasupport.proxy.JavaProxyClassFactory.ClassInvocationHolder; + /** * Contains methods that are only called from generated code * @@ -43,5 +49,20 @@ public static JavaProxyMethod initProxyMethod(JavaProxyClass proxyClass, String name, String desc, boolean hasSuper) { return proxyClass.initMethod(name, desc, hasSuper); } + + private static Map defaultJpihs = new ConcurrentHashMap<>(); + + public static int sized() + { + return defaultJpihs.size(); + } + + public static ClassInvocationHolder getDefaultJPIH(int id) { + return defaultJpihs.remove(id); + } + + public static void addDefaultJIPH(int id, ClassInvocationHolder jiph){ + defaultJpihs.put(id, jiph); + } } diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java index a6fba3d669e..4cff1e7f33d 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java @@ -196,6 +196,7 @@ public Class[] getInterfaces() { private transient JavaProxyConstructor[] constructors; + // TODO: filter out in Java Jcreate or here? public JavaProxyConstructor[] getConstructors() { JavaProxyConstructor[] constructors = this.constructors; if ( constructors != null ) return constructors; diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java index 1d191f18588..114639f9bd3 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java @@ -28,6 +28,7 @@ package org.jruby.javasupport.proxy; +import java.io.IOException; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -38,12 +39,25 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.UndeclaredThrowableException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import org.jruby.Ruby; import org.jruby.RubyClass; +import org.jruby.exceptions.RaiseException; +import org.jruby.internal.runtime.methods.DynamicMethod; +import org.jruby.java.proxies.JavaProxy; +import org.jruby.javasupport.Java; +import org.jruby.javasupport.JavaObject; +import org.jruby.javasupport.JavaUtil; +import org.jruby.javasupport.JavaUtilities; +import org.jruby.javasupport.proxy.JavaProxyConstructor.MethodInvocationHandler; +import org.jruby.runtime.Block; import org.jruby.runtime.Helpers; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ASM; import org.jruby.util.ArraySupport; import org.jruby.util.ClassDefiningClassLoader; @@ -78,6 +92,7 @@ public class JavaProxyClassFactory { .getMethod("java.lang.Class forName(java.lang.String)"); private static final String INVOCATION_HANDLER_FIELD_NAME = "__handler"; + private static final String INVOCATION_HANDLER_BUILDER_FIELD_NAME = "__handler_builder"; private static final String PROXY_CLASS_FIELD_NAME = "__proxy_class"; @@ -87,6 +102,8 @@ public class JavaProxyClassFactory { private static final Type INVOCATION_HANDLER_TYPE = Type.getType(JavaProxyInvocationHandler.class); + private static final Type INVOCATION_HANDLER_BUILDER_TYPE = Type.getType(ClassInvocationHolder.class); + // public Object invoke(Object receiver, JavaProxyMethod method, Object[] args) private static final org.objectweb.asm.commons.Method invoke = org.objectweb.asm.commons.Method .getMethod("java.lang.Object invoke(java.lang.Object, " + PROXY_METHOD_TYPE.getClassName() + ", java.lang.Object[])"); @@ -102,6 +119,19 @@ public class JavaProxyClassFactory { .getMethod(PROXY_METHOD_TYPE.getClassName() + " initProxyMethod(" + JavaProxyClass.class.getName() + ",java.lang.String,java.lang.String,boolean)"); + // public static ClassInvocationHolder getDefaultJPIH(Long) + private static final org.objectweb.asm.commons.Method getDefaultJPIH = org.objectweb.asm.commons.Method + .getMethod(ClassInvocationHolder.class.getName() + " getDefaultJPIH(int)"); + + // public JavaProxyInvocationHandler init(Object jself, Object[] args) // TODO: wrong types + private static final org.objectweb.asm.commons.Method initHandlerClass = org.objectweb.asm.commons.Method + .getMethod(JavaProxyInvocationHandler.class.getName() + " init("+JavaProxyClass.class.getName() + ",java.lang.Object,java.lang.Object[])"); + + + private static final org.objectweb.asm.commons.Method trampolineInit = org.objectweb.asm.commons.Method + .getMethod(JavaProxyInvocationHandler.class.getName() + "__jruby$trampoline$init(java.lang.Object[])"); + + private static final Type JAVA_PROXY_TYPE = Type.getType(InternalJavaProxy.class); private static final AtomicInteger counter = new AtomicInteger(0); @@ -158,14 +188,16 @@ public JavaProxyClass newProxyClass(final Ruby runtime, ClassDefiningClassLoader Type selfType = Type.getType('L' + toInternalClassName(targetClassName) + ';'); Map methods = collectMethods(superClass, interfaces, names, extraMethods, clazz.getMethodAnnotations()); + int id = System.identityHashCode(this); + InternalJavaProxyHelper.addDefaultJIPH(id, new ClassInvocationHolder(runtime, clazz)); - return generate(loader, targetClassName, superClass, interfaces, methods, selfType, clazz); + return generate(loader, targetClassName, superClass, interfaces, methods, selfType, clazz, id);//TODO: check for id not removed } private JavaProxyClass generate(ClassDefiningClassLoader loader, String targetClassName, Class superClass, Class[] interfaces, - Map methods, Type selfType, RubyClass rclass) { - ClassWriter cw = beginProxyClass(targetClassName, superClass, interfaces, loader); + Map methods, Type selfType, RubyClass rclass, int id) { + ClassWriter cw = beginProxyClass(selfType, targetClassName, superClass, interfaces, loader); Map>> fieldannos; Map fields; @@ -191,7 +223,7 @@ private JavaProxyClass generate(ClassDefiningClassLoader loader, String targetCl fv.visitEnd(); } - GeneratorAdapter clazzInit = createClassInitializer(selfType, cw); + GeneratorAdapter clazzInit = createClassInitializer(selfType, cw, id);//TODO:0 generateConstructors(superClass, selfType, cw); generate___getProxyClass(selfType, cw); @@ -206,6 +238,16 @@ private JavaProxyClass generate(ClassDefiningClassLoader loader, String targetCl cw.visitEnd(); Class clazz = loader.defineClass(selfType.getClassName(), cw.toByteArray()); + try + { + Files.write(Paths.get("/tmp/dumped", selfType.getClassName() + ".class"), cw.toByteArray()); + } + catch (IOException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + // trigger class initialization for the class try { @@ -231,7 +273,7 @@ private static String targetClassName(final Class clazz) { .append("$Proxy").append(nextId()).toString(); } - private static ClassWriter beginProxyClass(final String className, + private static ClassWriter beginProxyClass(Type selfType, final String className, final Class superClass, final Class[] interfaces, final ClassDefiningClassLoader loader) { ClassWriter cw = ASM.newClassWriter(loader.asClassLoader()); @@ -248,11 +290,37 @@ private static ClassWriter beginProxyClass(final String className, INVOCATION_HANDLER_TYPE.getDescriptor(), null, null ).visitEnd(); + // public static final ClassInvocationHolder __handler_builder; + cw.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, + INVOCATION_HANDLER_BUILDER_FIELD_NAME, + INVOCATION_HANDLER_BUILDER_TYPE.getDescriptor(), null, null + ).visitEnd(); + // /* public */ static final JavaProxyClass __proxy_class; cw.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, PROXY_CLASS_FIELD_NAME, PROXY_CLASS_TYPE.getDescriptor(), null, null ).visitEnd(); + + + //make trampoline + // NOTE: not for 0-arg ctor +// GeneratorAdapter ga2 = new GeneratorAdapter(Opcodes.ACC_PUBLIC, trampolineInit, null, null, cw); +// +// +// ga2.getStatic(selfType, PROXY_CLASS_FIELD_NAME, PROXY_CLASS_TYPE); +// ga2.loadThis(); +// ga2.loadArgArray(); +// ga2.invokeVirtual(INVOCATION_HANDLER_BUILDER_TYPE, initHandlerClass); +// +// +// ga2.loadThis(); +// ga2.loadArgs(); + + /// ???? + +// ga2.returnValue(); +// ga2.endMethod(); return cw; } @@ -321,14 +389,21 @@ private static void generateConstructors(Class superClass, Type selfType, ClassV // otherwise, define everything and let some of them fail at invocation generateConstructor(selfType, cons[i], cw); + if (cons[i].getParameterCount() == 0) // FIXME: zero only, but does somewhat work for 1+ too + generateRawConstructor(selfType, cons[i], cw); } } - private static GeneratorAdapter createClassInitializer(Type selfType, ClassVisitor cw) { + private static GeneratorAdapter createClassInitializer(Type selfType, ClassVisitor cw, int id) { GeneratorAdapter clazzInit = new GeneratorAdapter(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, new org.objectweb.asm.commons.Method("", Type.VOID_TYPE, EMPTY_TYPE_ARRAY), null, EMPTY_TYPE_ARRAY, cw); + clazzInit.visitLdcInsn(id); + clazzInit.invokeStatic(INTERNAL_PROXY_HELPER_TYPE, getDefaultJPIH); + clazzInit.putStatic(selfType, INVOCATION_HANDLER_BUILDER_FIELD_NAME, INVOCATION_HANDLER_BUILDER_TYPE); + // __handler_builder = JavaProxyClassFactory.getDefault(id) + clazzInit.visitLdcInsn(selfType.getClassName()); clazzInit.invokeStatic(JAVA_LANG_CLASS_TYPE, forName); clazzInit.invokeStatic(INTERNAL_PROXY_HELPER_TYPE, initProxyClass); @@ -482,20 +557,87 @@ private static Class[] generateConstructor(Type selfType, Constructor constructo if ( superConstructorVarArgs ) mv.visitAnnotation(Type.getDescriptor(VarArgs.class), true); GeneratorAdapter ga = new GeneratorAdapter(access, m, mv); + + ga.loadThis(); + ga.loadArg(superConstructorParameterTypes.length); + + ga.putField(selfType, INVOCATION_HANDLER_FIELD_NAME, INVOCATION_HANDLER_TYPE); + ga.loadThis(); ga.loadArgs(0, superConstructorParameterTypes.length); ga.invokeConstructor(toType(constructor.getDeclaringClass()), super_m); + // do a void return + ga.returnValue(); + ga.endMethod(); + + return newConstructorParameterTypes; + } + //TODO: dedupe code? + private static Class[] generateRawConstructor(Type selfType, Constructor constructor, ClassVisitor cw) { + Class[] superConstructorParameterTypes = constructor.getParameterTypes(); + + int access = Opcodes.ACC_PUBLIC; + String name1 = ""; + 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, toTypes(superConstructorParameterTypes)); + org.objectweb.asm.commons.Method m = new org.objectweb.asm.commons.Method( + name1, Type.VOID_TYPE, toTypes(superConstructorParameterTypes)); + + 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 constructors : + if ( superConstructorVarArgs ) mv.visitAnnotation(Type.getDescriptor(VarArgs.class), true); + GeneratorAdapter ga = new GeneratorAdapter(access, m, mv); + + ga.loadThis(); - ga.loadArg(superConstructorParameterTypes.length); + ga.getStatic(selfType, INVOCATION_HANDLER_BUILDER_FIELD_NAME, INVOCATION_HANDLER_BUILDER_TYPE); + ga.getStatic(selfType, PROXY_CLASS_FIELD_NAME, PROXY_CLASS_TYPE); + ga.loadThis(); + ga.loadArgArray();// NOTE: FOR non-zero args + ga.invokeVirtual(INVOCATION_HANDLER_BUILDER_TYPE, initHandlerClass); ga.putField(selfType, INVOCATION_HANDLER_FIELD_NAME, INVOCATION_HANDLER_TYPE); + + + ga.loadThis(); + ga.loadArgs(0, superConstructorParameterTypes.length); + ga.invokeConstructor(toType(constructor.getDeclaringClass()), super_m); // do a void return ga.returnValue(); ga.endMethod(); - return newConstructorParameterTypes; + return superConstructorParameterTypes; + } + + public static final class ClassInvocationHolder { + + private final Ruby runtime; + private final RubyClass clazz; + + ClassInvocationHolder(final Ruby runtime, final RubyClass self) { + this.runtime = runtime; this.clazz = self; + } + + public JavaProxyInvocationHandler init(JavaProxyClass jpc, Object jself, Object[] args) throws Throwable { + final IRubyObject self = clazz.allocate();// NOTE: this has to be done somewhere else in the code/I'm missing lots? + IRubyObject cjp = JavaObject.wrap(runtime, jself); + JavaUtilities.set_java_object(self, self, cjp); + //JavaProxyClass + self.getMetaClass().setInstanceVariable("@java_proxy_class", jpc); + //if ( ((JavaProxy) proxy).object == null ) + final JavaProxyInvocationHandler jpih = new MethodInvocationHandler(runtime, self);//FIXME: needs to be constant and sync with Java$JCreate + jpih.invoke_ctor(args); + return jpih; + } + + } static boolean isVarArgs(final Constructor ctor) { diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java index 986ede66692..d073a019d9d 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java @@ -63,6 +63,7 @@ public class JavaProxyConstructor extends JavaProxyReflectionObject implements P private final Constructor proxyConstructor; private final Class[] actualParameterTypes; private final boolean actualVarArgs; + private final boolean exportable; //exportable to java, or does it have the jruby JavaProxyInvocationHandler class at the end? private final JavaProxyClass declaringProxyClass; @@ -83,8 +84,9 @@ public static RubyClass createJavaProxyConstructorClass(Ruby runtime, RubyModule this.declaringProxyClass = proxyClass; this.proxyConstructor = constructor; Class[] parameterTypes = constructor.getParameterTypes(); + this.exportable = parameterTypes.length == 0;// || parameterTypes[parameterTypes.length - 1] != JavaProxyInvocationHandler.class; // see JavaProxyClassFactory's generateConstructor ... - this.actualParameterTypes = ArraySupport.newCopy(parameterTypes, parameterTypes.length - 1); + this.actualParameterTypes = ArraySupport.newCopy(parameterTypes, parameterTypes.length - (exportable?0:1)); this.actualVarArgs = JavaProxyClassFactory.isVarArgs(proxyConstructor); } @@ -95,6 +97,10 @@ public final Class[] getParameterTypes() { public final Class[] getExceptionTypes() { return proxyConstructor.getExceptionTypes(); } + + public final boolean isExportable() { + return exportable;//TODO: just this way to test + } public final boolean isVarArgs() { return actualVarArgs; } @@ -110,12 +116,25 @@ public final Object newInstance(Object[] args, JavaProxyInvocationHandler handle if ( len != actualParameterTypes.length ) { throw new IllegalArgumentException("wrong number of parameters"); } - return newInstanceImpl(ArraySupport.newCopy(args, len + 1), handler); // does args[ len ] = handler; + if (exportable) + return newInstanceImpl(args, null); + else + return newInstanceImpl(ArraySupport.newCopy(args, len + 1), handler); // does args[ len ] = handler; } - + /** + * For exportable objects, argsPlus1 is not plus one + * @param argsPlus1 + * @param handler + * @return + * @throws IllegalArgumentException + * @throws InstantiationException + * @throws IllegalAccessException + * @throws InvocationTargetException + */ final Object newInstanceImpl(Object[] argsPlus1, JavaProxyInvocationHandler handler) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { - argsPlus1[ argsPlus1.length - 1 ] = handler; + if (!exportable) + argsPlus1[ argsPlus1.length - 1 ] = handler; return proxyConstructor.newInstance(argsPlus1); } @@ -188,7 +207,7 @@ public JavaObject newInstance(final IRubyObject self, Object[] args) throws Rais public final JavaObject newInstance(final IRubyObject self, IRubyObject[] args) throws RaiseException { final Ruby runtime = getRuntime(); - final Object[] javaArgsPlus1 = RubyToJavaInvoker.convertArguments(this, args, +1); + final Object[] javaArgsPlus1 = RubyToJavaInvoker.convertArguments(this, args, (exportable?0:+1)); JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); try { return JavaObject.wrap(runtime, newInstanceImpl(javaArgsPlus1, handler)); @@ -199,7 +218,7 @@ public final JavaObject newInstance(final IRubyObject self, IRubyObject[] args) public final JavaObject newInstance(final IRubyObject self, IRubyObject arg0) throws RaiseException { final Ruby runtime = getRuntime(); - final Object[] javaArgsPlus1 = RubyToJavaInvoker.convertArguments(this, arg0, +1); + final Object[] javaArgsPlus1 = RubyToJavaInvoker.convertArguments(this, arg0, (exportable?0:+1)); JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); try { return JavaObject.wrap(runtime, newInstanceImpl(javaArgsPlus1, handler)); @@ -218,7 +237,7 @@ private static RaiseException mapInstantiationException(final Ruby runtime, fina throw ex; } - private static final class MethodInvocationHandler implements JavaProxyInvocationHandler { + static final class MethodInvocationHandler implements JavaProxyInvocationHandler { private final Ruby runtime; private final IRubyObject self; @@ -240,6 +259,13 @@ public Object invoke(Object proxy, JavaProxyMethod proxyMethod, Object[] nargs) final Class returnType = proxyMethod.getReturnType(); return returnType == void.class ? null : result.toJava( returnType ); } + + public void invoke_ctor(Object[] args) throws Throwable { + final RubyClass metaClass = self.getMetaClass(); + final DynamicMethod method = metaClass.searchMethod("initialize");// is this the right method? any optimization? + // TODO: sketchy... might call super! + invokeRuby(method, null, metaClass, "initialize", args);// TODO: only if last arg isn't ruby + } private IRubyObject invokeRuby(final DynamicMethod method, final JavaProxyMethod proxyMethod, final RubyClass metaClass, final String name, final Object[] nargs) { @@ -254,7 +280,7 @@ private IRubyObject invokeRuby(final DynamicMethod method, final JavaProxyMethod final ThreadContext context = runtime.getCurrentContext(); return method.call(context, self, metaClass, name, newArgs); } - if ( proxyMethod.hasSuperImplementation() ) { + if ( proxyMethod != null && proxyMethod.hasSuperImplementation() ) { final ThreadContext context = runtime.getCurrentContext(); final RubyClass superClass = metaClass.getSuperClass(); return Helpers.invokeAs(context, superClass, self, name, newArgs, Block.NULL_BLOCK); @@ -315,6 +341,12 @@ public Object invoke(Object proxy, JavaProxyMethod method, Object[] nargs) throw return procResult.toJava( method.getReturnType() ); } + @Override + public void invoke_ctor(Object[] args) throws Throwable + { + throw new UnsupportedOperationException(); + } + } private Object[] convertArguments(final RubyArray arguments) { diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyInvocationHandler.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyInvocationHandler.java index a6b80b2acaa..3b2e2aab316 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyInvocationHandler.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyInvocationHandler.java @@ -52,4 +52,12 @@ public interface JavaProxyInvocationHandler { * @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[]) */ Object invoke(Object receiver, JavaProxyMethod method, Object[] args) throws Throwable; + + + /** + * Calls the initialize method + * @param args + * @throws Throwable + */ + void invoke_ctor(Object[] args) throws Throwable; } \ No newline at end of file From fa00023989c7bcc62388372cb779526cf2226c9f Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Thu, 22 Oct 2020 02:10:50 -0400 Subject: [PATCH 02/49] WIP: Moving Concrete extension code into Reification code to add ctor support more easily --- core/src/main/java/org/jruby/RubyClass.java | 369 ++++++++++++++++-- .../main/java/org/jruby/javasupport/Java.java | 1 + .../proxy/JavaProxyClassFactory.java | 24 +- .../util/JavaClassConfiguration.java | 11 + 4 files changed, 362 insertions(+), 43 deletions(-) create mode 100644 core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index 09e2cc423ac..fb9b066903f 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -32,6 +32,8 @@ package org.jruby; import org.jruby.javasupport.JavaClass; +import org.jruby.javasupport.ext.JavaExtensions; +import org.jruby.javasupport.util.JavaClassConfiguration; import org.jruby.parser.StaticScope; import org.jruby.runtime.Arity; import org.jruby.runtime.JavaSites; @@ -53,6 +55,7 @@ import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -63,6 +66,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import org.jruby.anno.JRubyClass; @@ -72,6 +76,7 @@ import org.jruby.internal.runtime.methods.DynamicMethod; import org.jruby.java.codegen.RealClassGenerator; import org.jruby.java.codegen.Reified; +import org.jruby.java.proxies.ConcreteJavaProxy; import org.jruby.javasupport.Java; import org.jruby.runtime.Helpers; import org.jruby.runtime.Block; @@ -81,6 +86,7 @@ import org.jruby.runtime.MethodIndex; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ObjectMarshal; +import org.jruby.runtime.PositionAware; import org.jruby.runtime.ThreadContext; import static org.jruby.runtime.Visibility.*; import org.jruby.runtime.builtin.IRubyObject; @@ -97,6 +103,7 @@ import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Label; /** * @@ -201,6 +208,7 @@ public IRubyObject allocate(Ruby runtime, RubyClass klazz) { * @param clazz The class from which to grab a standard Ruby __allocate__ method. * * @note Used with `jrubyc --java` generated (interoperability) class files. + * @note Used with new concrete extension. */ public void setRubyStaticAllocator(final Class clazz) { try { @@ -211,6 +219,7 @@ public IRubyObject allocate(Ruby runtime, RubyClass klazz) { try { return (IRubyObject) method.invoke(null, runtime, klazz); } catch (InvocationTargetException ite) { + ite.printStackTrace(); throw runtime.newTypeError("could not allocate " + clazz + " with (Ruby, RubyClass) constructor:\n" + ite); } catch (IllegalAccessException iae) { throw runtime.newSecurityError("could not allocate " + clazz + " due to inaccessible (Ruby, RubyClass) constructor:\n" + iae); @@ -1253,7 +1262,7 @@ public Object unmarshalFrom(Ruby runtime, RubyClass type, * * @return true if the class can be reified, false otherwise */ - public boolean isReifiable() { + public boolean isReifiable(boolean[] java) { // already reified is not reifiable if (reifiedClass != null) return false; @@ -1267,12 +1276,20 @@ public boolean isReifiable() { if (reifiedSuper != null) { // super must be Object, BasicObject, or a reified user class - return reifiedSuper == RubyObject.class || + boolean result = reifiedSuper == RubyObject.class || reifiedSuper == RubyBasicObject.class || Reified.class.isAssignableFrom(reifiedSuper); + + if (result) + return true; + else + { + java[0] = true; + return true; + } } else { // non-native, non-reified super; recurse - return realSuper.isReifiable(); + return realSuper.isReifiable(java); } } @@ -1298,8 +1315,11 @@ public void reifyWithAncestors(boolean useChildLoader) { * @param useChildLoader whether to load the class into its own child classloader */ public void reifyWithAncestors(String classDumpDir, boolean useChildLoader) { - if (isReifiable()) { + boolean[] box = {false}; + if (isReifiable(box)) { RubyClass realSuper = getSuperClass().getRealClass(); + + //TODO; test nested java extension if (realSuper.reifiedClass == null) realSuper.reifyWithAncestors(classDumpDir, useChildLoader); reify(classDumpDir, useChildLoader); @@ -1323,8 +1343,10 @@ public final void reify(boolean useChildLoader) { * @param classDumpDir Directory to save reified java class */ public synchronized void reify(String classDumpDir, boolean useChildLoader) { + boolean[] java_box = {false}; // re-check reifiable in case another reify call has jumped in ahead of us - if (!isReifiable()) return; + if (!isReifiable(java_box)) return; + final boolean concreteExt = java_box[0]; // calculate an appropriate name, for anonymous using inspect like format e.g. "Class:0x628fad4a" final String name = getBaseName() != null ? getName() : @@ -1340,8 +1362,14 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) { Class reifiedParent = RubyObject.class; if (superClass.reifiedClass != null) reifiedParent = superClass.reifiedClass; + + Reificator reifier; + if (concreteExt) + reifier = new ConcreteJavaReifier(reifiedParent, javaName, javaPath); + else + reifier = new MethodReificator(reifiedParent, javaName, javaPath, null, javaPath); - final byte[] classBytes = new MethodReificator(reifiedParent, javaName, javaPath).reify(); + final byte[] classBytes = reifier.reify(); final ClassDefiningClassLoader parentCL; if (parentReified.getClassLoader() instanceof OneShotClassLoader) { @@ -1362,13 +1390,22 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) { java.lang.reflect.Method clinit = result.getDeclaredMethod("clinit", Ruby.class, RubyClass.class); clinit.invoke(null, runtime, this); - setClassAllocator(result); + if (concreteExt) + { + setRubyStaticAllocator(result); + + // update java_class + this.setInstanceVariable("@java_class", Java.wrapJavaObject(runtime, result)); + } + else + setRubyClassAllocator(result); reifiedClass = result; return; // success } catch (LinkageError error) { // fall through to failure path final String msg = error.getMessage(); + error.printStackTrace(); if ( msg != null && msg.contains("duplicate class definition for name") ) { logReifyException(error, false); } @@ -1398,15 +1435,23 @@ private abstract class BaseReificator implements Reificator { protected final Class reifiedParent; protected final String javaName; protected final String javaPath; + protected final String rubyName; + protected final String rubyPath; + protected final JavaClassConfiguration jcc; protected final ClassWriter cw; - BaseReificator(Class reifiedParent, String javaName, String javaPath) { + BaseReificator(Class reifiedParent, String javaName, String javaPath, String rubyName, String rubyPath) { this.reifiedParent = reifiedParent; this.javaName = javaName; this.javaPath = javaPath; + this.rubyName = rubyName; + this.rubyPath = rubyPath; + jcc = new JavaClassConfiguration(); + jcc.callInitialize = true; cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, javaPath, null, p(reifiedParent), interfaces()); + cw.visitSource("Reifier.gen", null);//TODO: name of RB? } @Override @@ -1415,6 +1460,7 @@ public byte[] reify() { // fields to hold Ruby and RubyClass references cw.visitField(ACC_STATIC | ACC_PRIVATE, "ruby", ci(Ruby.class), null, null); cw.visitField(ACC_STATIC | ACC_PRIVATE, "rubyClass", ci(RubyClass.class), null, null); + if (!isRubyObject()) cw.visitField(ACC_PRIVATE, "rubyObject", rubyName, null, null); // static initializing method SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, "clinit", sig(void.class, Ruby.class, RubyClass.class), null, null); @@ -1425,25 +1471,8 @@ public byte[] reify() { m.putstatic(javaPath, "rubyClass", ci(RubyClass.class)); m.voidreturn(); m.end(); - - // standard constructor that accepts Ruby, RubyClass - m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class, Ruby.class, RubyClass.class), null, null); - m.aload(0); - m.aload(1); - m.aload(2); - m.invokespecial(p(reifiedParent), "", sig(void.class, Ruby.class, RubyClass.class)); - m.voidreturn(); - m.end(); - - // no-arg constructor using static references to Ruby and RubyClass - m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", CodegenUtils.sig(void.class), null, null); - m.aload(0); - m.getstatic(javaPath, "ruby", ci(Ruby.class)); - m.getstatic(javaPath, "rubyClass", ci(RubyClass.class)); - m.invokespecial(p(reifiedParent), "", sig(void.class, Ruby.class, RubyClass.class)); - m.voidreturn(); - m.end(); - + + reifyConstructors(); customReify(); cw.visitEnd(); @@ -1464,12 +1493,71 @@ private String[] interfaces() { } return interfaceNames; } + + protected boolean isRubyObject() + { + return true; + } + + /** + * Loads self (if local) or the rubyObject (if a java proxy) cast to + * a RubyBasicObject, as everything is a RBO and it has a nicer interface + */ + protected void loadRubyObject(SkinnyMethodAdapter m) + { + if (isRubyObject()) + m.aload(0); // self + else + { + m.aload(0); // self + m.getfield(javaPath, "rubyObject", rubyName); // rubyObject + } + } + + + protected void rubycall(SkinnyMethodAdapter m, String signature) + { + m.invokevirtual(rubyPath, "callMethod", signature); + } + + protected void reifyConstructors() + { + // standard constructor that accepts Ruby, RubyClass. For use by JRuby (internally) + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class, Ruby.class, RubyClass.class), null, null); + m.aload(0); // uninitialized this + m.aload(1); // ruby + m.aload(2); // rubyclass + allocAndInitialize(m, false); + + if (jcc.javaConstructable) + { + // no-arg constructor using static references to Ruby and RubyClass. For use by java + m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", CodegenUtils.sig(void.class), null, null); + m.aload(0); // uninitialized this + m.getstatic(javaPath, "ruby", ci(Ruby.class)); + m.getstatic(javaPath, "rubyClass", ci(RubyClass.class)); + allocAndInitialize(m, true); + } + } + + protected void allocAndInitialize(SkinnyMethodAdapter m, boolean initIfAllowed) + { + m.invokespecial(p(reifiedParent), "", sig(void.class, Ruby.class, RubyClass.class)); + if (jcc.callInitialize && initIfAllowed) // if we want to initialize + { + m.aload(0); // initialized this + m.ldc("initialize"); // TODO: consts? + rubycall(m, sig(IRubyObject.class, String.class) ); + } + m.voidreturn(); + m.end(); + } } private class MethodReificator extends BaseReificator { - MethodReificator(Class reifiedParent, String javaName, String javaPath) { - super(reifiedParent, javaName, javaPath); + MethodReificator(Class reifiedParent, String javaName, String javaPath, String rubyName, String rubyPath) { + super(reifiedParent, javaName, javaPath, rubyName, rubyPath); } @Override @@ -1558,7 +1646,7 @@ private void defineClassMethods(Set instanceMethods) { m.getstatic(javaPath, "rubyClass", ci(RubyClass.class)); m.ldc(id); - m.aload(0); + m.aload(0); //TODO: is this supposed to be? loadRubyObject(m); // self/rubyObject m.invokevirtual("org/jruby/RubyClass", "callMethod", sig(IRubyObject.class, String.class, IRubyObject[].class) ); } m.areturn(); @@ -1598,12 +1686,34 @@ private void defineInstanceMethods(Set instanceMethods) { SkinnyMethodAdapter m; for (Map.Entry methodEntry : getMethods().entrySet()) { final String id = methodEntry.getKey(); + if (id.equals("initialize")) + continue; String javaMethodName = JavaNameMangler.mangleMethodName(id); + //MethodData md = methodEntry.getValue().getMethodData(); + PositionAware position = methodEntry.getValue() instanceof PositionAware ? (PositionAware) methodEntry.getValue() : new PositionAware() + { + + @Override + public int getLine() + { + return 0; // TODO: condense + } + + @Override + public String getFile() + { + return ""; + } + }; + cw.visitSource(position.getFile(), null);//TODO: ctors, etc Map> methodAnnos = getMethodAnnotations().get(id); List>> parameterAnnos = getParameterAnnotations().get(id); Class[] methodSignature = getMethodSignatures().get(id); + + if (methodSignature == null) + methodSignature = searchInheritedSignatures(id); final String signature; if (methodSignature == null) { // non-signature signature with just IRubyObject @@ -1612,21 +1722,25 @@ private void defineInstanceMethods(Set instanceMethods) { case 0: signature = sig(IRubyObject.class); // return IRubyObject foo() m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); + m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); + generateObjectBarrier(m, false, false); - m.aload(0); + loadRubyObject(m); // self/rubyObject m.ldc(id); - m.invokevirtual(javaPath, "callMethod", sig(IRubyObject.class, String.class)); + rubycall(m, sig(IRubyObject.class, String.class)); break; case 1: signature = sig(IRubyObject.class, IRubyObject.class); // return IRubyObject foo(IRubyObject arg1) m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); + m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); + generateObjectBarrier(m, false, false); - m.aload(0); + loadRubyObject(m); // self/rubyObject m.ldc(id); m.aload(1); // IRubyObject arg1 - m.invokevirtual(javaPath, "callMethod", sig(IRubyObject.class, String.class, IRubyObject.class)); + rubycall(m, sig(IRubyObject.class, String.class, IRubyObject.class)); break; // currently we only have : // callMethod(context, name) @@ -1639,9 +1753,11 @@ private void defineInstanceMethods(Set instanceMethods) { Class[] params = new Class[paramCount]; Arrays.fill(params, IRubyObject.class); signature = sig(IRubyObject.class, params); m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); + m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); + generateObjectBarrier(m, false, false); - m.aload(0); + loadRubyObject(m); // self/rubyObject m.ldc(id); // generate an IRubyObject[] for the method arguments : @@ -1658,13 +1774,15 @@ private void defineInstanceMethods(Set instanceMethods) { // NOTE: maybe improve to match fixed part for < -1 e.g. (IRubObject, IRubyObject, IRubyObject...) signature = sig(IRubyObject.class, IRubyObject[].class); m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS, javaMethodName, signature, null, null); + m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); + generateObjectBarrier(m, false, false); - m.aload(0); + loadRubyObject(m); // self/rubyObject m.ldc(id); m.aload(1); // IRubyObject[] arg1 } - m.invokevirtual(javaPath, "callMethod", sig(IRubyObject.class, String.class, IRubyObject[].class)); + rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class)); } m.areturn(); } @@ -1680,16 +1798,18 @@ private void defineInstanceMethods(Set instanceMethods) { int mod = ACC_PUBLIC; if ( isVarArgsSignature(id, methodSignature) ) mod |= ACC_VARARGS; m = new SkinnyMethodAdapter(cw, mod, javaMethodName, signature, null, null); + m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); + generateObjectBarrier(m, false, false); - m.getstatic(javaPath, "ruby", ci(Ruby.class)); + m.getstatic(javaPath, "ruby", ci(Ruby.class)); // runtime m.astore(rubyIndex); - m.aload(0); // self + loadRubyObject(m); // self/rubyObject m.ldc(id); // method name - RealClassGenerator.coerceArgumentsToRuby(m, params, rubyIndex); - m.invokevirtual(javaPath, "callMethod", sig(IRubyObject.class, String.class, IRubyObject[].class)); + RealClassGenerator.coerceArgumentsToRuby(m, params, rubyIndex); + rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class)); RealClassGenerator.coerceResultAndReturn(m, methodSignature[0]); } @@ -1701,6 +1821,172 @@ private void defineInstanceMethods(Set instanceMethods) { } } + protected Class[] searchInheritedSignatures(String id) + { + return null; + } + + protected void generateObjectBarrier(SkinnyMethodAdapter m, boolean ctorarg, boolean init) + { + // For non-concrete things, we ignore, as this is a RubyObject + } + + } // class MethodReificator + + private class ConcreteJavaReifier extends MethodReificator + { + + ConcreteJavaReifier(Class reifiedParent, String javaName, String javaPath) + { + // In theory, we should operate on IRubyObject, but everything + // that we need is a RubyBasicObject, and it has a nicer interface to boot + super(reifiedParent, javaName, javaPath, ci(RubyBasicObject.class), p(RubyBasicObject.class)); + } + + @Override + public void customReify() + { + super.customReify(); + + defineAllocator(); + } + + @Override + protected boolean isRubyObject() + { + return false; + } + + /* protected void rubycall(SkinnyMethodAdapter m, String signature) + { + m.invokeinterface(rubyPath, "callMethod", signature); + }*/ + + protected Class[] searchInheritedSignatures(String id) + { + + for (Method method : reifiedParent.getDeclaredMethods()) + { + //TODO: java <-> ruby conversion? + if (!method.getName().equals(id)) continue; + final int mod = method.getModifiers(); + if ( !Modifier.isPublic(mod) && !Modifier.isProtected(mod) ) continue; + + // found! built a signature to return + int params = method.getParameterCount(); + Class[] sig = new Class[params + 1]; + ArraySupport.copy(method.getParameterTypes(), sig, 1, params); + sig[0] = method.getReturnType(); + return sig; + } + return null; + } + + @Override + protected void reifyConstructors() + { + Optional> zeroArg = Optional.empty(); + List> candidates = new ArrayList<>(); + for (Constructor constructor : reifiedParent.getDeclaredConstructors()) + { + final int mod = constructor.getModifiers(); + if ( !Modifier.isPublic(mod) && !Modifier.isProtected(mod) ) continue; + candidates.add(constructor); + if (constructor.getParameterCount() == 0) // TODO: varargs? + { + zeroArg = Optional.of(constructor); + } + } + // TODO: config multi-arg + + + if (zeroArg.isPresent()) + { + // standard constructor that accepts Ruby, RubyClass. For use by JRuby (internally) + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class, Ruby.class, RubyClass.class), null, null); + m.aload(0); // uninitialized this + m.invokespecial(p(reifiedParent), "", sig(void.class)); + + generateObjectBarrier(m, true, false); + + m.voidreturn(); + m.end(); + + if (jcc.javaConstructable) + { + // no-arg constructor using static references to Ruby and RubyClass. For use by java + m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", CodegenUtils.sig(void.class), null, null); + m.aload(0); // uninitialized this + m.invokespecial(p(reifiedParent), "", sig(void.class)); + + generateObjectBarrier(m, false, true); + + m.voidreturn(); + m.end(); + } + } + } + + + /** + * Generates an init barrier. NOT Thread-safe, but hopefully nobody has threads in their constructor? + * + * @param m + * @param ctorArgInitialize If this is a ctor, has args not in locals, and if we should thus call initialize() + */ + protected void generateObjectBarrier(SkinnyMethodAdapter m, boolean ctorArg, boolean initialize) + { + // For non-concrete things, we check, as this is not a RubyObject + Label end = new Label(); + m.aload(0); + m.getfield(javaPath, "rubyObject", rubyName); + m.ifnonnull(end); //TODO: no nulls for ctors? + { + m.newobj(p(ConcreteJavaProxy.class)); + m.dup(); // rubyobject + if (ctorArg) + { + m.aload(1); // ruby + m.aload(2); // rubyclass + } + else + { + m.getstatic(javaPath, "ruby", ci(Ruby.class)); + m.getstatic(javaPath, "rubyClass", ci(RubyClass.class)); + } + + m.aload(0); // initialized this + m.invokespecial(p(ConcreteJavaProxy.class), "", sig(void.class, Ruby.class, RubyClass.class, Object.class)); + m.dup(); // rubyobject + m.aload(0); // this + m.swap(); + + m.putfield(javaPath, "rubyObject", rubyName); + if (jcc.callInitialize && initialize) + { + m.ldc("initialize"); // TODO: consts? + rubycall(m, sig(IRubyObject.class, String.class) ); + m.pop(); + } + else + m.pop(); + } + m.label(end); + } + + private void defineAllocator() + { + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, "__allocate__", sig(IRubyObject.class, Ruby.class, RubyClass.class), null, null); + m.newobj(javaPath); + m.dup(); + m.aload(0); // ruby + m.aload(1); // rubyclass + m.invokespecial(javaPath, "", sig(void.class, Ruby.class, RubyClass.class)); + m.getfield(javaPath, "rubyObject", rubyName); + m.areturn(); + m.end(); + } + } // class MethodReificator private boolean isVarArgsSignature(final String method, final Class[] methodSignature) { @@ -1721,6 +2007,7 @@ public void setReifiedClass(Class reifiedClass) { this.reifiedClass = reifiedClass; } + //FIXME: no longer IRubyObject anymore public Class getReifiedClass() { return reifiedClass; } diff --git a/core/src/main/java/org/jruby/javasupport/Java.java b/core/src/main/java/org/jruby/javasupport/Java.java index 0d27a1716bf..2390e246774 100644 --- a/core/src/main/java/org/jruby/javasupport/Java.java +++ b/core/src/main/java/org/jruby/javasupport/Java.java @@ -1504,6 +1504,7 @@ private Class[] getParameterTypes(final Method method) { } + //TODO: what is this doing? @SuppressWarnings("unchecked") public static Class generateRealClass(final RubyClass clazz) { final Ruby runtime = clazz.getRuntime(); diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java index 114639f9bd3..0cbc20d5687 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java @@ -48,6 +48,7 @@ import org.jruby.RubyClass; import org.jruby.exceptions.RaiseException; import org.jruby.internal.runtime.methods.DynamicMethod; +import org.jruby.java.proxies.ConcreteJavaProxy; import org.jruby.java.proxies.JavaProxy; import org.jruby.javasupport.Java; import org.jruby.javasupport.JavaObject; @@ -127,9 +128,9 @@ public class JavaProxyClassFactory { private static final org.objectweb.asm.commons.Method initHandlerClass = org.objectweb.asm.commons.Method .getMethod(JavaProxyInvocationHandler.class.getName() + " init("+JavaProxyClass.class.getName() + ",java.lang.Object,java.lang.Object[])"); - + /* private static final org.objectweb.asm.commons.Method trampolineInit = org.objectweb.asm.commons.Method - .getMethod(JavaProxyInvocationHandler.class.getName() + "__jruby$trampoline$init(java.lang.Object[])"); + .getMethod(JavaProxyInvocationHandler.class.getName() + "__jruby$trampoline$init(java.lang.Object[])");*/ private static final Type JAVA_PROXY_TYPE = Type.getType(InternalJavaProxy.class); @@ -199,6 +200,9 @@ private JavaProxyClass generate(ClassDefiningClassLoader loader, String targetCl Map methods, Type selfType, RubyClass rclass, int id) { ClassWriter cw = beginProxyClass(selfType, targetClassName, superClass, interfaces, loader); + if (1 < 2) + throw new RuntimeException("Ack, old code being hit!"); + Map>> fieldannos; Map fields; if (rclass != null) { @@ -638,6 +642,22 @@ public JavaProxyInvocationHandler init(JavaProxyClass jpc, Object jself, Object[ } + public static IRubyObject sinit(Ruby ruby, RubyClass clazz, Object jself, Object[] args) throws Throwable { + final IRubyObject self = new ConcreteJavaProxy(ruby, clazz, jself); + //clazz.allocate();// NOTE: this has to be done somewhere else in the code/I'm missing lots? + /* + IRubyObject cjp = JavaObject.wrap(runtime, jself); + JavaUtilities.set_java_object(self, self, cjp);*/ + //JavaProxyClass + // self.getMetaClass().setInstanceVariable("@java_proxy_class", jpc); + //if ( ((JavaProxy) proxy).object == null ) + //final JavaProxyInvocationHandler jpih = new MethodInvocationHandler(runtime, self);//FIXME: needs to be constant and sync with Java$JCreate + // jpih.invoke_ctor(args); + //return jpih; + //TODO: initialize? + return self; + } + } static boolean isVarArgs(final Constructor ctor) { diff --git a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java new file mode 100644 index 00000000000..245b37bef7a --- /dev/null +++ b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java @@ -0,0 +1,11 @@ +package org.jruby.javasupport.util; + +public class JavaClassConfiguration +{ + public boolean callInitialize = false; + public boolean allMethods = true; + public boolean javaConstructable = true; + + // for java proxies + public boolean allCtors = false; +} From 5c04320704f2cf7d7511839983cb1439d03d1f99 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Sat, 24 Oct 2020 03:18:29 -0400 Subject: [PATCH 03/49] More WIP constructor code. Multi-constructors should function, and now configurable from ruby-land --- core/src/main/java/org/jruby/Ruby.java | 8 - core/src/main/java/org/jruby/RubyClass.java | 328 +++++- .../compiler/impl/SkinnyMethodAdapter.java | 11 +- .../org/jruby/ir/targets/indy/Bootstrap.java | 4 +- .../java/codegen/RealClassGenerator.java | 7 + .../jruby/java/proxies/ConcreteJavaProxy.java | 103 +- .../main/java/org/jruby/javasupport/Java.java | 34 +- .../org/jruby/javasupport/JavaMethod.java | 20 +- .../java/org/jruby/javasupport/JavaUtil.java | 12 +- .../proxy/InternalJavaProxyHelper.java | 68 -- .../javasupport/proxy/JavaProxyClass.java | 104 +- .../proxy/JavaProxyClassFactory.java | 1043 +---------------- .../proxy/JavaProxyConstructor.java | 49 +- ...alJavaProxy.java => ReifiedJavaProxy.java} | 18 +- .../util/JavaClassConfiguration.java | 2 + .../main/java/org/jruby/runtime/Helpers.java | 6 +- lib/ruby/stdlib/jruby/core_ext/class.rb | 18 + 17 files changed, 538 insertions(+), 1297 deletions(-) delete mode 100644 core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxyHelper.java rename core/src/main/java/org/jruby/javasupport/proxy/{InternalJavaProxy.java => ReifiedJavaProxy.java} (82%) diff --git a/core/src/main/java/org/jruby/Ruby.java b/core/src/main/java/org/jruby/Ruby.java index 6ef25edeb27..f99a07ffbee 100644 --- a/core/src/main/java/org/jruby/Ruby.java +++ b/core/src/main/java/org/jruby/Ruby.java @@ -2960,14 +2960,6 @@ public Map> getBoundMethods() { return boundMethods; } - public void setJavaProxyClassFactory(JavaProxyClassFactory factory) { - this.javaProxyClassFactory = factory; - } - - public JavaProxyClassFactory getJavaProxyClassFactory() { - return javaProxyClassFactory; - } - private static final EnumSet interest = EnumSet.of( RubyEvent.C_CALL, diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index fb9b066903f..be737ec8edc 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -33,6 +33,8 @@ import org.jruby.javasupport.JavaClass; import org.jruby.javasupport.ext.JavaExtensions; +import org.jruby.javasupport.proxy.JavaProxyClass; +import org.jruby.javasupport.proxy.ReifiedJavaProxy; import org.jruby.javasupport.util.JavaClassConfiguration; import org.jruby.parser.StaticScope; import org.jruby.runtime.Arity; @@ -50,6 +52,7 @@ import static org.objectweb.asm.Opcodes.ACC_STATIC; import static org.objectweb.asm.Opcodes.ACC_SUPER; import static org.objectweb.asm.Opcodes.ACC_VARARGS; +import static org.objectweb.asm.Opcodes.ACC_FINAL; import java.io.IOException; import java.lang.reflect.Constructor; @@ -68,6 +71,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; @@ -104,6 +108,9 @@ import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; import org.objectweb.asm.Label; +import org.objectweb.asm.commons.GeneratorAdapter; + +import com.test.patrick.AbstractClass; /** * @@ -1383,16 +1390,29 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) { } // Attempt to load the name we plan to use; skip reification if it exists already (see #1229). try { + //TODO: Class result = parentCL.defineClass(javaName, classBytes); dumpReifiedClass(classDumpDir, javaPath, classBytes); + //Trigger initilization @SuppressWarnings("unchecked") - java.lang.reflect.Method clinit = result.getDeclaredMethod("clinit", Ruby.class, RubyClass.class); - clinit.invoke(null, runtime, this); + java.lang.reflect.Field rt = result.getDeclaredField("ruby"); + rt.setAccessible(true); + if (rt.get(null) == null) + throw new RuntimeException("No ruby field set!"); if (concreteExt) { - setRubyStaticAllocator(result); + //TODO: check for rubyctor + if (((ConcreteJavaReifier)reifier).simpleAlloc) + { + setRubyStaticAllocator(result); + } + else + { + /**Allocator "set" via clinit @see JavaProxyClass.setProxyClassReified + */ + } // update java_class this.setInstanceVariable("@java_class", Java.wrapJavaObject(runtime, result)); @@ -1414,6 +1434,7 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) { } } catch (Exception ex) { + ex.printStackTrace(); logReifyException(ex, true); } @@ -1429,10 +1450,16 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) { interface Reificator { byte[] reify(); } // interface Reificator + + private abstract class BaseReificator implements Reificator { - protected final Class reifiedParent; + protected static final String RUBY_INIT_ARGS_FIELD = "$rubyInitArgs"; + protected static final String RUBY_OBJECT_FIELD = "$rubyObject"; + protected static final String RUBY_PROXY_CLASS_FIELD = "$rubyProxyClass"; + + protected final Class reifiedParent; protected final String javaName; protected final String javaPath; protected final String rubyName; @@ -1446,47 +1473,53 @@ private abstract class BaseReificator implements Reificator { this.javaPath = javaPath; this.rubyName = rubyName; this.rubyPath = rubyPath; - jcc = new JavaClassConfiguration(); - jcc.callInitialize = true; + jcc = getClassConfig(); + //jcc.callInitialize = true; + //jcc.allCtors = true; + //jcc.allMethods = false; cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, javaPath, null, p(reifiedParent), interfaces()); cw.visitSource("Reifier.gen", null);//TODO: name of RB? - } + } @Override public byte[] reify() { // fields to hold Ruby and RubyClass references - cw.visitField(ACC_STATIC | ACC_PRIVATE, "ruby", ci(Ruby.class), null, null); - cw.visitField(ACC_STATIC | ACC_PRIVATE, "rubyClass", ci(RubyClass.class), null, null); - if (!isRubyObject()) cw.visitField(ACC_PRIVATE, "rubyObject", rubyName, null, null); + cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, "ruby", ci(Ruby.class), null, null); + cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, "rubyClass", ci(RubyClass.class), null, null); + if (!isRubyObject()) cw.visitField(ACC_PRIVATE, RUBY_OBJECT_FIELD, rubyName, null, null); + if (!isRubyObject()) cw.visitField(ACC_PRIVATE, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class), null, null); + if (!isRubyObject()) cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class), null, null); - // static initializing method - SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, "clinit", sig(void.class, Ruby.class, RubyClass.class), null, null); - m.start(); - m.aload(0); - m.putstatic(javaPath, "ruby", ci(Ruby.class)); - m.aload(1); - m.putstatic(javaPath, "rubyClass", ci(RubyClass.class)); - m.voidreturn(); - m.end(); reifyConstructors(); customReify(); + + + // static initializing method, note this is after the constructors to check for alloc-ables (see Concrete Java) + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, "", sig(void.class), null, null); + m.start(); + reifyClinit(m); + m.voidreturn(); + m.end(); cw.visitEnd(); return cw.toByteArray(); } + + public abstract void reifyClinit(SkinnyMethodAdapter m); public abstract void customReify(); private String[] interfaces() { + //TODO: filter interfaces as per old code? final Class[] interfaces = Java.getInterfacesFromRubyClass(RubyClass.this); final String[] interfaceNames = new String[interfaces.length + 1]; // mark this as a Reified class - interfaceNames[0] = p(Reified.class); + interfaceNames[0] = p(isRubyObject() ? Reified.class : ReifiedJavaProxy.class); // add the other user-specified interfaces for (int i = 0; i < interfaces.length; i++) { interfaceNames[i + 1] = p(interfaces[i]); @@ -1510,7 +1543,7 @@ protected void loadRubyObject(SkinnyMethodAdapter m) else { m.aload(0); // self - m.getfield(javaPath, "rubyObject", rubyName); // rubyObject + m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); // rubyObject } } @@ -1552,6 +1585,13 @@ protected void allocAndInitialize(SkinnyMethodAdapter m, boolean initIfAllowed) m.voidreturn(); m.end(); } + + protected Class[] join(Class[] base, Class... extra) + { + Class[] more = ArraySupport.newCopy(base, base.length + extra.length); + ArraySupport.copy(extra, more, base.length, extra.length); + return more; + } } private class MethodReificator extends BaseReificator { @@ -1624,6 +1664,7 @@ private void defineClassMethods(Set instanceMethods) { String signature; if (methodSignature == null) { + if (!jcc.allMethods) continue; final Arity arity = methodEntry.getValue().getArity(); // non-signature signature with just IRubyObject switch (arity.getValue()) { @@ -1682,12 +1723,17 @@ private void defineClassMethods(Set instanceMethods) { } } + //TODO: only generate that are overrideable (javaproxyclass) private void defineInstanceMethods(Set instanceMethods) { SkinnyMethodAdapter m; for (Map.Entry methodEntry : getMethods().entrySet()) { final String id = methodEntry.getKey(); if (id.equals("initialize")) - continue; + { + DynamicMethod dm = methodEntry.getValue(); + System.out.println(dm.getClass().getName()); + System.out.println(dm); + } String javaMethodName = JavaNameMangler.mangleMethodName(id); //MethodData md = methodEntry.getValue().getMethodData(); @@ -1717,6 +1763,7 @@ public String getFile() final String signature; if (methodSignature == null) { // non-signature signature with just IRubyObject + if (!jcc.allMethods) continue; final Arity arity = methodEntry.getValue().getArity(); switch (arity.getValue()) { case 0: @@ -1724,7 +1771,7 @@ public String getFile() m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); - generateObjectBarrier(m, false, false); + generateObjectBarrier(m); loadRubyObject(m); // self/rubyObject m.ldc(id); @@ -1735,7 +1782,7 @@ public String getFile() m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); - generateObjectBarrier(m, false, false); + generateObjectBarrier(m); loadRubyObject(m); // self/rubyObject m.ldc(id); @@ -1755,7 +1802,7 @@ public String getFile() m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); - generateObjectBarrier(m, false, false); + generateObjectBarrier(m); loadRubyObject(m); // self/rubyObject m.ldc(id); @@ -1776,7 +1823,7 @@ public String getFile() m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS, javaMethodName, signature, null, null); m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); - generateObjectBarrier(m, false, false); + generateObjectBarrier(m); loadRubyObject(m); // self/rubyObject m.ldc(id); @@ -1800,7 +1847,7 @@ public String getFile() m = new SkinnyMethodAdapter(cw, mod, javaMethodName, signature, null, null); m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); - generateObjectBarrier(m, false, false); + generateObjectBarrier(m); m.getstatic(javaPath, "ruby", ci(Ruby.class)); // runtime m.astore(rubyIndex); @@ -1820,13 +1867,31 @@ public String getFile() m.end(); } } + + @Override + public void reifyClinit(SkinnyMethodAdapter m) + { /// i0, o[], i1, o[] + // [] i0 [], i1 + + m.ldc(1); // rubyclass index + m.ldc(JavaProxyClass.addStaticInitLookup(runtime, RubyClass.this)); + m.invokestatic(p(JavaProxyClass.class), "getStaticInitLookup", sig(Object[].class, int.class)); + m.dup_x1(); // array + m.ldc(0); // ruby index + m.aaload(); // extract ruby + m.checkcast(p(Ruby.class)); + m.putstatic(javaPath, "ruby", ci(Ruby.class)); + m.aaload(); // extract rubyclass + m.checkcast(p(RubyClass.class)); + m.putstatic(javaPath, "rubyClass", ci(RubyClass.class)); + } protected Class[] searchInheritedSignatures(String id) { return null; } - protected void generateObjectBarrier(SkinnyMethodAdapter m, boolean ctorarg, boolean init) + protected void generateObjectBarrier(SkinnyMethodAdapter m) { // For non-concrete things, we ignore, as this is a RubyObject } @@ -1835,6 +1900,8 @@ protected void generateObjectBarrier(SkinnyMethodAdapter m, boolean ctorarg, boo private class ConcreteJavaReifier extends MethodReificator { + boolean rubyctor = false; + boolean simpleAlloc = false; ConcreteJavaReifier(Class reifiedParent, String javaName, String javaPath) { @@ -1848,7 +1915,10 @@ public void customReify() { super.customReify(); - defineAllocator(); + if (simpleAlloc && rubyctor) + defineAllocator(); + + defineInterfaceMethods(); } @Override @@ -1856,11 +1926,24 @@ protected boolean isRubyObject() { return false; } + + @Override + public void reifyClinit(SkinnyMethodAdapter m) + { + super.reifyClinit(m); + m.getstatic(javaPath, "ruby", ci(Ruby.class)); + m.getstatic(javaPath, "rubyClass", ci(RubyClass.class)); + m.ldc(org.objectweb.asm.Type.getType("L"+javaPath+";")); + if (simpleAlloc) // if simple, don't init, if complex, do init + m.iconst_0(); + else + m.iconst_1(); - /* protected void rubycall(SkinnyMethodAdapter m, String signature) - { - m.invokeinterface(rubyPath, "callMethod", signature); - }*/ + //TODO: eww, where should this call go? here? there? I dislike the leaking of the abstraction across a larger part of the code than necessary + m.invokestatic(p(JavaProxyClass.class), "setProxyClassReified", sig(JavaProxyClass.class, Ruby.class, RubyClass.class, Class.class, boolean.class)); + m.putstatic(javaPath, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class)); + // Note: no end, that's in the parent call + } protected Class[] searchInheritedSignatures(String id) { @@ -1873,11 +1956,7 @@ protected Class[] searchInheritedSignatures(String id) if ( !Modifier.isPublic(mod) && !Modifier.isProtected(mod) ) continue; // found! built a signature to return - int params = method.getParameterCount(); - Class[] sig = new Class[params + 1]; - ArraySupport.copy(method.getParameterTypes(), sig, 1, params); - sig[0] = method.getReturnType(); - return sig; + return join(new Class[]{method.getReturnType()}, method.getParameterTypes()); } return null; } @@ -1897,36 +1976,146 @@ protected void reifyConstructors() zeroArg = Optional.of(constructor); } } - // TODO: config multi-arg - if (zeroArg.isPresent()) { + rubyctor = true; + simpleAlloc = true; // standard constructor that accepts Ruby, RubyClass. For use by JRuby (internally) SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class, Ruby.class, RubyClass.class), null, null); m.aload(0); // uninitialized this + + // set args for init + m.dup(); + m.getstatic(p(IRubyObject.class), "NULL_ARRAY", ci(IRubyObject[].class)); + m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + + // call super m.invokespecial(p(reifiedParent), "", sig(void.class)); - - generateObjectBarrier(m, true, false); + + // call init (if not already called) + generateObjectBarrier(m, 0, false); + + // clear args to avoid holding refs to args + m.aload(0); + m.aconst_null(); + m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); m.voidreturn(); m.end(); + if (jcc.javaConstructable) { // no-arg constructor using static references to Ruby and RubyClass. For use by java - m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", CodegenUtils.sig(void.class), null, null); + m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class), null, null); m.aload(0); // uninitialized this + + // set args for init + m.dup(); + m.getstatic(p(IRubyObject.class), "NULL_ARRAY", ci(IRubyObject[].class)); + m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + + // call super m.invokespecial(p(reifiedParent), "", sig(void.class)); - generateObjectBarrier(m, false, true); + // call init (if not already called) + generateObjectBarrier(m, -1, true); + + // clear args to avoid holding refs to args + m.aload(0); + m.aconst_null(); + m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); m.voidreturn(); m.end(); } } + /*else + { + //TODO: copy jpcf logic for alloc + throw new RuntimeException("Class doesn't have no-arg ctor"); + }*/ + + if (candidates.size() > 0 && jcc.allCtors) //TODO: doc: implies javaConstructable? + { + for (Constructor constructor : candidates) + { + if (zeroArg.isPresent() && constructor == zeroArg.get()) continue; + + if (jcc.rubyConstructable) + { + rubyctor = true; + + //TODO: create empty object before super call??? + // standard constructor that accepts Ruby, RubyClass. For use by JRuby (internally) + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class, join(constructor.getParameterTypes(), Ruby.class, RubyClass.class)), null, null); + m.aload(0); // uninitialized this + + // set args for init + m.dup(); + RealClassGenerator.coerceArgumentsToRuby(m, constructor.getParameterTypes(), RealClassGenerator.calcBaseIndex(constructor.getParameterTypes(), 1)); + m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + + // call super + GeneratorAdapter ga = RealClassGenerator.makeGenerator(m); + ga.loadArgs(0, constructor.getParameterCount()); + m.invokespecial(p(reifiedParent), "", sig(void.class, constructor.getParameterTypes())); + + // call init (if not already called) + generateObjectBarrier(m, constructor.getParameterCount(), true);// NOTE: these ones we DO call init from + + // clear args to avoid holding refs to args + m.aload(0); + m.aconst_null(); + m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + + m.voidreturn(); + m.end(); + } + + if (jcc.javaConstructable) + { + // no-arg constructor using static references to Ruby and RubyClass. For use by java + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class, constructor.getParameterTypes()), null, null); + m.aload(0); + + // set args for init + final int baseIndex = RealClassGenerator.calcBaseIndex(constructor.getParameterTypes(), 1); + final int rubyIndex = baseIndex; + m.getstatic(javaPath, "ruby", ci(Ruby.class)); + m.astore(rubyIndex); // save ruby in local + m.dup(); + RealClassGenerator.coerceArgumentsToRuby(m, constructor.getParameterTypes(), rubyIndex); + m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + + // call super + GeneratorAdapter ga = RealClassGenerator.makeGenerator(m); + ga.loadArgs(0, constructor.getParameterCount()); + m.invokespecial(p(reifiedParent), "", sig(void.class, constructor.getParameterTypes())); + + // call init (if not already called) + generateObjectBarrier(m, -1, true); + + // clear args to avoid holding refs to args + m.aload(0); + m.aconst_null(); + m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + + m.voidreturn(); + m.end(); + } + } + } + } + + /** + * Generates an init barrier. NOT Thread-safe, but hopefully nobody has threads in their constructor? + */ + protected void generateObjectBarrier(SkinnyMethodAdapter m) + { + generateObjectBarrier(m, -1, false); } - /** * Generates an init barrier. NOT Thread-safe, but hopefully nobody has threads in their constructor? @@ -1934,20 +2123,20 @@ protected void reifyConstructors() * @param m * @param ctorArgInitialize If this is a ctor, has args not in locals, and if we should thus call initialize() */ - protected void generateObjectBarrier(SkinnyMethodAdapter m, boolean ctorArg, boolean initialize) + protected void generateObjectBarrier(SkinnyMethodAdapter m, int ctorArg, boolean initialize) { // For non-concrete things, we check, as this is not a RubyObject Label end = new Label(); m.aload(0); - m.getfield(javaPath, "rubyObject", rubyName); + m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); m.ifnonnull(end); //TODO: no nulls for ctors? { m.newobj(p(ConcreteJavaProxy.class)); m.dup(); // rubyobject - if (ctorArg) + if (ctorArg >= 0) { - m.aload(1); // ruby - m.aload(2); // rubyclass + m.aload(1+ctorArg); // ruby + m.aload(2+ctorArg); // rubyclass } else { @@ -1961,11 +2150,13 @@ protected void generateObjectBarrier(SkinnyMethodAdapter m, boolean ctorArg, boo m.aload(0); // this m.swap(); - m.putfield(javaPath, "rubyObject", rubyName); - if (jcc.callInitialize && initialize) + m.putfield(javaPath, RUBY_OBJECT_FIELD, rubyName); + if (jcc.callInitialize && initialize) //TODO: should init be called when super calls an abstract method we implement? { m.ldc("initialize"); // TODO: consts? - rubycall(m, sig(IRubyObject.class, String.class) ); + m.aload(0); // this + m.getfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class) ); m.pop(); } else @@ -1973,7 +2164,7 @@ protected void generateObjectBarrier(SkinnyMethodAdapter m, boolean ctorArg, boo } m.label(end); } - + private void defineAllocator() { SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, "__allocate__", sig(IRubyObject.class, Ruby.class, RubyClass.class), null, null); @@ -1982,7 +2173,22 @@ private void defineAllocator() m.aload(0); // ruby m.aload(1); // rubyclass m.invokespecial(javaPath, "", sig(void.class, Ruby.class, RubyClass.class)); - m.getfield(javaPath, "rubyObject", rubyName); + m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); // Note: rubyObject has a ref to `this`, so no GC worries + m.areturn(); + m.end(); + } + + private void defineInterfaceMethods() + { + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "___jruby$rubyObject", sig(IRubyObject.class), null, null); + m.aload(0); // this + m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); + m.areturn(); + m.end(); + + + m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "___jruby$proxyClass", sig(JavaProxyClass.class), null, null); + m.getstatic(javaPath, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class)); m.areturn(); m.end(); } @@ -2122,6 +2328,16 @@ public synchronized void addClassAnnotation(Class annotation, Map fields) { classAnnotations.put(annotation, fields); } + public synchronized JavaClassConfiguration getClassConfig() { + if (javaClassConfiguration == null) javaClassConfiguration = new JavaClassConfiguration(); + + return javaClassConfiguration; + } + + public synchronized void setClassConfig(JavaClassConfiguration jcc) { + javaClassConfiguration = jcc; + } + @Override public T toJava(Class target) { if (target == Class.class) { @@ -2768,6 +2984,8 @@ public static CS_NAMES fromOrdinal(int ordinal) { private Map fieldSignatures; private Map> classAnnotations; + + private JavaClassConfiguration javaClassConfiguration; /** A cached tuple of method, type, and generation for dumping */ private MarshalTuple cachedDumpMarshal = MarshalTuple.NULL_TUPLE; diff --git a/core/src/main/java/org/jruby/compiler/impl/SkinnyMethodAdapter.java b/core/src/main/java/org/jruby/compiler/impl/SkinnyMethodAdapter.java index bb50db18f5d..723bd647e23 100644 --- a/core/src/main/java/org/jruby/compiler/impl/SkinnyMethodAdapter.java +++ b/core/src/main/java/org/jruby/compiler/impl/SkinnyMethodAdapter.java @@ -73,17 +73,19 @@ public final class SkinnyMethodAdapter extends MethodVisitor { private final ClassVisitor cv; private final Label start; private final Label end; + private final String signature; private MethodVisitor method; private Printer printer; - public SkinnyMethodAdapter(ClassVisitor cv, int flags, String name, String signature, String something, String[] exceptions) { + public SkinnyMethodAdapter(ClassVisitor cv, int flags, String name, String signature, String genericTypeInformation, String[] exceptions) { super(ASM4); - setMethodVisitor(cv.visitMethod(flags, name, signature, something, exceptions)); + setMethodVisitor(cv.visitMethod(flags, name, signature, genericTypeInformation, exceptions)); this.cv = cv; this.name = name; this.start = new Label(); this.end = new Label(); + this.signature = signature; } public ClassVisitor getClassVisitor() { @@ -102,6 +104,11 @@ public void setMethodVisitor(MethodVisitor mv) { this.method = mv; } } + + //TODO: copy to other code? + public String getSignature() { + return signature; + } /** * Short-hand for specifying a set of aloads diff --git a/core/src/main/java/org/jruby/ir/targets/indy/Bootstrap.java b/core/src/main/java/org/jruby/ir/targets/indy/Bootstrap.java index 6d598f17e40..781627ca9f6 100644 --- a/core/src/main/java/org/jruby/ir/targets/indy/Bootstrap.java +++ b/core/src/main/java/org/jruby/ir/targets/indy/Bootstrap.java @@ -15,7 +15,7 @@ import org.jruby.ir.runtime.IRRuntimeHelpers; import org.jruby.java.invokers.SingletonMethodInvoker; import org.jruby.javasupport.JavaUtil; -import org.jruby.javasupport.proxy.InternalJavaProxy; +import org.jruby.javasupport.proxy.ReifiedJavaProxy; import org.jruby.parser.StaticScope; import org.jruby.runtime.Binding; import org.jruby.runtime.Block; @@ -1191,7 +1191,7 @@ private static MethodHandle createJavaHandle(InvokeSite site, DynamicMethod meth } public static boolean subclassProxyTest(Object target) { - return target instanceof InternalJavaProxy; + return target instanceof ReifiedJavaProxy; } private static final MethodHandle IS_JAVA_SUBCLASS = findStatic(Bootstrap.class, "subclassProxyTest", methodType(boolean.class, Object.class)); diff --git a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java index d7c3ccad6a8..ed5a03651cf 100644 --- a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java +++ b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java @@ -57,7 +57,9 @@ import org.jruby.util.Loader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; +import org.objectweb.asm.commons.GeneratorAdapter; import static org.jruby.RubyInstanceConfig.JAVA_VERSION; import static org.jruby.util.CodegenUtils.ci; @@ -843,4 +845,9 @@ public static int calcBaseIndex(final Class[] params, int baseIndex) { return baseIndex; } + public static GeneratorAdapter makeGenerator(SkinnyMethodAdapter m) + { + return new GeneratorAdapter(m.getMethodVisitor(), Opcodes.ACC_PUBLIC, "ignored", m.getSignature()); + + } } diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index 33e39bebe13..c9f7f61d732 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -1,10 +1,14 @@ package org.jruby.java.proxies; +import java.lang.reflect.Field; + import org.jruby.Ruby; import org.jruby.RubyClass; import org.jruby.RubyModule; import org.jruby.internal.runtime.methods.DynamicMethod; import org.jruby.javasupport.Java; +import org.jruby.javasupport.JavaObject; +import org.jruby.javasupport.proxy.ReifiedJavaProxy; import org.jruby.runtime.Block; import org.jruby.runtime.CallSite; import org.jruby.runtime.MethodIndex; @@ -86,103 +90,111 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz } } - +//new override private static final class NewMethod extends org.jruby.internal.runtime.methods.JavaMethod { - - private transient CallSite jcreateSite; - final DynamicMethod newMethod; + final DynamicMethod newMethod; NewMethod(final RubyClass clazz) { super(clazz, Visibility.PUBLIC, "new"); newMethod = clazz.searchMethod("new"); } - - private CallSite jcreateSite() { // most of the time we won't need to instantiate - CallSite callSite = jcreateSite; - if (callSite == null) { - callSite = jcreateSite = MethodIndex.getFunctionalCallSite("__jcreate!"); +// TODO: reload this on method changes? + private DynamicMethod reifyAndNewMethod(IRubyObject clazz) { + + RubyClass parent = ((RubyClass)clazz); + System.err.println(parent.getName() + " is " + parent.getJavaProxy()); + if (parent.getJavaProxy()) return newMethod; + + // overridden class: reify and re-lookup new as reification changes it + if (parent.getReifiedClass() == null) { + parent.reifyWithAncestors(); } - return callSite; + //System.err.println(parent.getName() + " is " + parent.getJavaProxy()); + return new NewMethodReified(parent, parent.getReifiedClass()); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) { - IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy", args, block); - if ( ((JavaProxy) proxy).object == null ) jcreateSite().call(context, proxy, proxy, args, block); - return proxy; + return reifyAndNewMethod(self).call(context, self, clazz, "new_proxy", args, block); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) { - IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy", block); - if ( ((JavaProxy) proxy).object == null ) jcreateSite().call(context, proxy, proxy, block); - return proxy; + return reifyAndNewMethod(self).call(context, self, clazz, "new",block); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) { - IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy", arg0, block); - if ( ((JavaProxy) proxy).object == null ) jcreateSite().call(context, proxy, proxy, arg0, block); - return proxy; + return reifyAndNewMethod(self).call(context, self, clazz, "new",arg0, block); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) { - IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy", arg0, arg1, block); - if ( ((JavaProxy) proxy).object == null ) jcreateSite().call(context, proxy, proxy, arg0, arg1, block); - return proxy; + return reifyAndNewMethod(self).call(context, self, clazz, "new",arg0, arg1, block); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) { - IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy", arg0, arg1, arg2, block); - if ( ((JavaProxy) proxy).object == null ) jcreateSite().call(context, proxy, proxy, arg0, arg1, arg2, block); - return proxy; + return reifyAndNewMethod(self).call(context, self, clazz, "new",arg0, arg1, arg2, block); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) { - IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy", args); - if ( ((JavaProxy) proxy).object == null ) jcreateSite().call(context, proxy, proxy, args); - return proxy; + return reifyAndNewMethod(self).call(context, self, clazz, "new",args); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name) { - IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy"); - if ( ((JavaProxy) proxy).object == null ) jcreateSite().call(context, proxy, proxy); - return proxy; + return reifyAndNewMethod(self).call(context, self, clazz,"new_proxy"); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0) { - IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy", arg0); - if ( ((JavaProxy) proxy).object == null ) jcreateSite().call(context, proxy, proxy, arg0); - return proxy; + return reifyAndNewMethod(self).call(context, self, clazz, "new_proxy",arg0); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1) { - IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy", arg0, arg1); - if ( ((JavaProxy) proxy).object == null ) jcreateSite().call(context, proxy, proxy, arg0, arg1); - return proxy; + return reifyAndNewMethod(self).call(context, self, clazz,"new", arg0, arg1); } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) { - IRubyObject proxy = newMethod.call(context, self, clazz, "new_proxy", arg0, arg1, arg2); - if ( ((JavaProxy) proxy).object == null ) jcreateSite().call(context, proxy, proxy, arg0, arg1, arg2); - return proxy; + return reifyAndNewMethod(self).call(context, self, clazz,"new", arg0, arg1, arg2); + } + + } + +//TODO: cleanup + public static final class NewMethodReified extends org.jruby.internal.runtime.methods.JavaMethod.JavaMethodN { + + private Field rubyObject; + private final DynamicMethod initialize; + + //TODO: package? + public NewMethodReified(final RubyClass clazz, final Class reified) { + super(clazz, Visibility.PUBLIC, "new"); + initialize = clazz.searchMethod("__jcreate!"); } + @Override + public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, + IRubyObject[] args) + { + + JavaObject jo = (JavaObject)initialize.call(context, self, clazz, "new", args); + return ((ReifiedJavaProxy)jo.getValue()).___jruby$rubyObject(); + } + } - protected static void initialize(final RubyClass ConcreteJavaProxy) { - ConcreteJavaProxy.addMethod("initialize", new InitializeMethod(ConcreteJavaProxy)); + protected static void initialize(final RubyClass concreteJavaProxy) { + concreteJavaProxy.addMethod("initialize", new InitializeMethod(concreteJavaProxy)); + System.err.println("adding to " + concreteJavaProxy.getName()); // We define a custom "new" method to ensure that __jcreate! is getting called, // so that if the user doesn't call super in their subclasses, the object will // still get set up properly. See JRUBY-4704. - RubyClass singleton = ConcreteJavaProxy.getSingletonClass(); + RubyClass singleton = concreteJavaProxy.getSingletonClass(); singleton.addMethod("new", new NewMethod(singleton)); } @@ -213,6 +225,11 @@ public IRubyObject id() { @SuppressWarnings("unchecked") public T toJava(Class type) { final Object object = getObject(); + if (object == null) + { + System.out.println(":-("); + return null; + } final Class clazz = object.getClass(); if ( type.isPrimitive() ) { diff --git a/core/src/main/java/org/jruby/javasupport/Java.java b/core/src/main/java/org/jruby/javasupport/Java.java index 2390e246774..0050de85dbe 100644 --- a/core/src/main/java/org/jruby/javasupport/Java.java +++ b/core/src/main/java/org/jruby/javasupport/Java.java @@ -131,7 +131,6 @@ public void load(Ruby runtime, boolean wrap) { RubyClassPathVariable.createClassPathVariable(runtime); - runtime.setJavaProxyClassFactory(JavaProxyClassFactory.createFactory()); // modify ENV_JAVA to be a read/write version final Map systemProperties = new SystemPropertiesMap(); @@ -633,35 +632,38 @@ public final IRubyObject call(ThreadContext context, IRubyObject self, RubyModul final JavaProxyConstructor matching; switch (constructors.length) { - case 1: - case 2: matching = matchConstructor0ArityOne(context, constructors, arg0); break; + case 1: matching = matchConstructor0ArityOne(context, constructors, arg0); break; default: matching = matchConstructorArityOne(context, constructors, arg0); } - + if (self instanceof JavaProxy) + { + return context.nil; + } JavaObject newObject = matching.newInstance(self, arg0); - return JavaUtilities.set_java_object(self, self, newObject); + return newObject; } - @Override //TODO: ensure both sides are good + @Override public final IRubyObject call(final ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) { - if (((JavaProxy) self).getObject() == null ) - { final int arity = args.length; final JavaProxyConstructor[] constructors = getProxyClass(self).getConstructors(); final JavaProxyConstructor matching; switch (constructors.length) { - case 1:// IS this logic sound? or should we be more careful - case 2: matching = matchConstructor0(context, constructors, arity, args); break; + case 1: matching = matchConstructor0(context, constructors, arity, args); break; default: matching = matchConstructor(context, constructors, arity, args); } + if (self instanceof JavaProxy) + { + return context.nil; + } JavaObject newObject = matching.newInstance(self, args); - return JavaUtilities.set_java_object(self, self, newObject); - } else - { - return (IRubyObject) ((JavaProxy)self).dataGetStruct(); - } + if (self instanceof JavaProxy) + { + JavaUtilities.set_java_object(self, self, newObject); + } + return newObject; } // assumes only 1 *Ruby* constructor exists! (Filters out nonruby) @@ -730,7 +732,7 @@ private JavaProxyConstructor matchConstructorArityOne(final ThreadContext contex } // generic (slowest) path - private JavaProxyConstructor matchConstructor(final ThreadContext context, + public JavaProxyConstructor matchConstructor(final ThreadContext context, final JavaProxyConstructor[] constructors, final int arity, final IRubyObject... args) { ArrayList forArity = findCallablesForArity(arity, constructors); diff --git a/core/src/main/java/org/jruby/javasupport/JavaMethod.java b/core/src/main/java/org/jruby/javasupport/JavaMethod.java index 5b9f6eaf1b6..13b56e79312 100644 --- a/core/src/main/java/org/jruby/javasupport/JavaMethod.java +++ b/core/src/main/java/org/jruby/javasupport/JavaMethod.java @@ -53,7 +53,7 @@ import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.RaiseException; -import org.jruby.javasupport.proxy.InternalJavaProxy; +import org.jruby.javasupport.proxy.ReifiedJavaProxy; import org.jruby.javasupport.proxy.JavaProxyClass; import org.jruby.javasupport.proxy.JavaProxyMethod; import org.jruby.runtime.ObjectAllocator; @@ -241,11 +241,11 @@ public IRubyObject invoke(ThreadContext context, IRubyObject[] args) { // // this test really means, that this is a ruby-defined subclass of a java class // - if ( javaInvokee instanceof InternalJavaProxy && + if ( javaInvokee instanceof ReifiedJavaProxy && // don't bother to check if final method, it won't // be there (not generated, can't be!) ! isFinal ) { - JavaProxyClass jpc = ((InternalJavaProxy) javaInvokee).___getProxyClass(); + JavaProxyClass jpc = ((ReifiedJavaProxy) javaInvokee).___jruby$proxyClass(); JavaProxyMethod jpm = jpc.getMethod( method.getName(), parameterTypes ); if ( jpm != null && jpm.hasSuperImplementation() ) { return invokeWithExceptionHandling(context, jpm.getSuperMethod(), javaInvokee, arguments); @@ -604,11 +604,11 @@ public final AccessibleObject accessibleObject() { private boolean mightBeProxy(Object javaInvokee) { // this test really means, that this is a ruby-defined subclass of a java class - return javaInvokee instanceof InternalJavaProxy && !isFinal; + return javaInvokee instanceof ReifiedJavaProxy && !isFinal; } private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee, Object... args) { - JavaProxyClass jpc = ((InternalJavaProxy) javaInvokee).___getProxyClass(); + JavaProxyClass jpc = ((ReifiedJavaProxy) javaInvokee).___jruby$proxyClass(); JavaProxyMethod jpm; if ((jpm = jpc.getMethod(method.getName(), parameterTypes)) != null && jpm.hasSuperImplementation()) { return invokeDirectSuperWithExceptionHandling(context, jpm.getSuperMethod(), javaInvokee, args); @@ -618,7 +618,7 @@ private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee } private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee) { - JavaProxyClass jpc = ((InternalJavaProxy) javaInvokee).___getProxyClass(); + JavaProxyClass jpc = ((ReifiedJavaProxy) javaInvokee).___jruby$proxyClass(); JavaProxyMethod jpm; if ((jpm = jpc.getMethod(method.getName(), parameterTypes)) != null && jpm.hasSuperImplementation()) { return invokeDirectSuperWithExceptionHandling(context, jpm.getSuperMethod(), javaInvokee); @@ -628,7 +628,7 @@ private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee } private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee, Object arg0) { - JavaProxyClass jpc = ((InternalJavaProxy) javaInvokee).___getProxyClass(); + JavaProxyClass jpc = ((ReifiedJavaProxy) javaInvokee).___jruby$proxyClass(); JavaProxyMethod jpm; if ((jpm = jpc.getMethod(method.getName(), parameterTypes)) != null && jpm.hasSuperImplementation()) { return invokeDirectSuperWithExceptionHandling(context, jpm.getSuperMethod(), javaInvokee, arg0); @@ -638,7 +638,7 @@ private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee } private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee, Object arg0, Object arg1) { - JavaProxyClass jpc = ((InternalJavaProxy) javaInvokee).___getProxyClass(); + JavaProxyClass jpc = ((ReifiedJavaProxy) javaInvokee).___jruby$proxyClass(); JavaProxyMethod jpm; if ((jpm = jpc.getMethod(method.getName(), parameterTypes)) != null && jpm.hasSuperImplementation()) { return invokeDirectSuperWithExceptionHandling(context, jpm.getSuperMethod(), javaInvokee, arg0, arg1); @@ -648,7 +648,7 @@ private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee } private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee, Object arg0, Object arg1, Object arg2) { - JavaProxyClass jpc = ((InternalJavaProxy) javaInvokee).___getProxyClass(); + JavaProxyClass jpc = ((ReifiedJavaProxy) javaInvokee).___jruby$proxyClass(); JavaProxyMethod jpm; if ((jpm = jpc.getMethod(method.getName(), parameterTypes)) != null && jpm.hasSuperImplementation()) { return invokeDirectSuperWithExceptionHandling(context, jpm.getSuperMethod(), javaInvokee, arg0, arg1, arg2); @@ -658,7 +658,7 @@ private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee } private IRubyObject tryProxyInvocation(ThreadContext context, Object javaInvokee, Object arg0, Object arg1, Object arg2, Object arg3) { - JavaProxyClass jpc = ((InternalJavaProxy) javaInvokee).___getProxyClass(); + JavaProxyClass jpc = ((ReifiedJavaProxy) javaInvokee).___jruby$proxyClass(); JavaProxyMethod jpm; if ((jpm = jpc.getMethod(method.getName(), parameterTypes)) != null && jpm.hasSuperImplementation()) { return invokeDirectSuperWithExceptionHandling(context, jpm.getSuperMethod(), javaInvokee, arg0, arg1, arg2, arg3); diff --git a/core/src/main/java/org/jruby/javasupport/JavaUtil.java b/core/src/main/java/org/jruby/javasupport/JavaUtil.java index 814f9cfeb53..cf820dc7c0d 100644 --- a/core/src/main/java/org/jruby/javasupport/JavaUtil.java +++ b/core/src/main/java/org/jruby/javasupport/JavaUtil.java @@ -86,7 +86,7 @@ import org.jruby.java.proxies.ArrayJavaProxy; import org.jruby.java.proxies.JavaProxy; import org.jruby.java.proxies.RubyObjectHolderProxy; -import org.jruby.javasupport.proxy.InternalJavaProxy; +import org.jruby.javasupport.proxy.ReifiedJavaProxy; import org.jruby.runtime.Helpers; import org.jruby.runtime.Block; import org.jruby.runtime.ThreadContext; @@ -182,6 +182,10 @@ public static IRubyObject convertJavaToRuby(Ruby runtime, boolean b) { * @return corresponding Ruby type, or a functional Java proxy */ public static IRubyObject convertJavaToUsableRubyObject(Ruby runtime, Object object) { + if (object.getClass().getName().startsWith("rubyobj")) + { + + } IRubyObject result = trySimpleConversions(runtime, object); if (result != null) return result; @@ -639,9 +643,9 @@ public static IRubyObject trySimpleConversions(Ruby runtime, Object object) { return ((RubyObjectHolderProxy) object).__ruby_object(); } - if ( object instanceof InternalJavaProxy ) { - final InternalJavaProxy internalJavaProxy = (InternalJavaProxy) object; - IRubyObject orig = internalJavaProxy.___getInvocationHandler().getOrig(); + if ( object instanceof ReifiedJavaProxy ) { + final ReifiedJavaProxy internalJavaProxy = (ReifiedJavaProxy) object; + IRubyObject orig = internalJavaProxy.___jruby$rubyObject(); if (orig != null) return orig; } diff --git a/core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxyHelper.java b/core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxyHelper.java deleted file mode 100644 index 6d1dc45f507..00000000000 --- a/core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxyHelper.java +++ /dev/null @@ -1,68 +0,0 @@ -/***** BEGIN LICENSE BLOCK ***** - * Version: EPL 2.0/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Eclipse Public - * License Version 2.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/epl-v20.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) 2006 Kresten Krab Thorup - * - * 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.proxy; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import org.jruby.javasupport.proxy.JavaProxyClassFactory.ClassInvocationHolder; - -/** - * Contains methods that are only called from generated code - * - * @author krab - */ -public class InternalJavaProxyHelper { - - public static JavaProxyClass initProxyClass(Class proxy) { - return new JavaProxyClass(proxy); - } - - public static JavaProxyMethod initProxyMethod(JavaProxyClass proxyClass, - String name, String desc, boolean hasSuper) { - return proxyClass.initMethod(name, desc, hasSuper); - } - - private static Map defaultJpihs = new ConcurrentHashMap<>(); - - public static int sized() - { - return defaultJpihs.size(); - } - - public static ClassInvocationHolder getDefaultJPIH(int id) { - return defaultJpihs.remove(id); - } - - public static void addDefaultJIPH(int id, ClassInvocationHolder jiph){ - defaultJpihs.put(id, jiph); - } - -} diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java index 4cff1e7f33d..2978fab4747 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java @@ -47,6 +47,8 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; import org.jruby.Ruby; import org.jruby.RubyArray; @@ -60,6 +62,8 @@ import org.jruby.anno.JRubyMethod; import org.jruby.exceptions.RaiseException; import org.jruby.internal.runtime.methods.DynamicMethod; +import org.jruby.java.codegen.Reified; +import org.jruby.java.proxies.ConcreteJavaProxy.NewMethodReified; import org.jruby.javasupport.*; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.builtin.IRubyObject; @@ -158,7 +162,11 @@ public static JavaProxyClass newProxyClass(final Ruby runtime, Class superClass, if ( proxyClass != null ) return proxyClass; final ClassLoader loader = runtime.getJRubyClassLoader(); - proxyClass = runtime.getJavaProxyClassFactory().genProxyClass(runtime, (ClassDefiningClassLoader) loader, null, superClass, interfaces, names, field, extraMethods, clazz); + RuntimeException re = new RuntimeException("New newProxyClass call"); + re.printStackTrace(); + if (4>3) + throw re; + proxyClass = null; return JavaSupportImpl.saveJavaProxyClass(runtime, classKey, proxyClass); } @@ -166,7 +174,7 @@ public static JavaProxyClass newProxyClass(Ruby runtime, Class superClass, Class[] interfaces) throws InvocationTargetException { return newProxyClass(runtime, superClass, interfaces, null, null, null, null); } - +/* public static Object newProxyInstance(Ruby runtime, Class superClass, Class[] interfaces, Class[] constructorParameters, Object[] constructorArgs, JavaProxyInvocationHandler handler) throws IllegalArgumentException, @@ -178,7 +186,7 @@ public static Object newProxyInstance(Ruby runtime, Class superClass, Class[] in constructorParameters == null ? EMPTY_CLASS_ARRAY : constructorParameters ); return constructor.newInstance(constructorArgs, handler); - } + }*/ public Class getSuperclass() { return proxyClass.getSuperclass(); @@ -188,7 +196,8 @@ public Class[] getInterfaces() { Class[] ifaces = proxyClass.getInterfaces(); Class[] result = new Class[ifaces.length - 1]; for ( int i = 0, j = 0; i < ifaces.length; i++ ) { - if ( ifaces[i] == InternalJavaProxy.class ) continue; + //TODO: ??? + if ( ifaces[i] == ReifiedJavaProxy.class ) continue; result[ j++ ] = ifaces[i]; } return result; @@ -198,16 +207,18 @@ public Class[] getInterfaces() { // TODO: filter out in Java Jcreate or here? public JavaProxyConstructor[] getConstructors() { - JavaProxyConstructor[] constructors = this.constructors; - if ( constructors != null ) return constructors; + JavaProxyConstructor[] constructorsCached = this.constructors; + if ( constructorsCached != null ) return constructorsCached; final Ruby runtime = getRuntime(); final Constructor[] ctors = proxyClass.getConstructors(); - constructors = new JavaProxyConstructor[ ctors.length ]; + List constructors = new ArrayList<>(ctors.length); for ( int i = 0; i < ctors.length; i++ ) { - constructors[i] = new JavaProxyConstructor(runtime, this, ctors[i]); + JavaProxyConstructor jpc = new JavaProxyConstructor(runtime, this, ctors[i]); + if (!jpc.isExportable()) + constructors.add(jpc); } - return this.constructors = constructors; + return this.constructors = constructors.toArray(new JavaProxyConstructor[constructors.size()]); } public JavaProxyConstructor getConstructor(final Class[] args) @@ -215,7 +226,7 @@ public JavaProxyConstructor getConstructor(final Class[] args) final Class[] realArgs = new Class[args.length + 1]; System.arraycopy(args, 0, realArgs, 0, args.length); - realArgs[ args.length ] = JavaProxyInvocationHandler.class; + realArgs[ args.length ] = JavaProxyInvocationHandler.class; //TODO: fix! @SuppressWarnings("unchecked") Constructor constructor = proxyClass.getConstructor(realArgs); @@ -599,9 +610,76 @@ public static RubyObject get_with_class(final IRubyObject self, IRubyObject obj) return getProxyClass(runtime, (RubyClass) obj); } + + //Note: called from of reified classes + public static JavaProxyClass setProxyClassReified(final Ruby runtime, final RubyClass clazz, final Class reified, final boolean allocator) { + JavaProxyClass proxyClass = new JavaProxyClass(runtime, reified); + // TODO: don't duplicate this code from above + // NOTE: currently we regenerate proxy classes when a Ruby method is added on the type + //JavaSupport.ProxyClassKey classKey = JavaSupport.ProxyClassKey.getInstance(superClass, interfaces, names); + //TODO: + //JavaSupportImpl.saveJavaProxyClass(runtime, classKey, proxyClass); + clazz.setInstanceVariable("@java_proxy_class", proxyClass); + + + RubyClass singleton = clazz.getSingletonClass(); + + System.err.println("Setting on " + clazz.toString() + " + single " + singleton.toString()); + singleton.setInstanceVariable("@java_proxy_class", proxyClass); + singleton.setInstanceVariable("@java_class", Java.wrapJavaObject(runtime, reified)); + if (allocator) singleton.addMethod("new", new NewMethodReified(clazz, reified)); +// +// //TODO: wait, what???? +// singleton = clazz.getMetaClass(); +// singleton.setInstanceVariable("@java_proxy_class", proxyClass); +// singleton.setInstanceVariable("@java_class", Java.wrapJavaObject(runtime, reified)); +// if (allocator) singleton.addMethod("new", new NewMethodReified(clazz, reified)); + return proxyClass; + } + + /** + * These objects are to allow static initializers in reified code. See RubyClass.BaseReifier for + * details + */ + private static final AtomicInteger lookupIdNext = new AtomicInteger(0); + private static final Map lookup = new ConcurrentHashMap<>(); + + public static Integer addStaticInitLookup(Ruby runtime, RubyClass clazz) + { + int val = lookupIdNext.incrementAndGet(); + lookup.put(val, new Object[]{runtime, clazz}); + return val; + } + // used by reified code in RubyClass + public static Object[] getStaticInitLookup(int id) + { + return lookup.remove(id); + } public static JavaProxyClass getProxyClass(final Ruby runtime, final RubyClass clazz) { - + + try + { + clazz.reifyWithAncestors(); + JavaProxyClass jpc = (JavaProxyClass) clazz.getInstanceVariable("@java_proxy_class"); + if (5>4) + return jpc; + } + catch (RaiseException e) { + throw e; + } + catch (Exception e) { + e.printStackTrace(); + String msg = e.getLocalizedMessage(); + if ( msg == null ) msg = e.toString(); + RaiseException ex = runtime.newArgumentError("unable to create proxy class for " + clazz.getName() + " : " + msg); + ex.initCause(e); + throw ex; + } + + + // TODO: ensure none of the below code matters, and trim below here + // Let's only generate methods for those the user may actually intend to override. // That includes any defined in the current class, and any ancestors that are also JavaProxyClasses // (but none from any other ancestor classes). Methods defined in mixins will be considered @@ -694,12 +772,14 @@ else if (javaClass != var) { Class[] interfaces = interfaceList.isEmpty() ? EMPTY_CLASS_ARRAY : interfaceList.toArray(new Class[interfaceList.size()]); try { - return newProxyClass(runtime, javaClass.javaClass(), interfaces, names, clazz.getFieldSignatures(), clazz.getMethodSignatures(), clazz); + throw new RuntimeException("Old Concrete called"); + //return null; } catch (RaiseException e) { throw e; } catch (Exception e) { + String msg = e.getLocalizedMessage(); if ( msg == null ) msg = e.toString(); RaiseException ex = runtime.newArgumentError("unable to create proxy class for " + javaClass + " : " + msg); diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java index 0cbc20d5687..738cd1e1e9d 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClassFactory.java @@ -54,7 +54,6 @@ import org.jruby.javasupport.JavaObject; import org.jruby.javasupport.JavaUtil; import org.jruby.javasupport.JavaUtilities; -import org.jruby.javasupport.proxy.JavaProxyConstructor.MethodInvocationHandler; import org.jruby.runtime.Block; import org.jruby.runtime.Helpers; import org.jruby.runtime.ThreadContext; @@ -85,1050 +84,10 @@ public class JavaProxyClassFactory { private static final Logger LOG = LoggerFactory.getLogger(JavaProxyClassFactory.class); - static final Type[] EMPTY_TYPE_ARRAY = new Type[0]; - - static final Type JAVA_LANG_CLASS_TYPE = Type.getType(Class.class); - - private static final org.objectweb.asm.commons.Method forName = org.objectweb.asm.commons.Method - .getMethod("java.lang.Class forName(java.lang.String)"); - - private static final String INVOCATION_HANDLER_FIELD_NAME = "__handler"; - private static final String INVOCATION_HANDLER_BUILDER_FIELD_NAME = "__handler_builder"; - - private static final String PROXY_CLASS_FIELD_NAME = "__proxy_class"; - - private static final Type PROXY_METHOD_TYPE = Type.getType(JavaProxyMethod.class); - - private static final Type PROXY_CLASS_TYPE = Type.getType(JavaProxyClass.class); - - private static final Type INVOCATION_HANDLER_TYPE = Type.getType(JavaProxyInvocationHandler.class); - - private static final Type INVOCATION_HANDLER_BUILDER_TYPE = Type.getType(ClassInvocationHolder.class); - - // public Object invoke(Object receiver, JavaProxyMethod method, Object[] args) - private static final org.objectweb.asm.commons.Method invoke = org.objectweb.asm.commons.Method - .getMethod("java.lang.Object invoke(java.lang.Object, " + PROXY_METHOD_TYPE.getClassName() + ", java.lang.Object[])"); - - private static final Type INTERNAL_PROXY_HELPER_TYPE = Type.getType(InternalJavaProxyHelper.class); - - // public static JavaProxyClass initProxyClass(Class) - private static final org.objectweb.asm.commons.Method initProxyClass = org.objectweb.asm.commons.Method - .getMethod(JavaProxyClass.class.getName() + " initProxyClass(java.lang.Class)"); - - // public static JavaProxyMethod initProxyMethod(JavaProxyClass proxyClass, String name, String desc, boolean hasSuper) - private static final org.objectweb.asm.commons.Method initProxyMethod = org.objectweb.asm.commons.Method - .getMethod(PROXY_METHOD_TYPE.getClassName() + " initProxyMethod(" - + JavaProxyClass.class.getName() + ",java.lang.String,java.lang.String,boolean)"); - - // public static ClassInvocationHolder getDefaultJPIH(Long) - private static final org.objectweb.asm.commons.Method getDefaultJPIH = org.objectweb.asm.commons.Method - .getMethod(ClassInvocationHolder.class.getName() + " getDefaultJPIH(int)"); - - // public JavaProxyInvocationHandler init(Object jself, Object[] args) // TODO: wrong types - private static final org.objectweb.asm.commons.Method initHandlerClass = org.objectweb.asm.commons.Method - .getMethod(JavaProxyInvocationHandler.class.getName() + " init("+JavaProxyClass.class.getName() + ",java.lang.Object,java.lang.Object[])"); - - /* - private static final org.objectweb.asm.commons.Method trampolineInit = org.objectweb.asm.commons.Method - .getMethod(JavaProxyInvocationHandler.class.getName() + "__jruby$trampoline$init(java.lang.Object[])");*/ - - - private static final Type JAVA_PROXY_TYPE = Type.getType(InternalJavaProxy.class); - - private static final AtomicInteger counter = new AtomicInteger(0); - - private static int nextId() { return counter.incrementAndGet(); } - - public static JavaProxyClassFactory createFactory() { - final String factoryClassName = Options.JI_PROXYCLASSFACTORY.load(); - - JavaProxyClassFactory factory = null; - if ( factoryClassName != null ) { - try { - Class clazz = Class.forName(factoryClassName); - Object instance = clazz.getConstructor().newInstance(); - if ( instance instanceof JavaProxyClassFactory ) { - factory = (JavaProxyClassFactory) instance; - LOG.info("Created proxy class factory: {}", factory); - } else { - LOG.error("Invalid proxy class factory: {}", instance); - } - } catch (ClassNotFoundException | - InstantiationException | - InvocationTargetException | - IllegalAccessException | - NoSuchMethodException e) { - LOG.error(e.getClass().getSimpleName() + " creating proxy class factory: ", e); - } - } - - return factory != null ? factory : new JavaProxyClassFactory(); - } - static ThreadLocal runtimeTLS = new ThreadLocal<>(); - public final JavaProxyClass genProxyClass(final Ruby runtime, ClassDefiningClassLoader loader, - String targetClassName, Class superClass, Class[] interfaces, Set names, Map fields, Map extraMethods, RubyClass clazz) - throws InvocationTargetException { - final Ruby prev = runtimeTLS.get(); - runtimeTLS.set(runtime); - try { - return newProxyClass(runtime, loader, targetClassName, superClass, interfaces, names, fields, extraMethods, clazz); - } - finally { runtimeTLS.set(prev); } - } - - public JavaProxyClass newProxyClass(final Ruby runtime, ClassDefiningClassLoader loader, - String targetClassName, Class superClass, Class[] interfaces, Set names, Map fields, Map extraMethods, RubyClass clazz) - throws InvocationTargetException { - - if (targetClassName == null) { - targetClassName = targetClassName(superClass); - } - validateArgs(runtime, targetClassName, superClass); - - Type selfType = Type.getType('L' + toInternalClassName(targetClassName) + ';'); - Map methods = collectMethods(superClass, interfaces, names, extraMethods, clazz.getMethodAnnotations()); - int id = System.identityHashCode(this); - InternalJavaProxyHelper.addDefaultJIPH(id, new ClassInvocationHolder(runtime, clazz)); - - return generate(loader, targetClassName, superClass, interfaces, methods, selfType, clazz, id);//TODO: check for id not removed - } - - private JavaProxyClass generate(ClassDefiningClassLoader loader, String targetClassName, - Class superClass, Class[] interfaces, - Map methods, Type selfType, RubyClass rclass, int id) { - ClassWriter cw = beginProxyClass(selfType, targetClassName, superClass, interfaces, loader); - - if (1 < 2) - throw new RuntimeException("Ack, old code being hit!"); - - Map>> fieldannos; - Map fields; - if (rclass != null) { - fieldannos = rclass.getFieldAnnotations(); - fields = rclass.getFieldSignatures(); - } else { - fieldannos = Collections.EMPTY_MAP; - fields = Collections.EMPTY_MAP; - } - - for (String key : fields.keySet()) { - FieldVisitor fv = cw.visitField(Opcodes.ACC_PUBLIC, - key, - Type.getDescriptor(fields.get(key)), null, null - ); - Map> annos = fieldannos.getOrDefault(key, Collections.EMPTY_MAP); - annos.forEach((anno, args) -> { - AnnotationVisitor av = fv.visitAnnotation(Type.getType(anno).getDescriptor(), true); - CodegenUtils.visitAnnotationFields(av, args); - av.visitEnd(); - }); - fv.visitEnd(); - } - - GeneratorAdapter clazzInit = createClassInitializer(selfType, cw, id);//TODO:0 - - generateConstructors(superClass, selfType, cw); - generate___getProxyClass(selfType, cw); - generate___getInvocationHandler(selfType, cw); - generateProxyMethods(superClass, methods, selfType, cw, clazzInit); - - // finish class initializer - clazzInit.returnValue(); - clazzInit.endMethod(); - - // end class - cw.visitEnd(); - - Class clazz = loader.defineClass(selfType.getClassName(), cw.toByteArray()); - try - { - Files.write(Paths.get("/tmp/dumped", selfType.getClassName() + ".class"), cw.toByteArray()); - } - catch (IOException e) - { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - - // trigger class initialization for the class - try { - Field proxy_class = clazz.getDeclaredField(PROXY_CLASS_FIELD_NAME); - // proxy_class.setAccessible(true); // field is public - return (JavaProxyClass) proxy_class.get(clazz); - } - catch (NoSuchFieldException|IllegalAccessException ex) { - Helpers.throwException(ex); return null; // re-throws (although unexpected) - } - } - - private static String targetClassName(final Class clazz) { - // We always prepend an org.jruby.proxy package to the beginning - // because java and javax packages are protected and signed - // jars prevent us generating new classes with those package - // names. See JRUBY-2439. - final String fullName = clazz.getName(); - final int idx = fullName.lastIndexOf('.'); - String className = idx == -1 ? fullName : fullName.substring(idx + 1); - return proxyPackageName(fullName) - .append('.').append(className) - .append("$Proxy").append(nextId()).toString(); - } - - private static ClassWriter beginProxyClass(Type selfType, final String className, - final Class superClass, final Class[] interfaces, final ClassDefiningClassLoader loader) { - - ClassWriter cw = ASM.newClassWriter(loader.asClassLoader()); - - // start class - cw.visit(JAVA_VERSION, Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SUPER, - toInternalClassName(className), /*signature*/ null, - toInternalClassName(superClass), - interfaceNamesForProxyClass(interfaces)); - - // private final JavaProxyInvocationHandler __handler; - cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL, - INVOCATION_HANDLER_FIELD_NAME, - INVOCATION_HANDLER_TYPE.getDescriptor(), null, null - ).visitEnd(); - - // public static final ClassInvocationHolder __handler_builder; - cw.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, - INVOCATION_HANDLER_BUILDER_FIELD_NAME, - INVOCATION_HANDLER_BUILDER_TYPE.getDescriptor(), null, null - ).visitEnd(); - - // /* public */ static final JavaProxyClass __proxy_class; - cw.visitField(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, - PROXY_CLASS_FIELD_NAME, - PROXY_CLASS_TYPE.getDescriptor(), null, null - ).visitEnd(); - - - //make trampoline - // NOTE: not for 0-arg ctor -// GeneratorAdapter ga2 = new GeneratorAdapter(Opcodes.ACC_PUBLIC, trampolineInit, null, null, cw); -// -// -// ga2.getStatic(selfType, PROXY_CLASS_FIELD_NAME, PROXY_CLASS_TYPE); -// ga2.loadThis(); -// ga2.loadArgArray(); -// ga2.invokeVirtual(INVOCATION_HANDLER_BUILDER_TYPE, initHandlerClass); -// -// -// ga2.loadThis(); -// ga2.loadArgs(); - - /// ???? - -// ga2.returnValue(); -// ga2.endMethod(); - - return cw; - } - - private static String[] interfaceNamesForProxyClass(final Class[] interfaces) { - String[] interfaceNames = new String[interfaces.length + 1]; - - for (int i = 0; i < interfaces.length; i++) { - interfaceNames[i] = toInternalClassName(interfaces[i]); - } - // all proxies implement our InternalJavaProxy interface : - interfaceNames[interfaces.length] = toInternalClassName(InternalJavaProxy.class); - - return interfaceNames; - } - - private static void generateProxyMethods(Class superClass, - Map methods, Type selfType, ClassVisitor cw, - GeneratorAdapter clazzInit) { - for (MethodData md: methods.values()) { - Type superClassType = Type.getType(superClass); - generateProxyMethod(selfType, superClassType, cw, clazzInit, md); - } - } - - /** - * @see InternalJavaProxy - */ - private static void generate___getInvocationHandler(Type selfType, ClassVisitor cw) { - // public JavaProxyInvocationHandler ___getInvocationHandler() { return this.__handler; } - - // make getter for handler (due implements InternalJavaProxy) - GeneratorAdapter gh = new GeneratorAdapter(Opcodes.ACC_PUBLIC, - new org.objectweb.asm.commons.Method("___getInvocationHandler", - INVOCATION_HANDLER_TYPE, EMPTY_TYPE_ARRAY), null, - EMPTY_TYPE_ARRAY, cw); - - gh.loadThis(); - gh.getField(selfType, INVOCATION_HANDLER_FIELD_NAME, INVOCATION_HANDLER_TYPE); - gh.returnValue(); - gh.endMethod(); - } - - /** - * @see InternalJavaProxy - */ - private static void generate___getProxyClass(Type selfType, ClassVisitor cw) { - // public JavaProxyClass __getProxyClass() { return /* static */ __proxy_class; } - - // make getter for proxy class (due implements InternalJavaProxy) - GeneratorAdapter gpc = new GeneratorAdapter(Opcodes.ACC_PUBLIC, - new org.objectweb.asm.commons.Method("___getProxyClass", - PROXY_CLASS_TYPE, EMPTY_TYPE_ARRAY), null, - EMPTY_TYPE_ARRAY, cw); - gpc.getStatic(selfType, PROXY_CLASS_FIELD_NAME, PROXY_CLASS_TYPE); - gpc.returnValue(); - gpc.endMethod(); - } - - private static void generateConstructors(Class superClass, Type selfType, ClassVisitor cw) { - Constructor[] cons = superClass.getDeclaredConstructors(); - - for (int i = 0; i < cons.length; i++) { - // if the constructor is private, pretend it doesn't exist - if (Modifier.isPrivate(cons[i].getModifiers())) continue; - - // otherwise, define everything and let some of them fail at invocation - generateConstructor(selfType, cons[i], cw); - if (cons[i].getParameterCount() == 0) // FIXME: zero only, but does somewhat work for 1+ too - generateRawConstructor(selfType, cons[i], cw); - } - } - - private static GeneratorAdapter createClassInitializer(Type selfType, ClassVisitor cw, int id) { - GeneratorAdapter clazzInit = new GeneratorAdapter(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC, - new org.objectweb.asm.commons.Method("", Type.VOID_TYPE, EMPTY_TYPE_ARRAY), - null, EMPTY_TYPE_ARRAY, cw); - - clazzInit.visitLdcInsn(id); - clazzInit.invokeStatic(INTERNAL_PROXY_HELPER_TYPE, getDefaultJPIH); - clazzInit.putStatic(selfType, INVOCATION_HANDLER_BUILDER_FIELD_NAME, INVOCATION_HANDLER_BUILDER_TYPE); - // __handler_builder = JavaProxyClassFactory.getDefault(id) - - clazzInit.visitLdcInsn(selfType.getClassName()); - clazzInit.invokeStatic(JAVA_LANG_CLASS_TYPE, forName); - clazzInit.invokeStatic(INTERNAL_PROXY_HELPER_TYPE, initProxyClass); - clazzInit.dup(); - clazzInit.putStatic(selfType, PROXY_CLASS_FIELD_NAME, PROXY_CLASS_TYPE); - // __proxy_class = InternalJavaProxyHelper.initProxyClass( Class.forName(className) ); - - return clazzInit; - } - - private static void generateProxyMethod(Type selfType, Type superType, - ClassVisitor cw, GeneratorAdapter clazzInit, MethodData md) { - if (!md.generateProxyMethod()) return; - - org.objectweb.asm.commons.Method m = md.getMethod(); - Type[] ex = toTypes(md.getExceptions()); - - String field_name = "__mth$" + md.getName() + md.scrambledSignature(); - - // private static JavaProxyMethod __mth$sort$java_util_Comparator; - FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL, - field_name, PROXY_METHOD_TYPE.getDescriptor(), null, null); - fv.visitEnd(); - - // static { ... } initializer block - clazzInit.dup(); - clazzInit.push(m.getName()); - clazzInit.push(m.getDescriptor()); - clazzInit.push(md.isImplemented()); - // JavaProxyMethod initProxyMethod(JavaProxyClass proxyClass, String name, String desc, boolean hasSuper) - clazzInit.invokeStatic(INTERNAL_PROXY_HELPER_TYPE, initProxyMethod); - // __mth$sort$java_util_Comparator = initProxyMethod(...) - 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() - ); - - // - // construct the proxy method - // - GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC, m, null, ex, cw); - - if (md.hasAnnotations()) generateMethodAnnotations(ga, md); - - ga.loadThis(); - 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... - - if (md.isImplemented()) { - ga.dup(); - Label ok = ga.newLabel(); - ga.ifNonNull(ok); - - ga.loadThis(); - ga.loadArgs(); - ga.invokeConstructor(superType, m); - ga.returnValue(); - ga.mark(ok); - } - - ga.loadThis(); - ga.getStatic(selfType, field_name, PROXY_METHOD_TYPE); - - if (m.getArgumentTypes().length == 0) { - // load static empty array - ga.getStatic(JAVA_PROXY_TYPE, "NO_ARGS", toType(Object[].class)); - } else { - // box arguments - ga.loadArgArray(); - } - - Label before = ga.mark(); - - ga.invokeInterface(INVOCATION_HANDLER_TYPE, invoke); - - Label after = ga.mark(); - - ga.unbox(m.getReturnType()); - ga.returnValue(); - - // this is a simple rethrow handler - Label rethrow = ga.mark(); - ga.visitInsn(Opcodes.ATHROW); - - for (int i = 0; i < ex.length; i++) { - ga.visitTryCatchBlock(before, after, rethrow, ex[i].getInternalName()); - } - - ga.visitTryCatchBlock(before, after, rethrow, "java/lang/Error"); - ga.visitTryCatchBlock(before, after, rethrow, "java/lang/RuntimeException"); - - Type thr = toType(Throwable.class); - Label handler = ga.mark(); - 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 (java.lang.Throwable)")); - ga.throwException(); - - ga.visitTryCatchBlock(before, after, handler, "java/lang/Throwable"); - - ga.endMethod(); - - // - // construct the super-proxy method - // - if (md.isImplemented()) { - GeneratorAdapter ga2 = new GeneratorAdapter(Opcodes.ACC_PUBLIC, sm, null, ex, cw); - - ga2.loadThis(); - ga2.loadArgs(); - ga2.invokeConstructor(superType, m); - ga2.returnValue(); - ga2.endMethod(); - } - } - - private static void generateMethodAnnotations(MethodVisitor mv, MethodData md) { - md.getAnnotations().forEach((anno, args) -> { - AnnotationVisitor av = mv.visitAnnotation(Type.getType(anno).getDescriptor(), true); - CodegenUtils.visitAnnotationFields(av, args); - av.visitEnd(); - }); - } - - private static Class[] generateConstructor(Type selfType, Constructor constructor, ClassVisitor cw) { - Class[] superConstructorParameterTypes = constructor.getParameterTypes(); - Class[] newConstructorParameterTypes = new Class[superConstructorParameterTypes.length + 1]; - ArraySupport.copy(superConstructorParameterTypes, newConstructorParameterTypes, 0, superConstructorParameterTypes.length); - newConstructorParameterTypes[superConstructorParameterTypes.length] = JavaProxyInvocationHandler.class; - - int access = Opcodes.ACC_PUBLIC; - String name1 = ""; - 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, toTypes(superConstructorParameterTypes)); - org.objectweb.asm.commons.Method m = new org.objectweb.asm.commons.Method( - name1, Type.VOID_TYPE, toTypes(newConstructorParameterTypes)); - - 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 constructors : - if ( superConstructorVarArgs ) mv.visitAnnotation(Type.getDescriptor(VarArgs.class), true); - GeneratorAdapter ga = new GeneratorAdapter(access, m, mv); - - - ga.loadThis(); - ga.loadArg(superConstructorParameterTypes.length); - - ga.putField(selfType, INVOCATION_HANDLER_FIELD_NAME, INVOCATION_HANDLER_TYPE); - - ga.loadThis(); - ga.loadArgs(0, superConstructorParameterTypes.length); - ga.invokeConstructor(toType(constructor.getDeclaringClass()), super_m); - - // do a void return - ga.returnValue(); - ga.endMethod(); - - return newConstructorParameterTypes; - } - //TODO: dedupe code? - private static Class[] generateRawConstructor(Type selfType, Constructor constructor, ClassVisitor cw) { - Class[] superConstructorParameterTypes = constructor.getParameterTypes(); - - int access = Opcodes.ACC_PUBLIC; - String name1 = ""; - 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, toTypes(superConstructorParameterTypes)); - org.objectweb.asm.commons.Method m = new org.objectweb.asm.commons.Method( - name1, Type.VOID_TYPE, toTypes(superConstructorParameterTypes)); - - 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 constructors : - if ( superConstructorVarArgs ) mv.visitAnnotation(Type.getDescriptor(VarArgs.class), true); - GeneratorAdapter ga = new GeneratorAdapter(access, m, mv); - - - ga.loadThis(); - ga.getStatic(selfType, INVOCATION_HANDLER_BUILDER_FIELD_NAME, INVOCATION_HANDLER_BUILDER_TYPE); - ga.getStatic(selfType, PROXY_CLASS_FIELD_NAME, PROXY_CLASS_TYPE); - ga.loadThis(); - ga.loadArgArray();// NOTE: FOR non-zero args - ga.invokeVirtual(INVOCATION_HANDLER_BUILDER_TYPE, initHandlerClass); - - ga.putField(selfType, INVOCATION_HANDLER_FIELD_NAME, INVOCATION_HANDLER_TYPE); - - - ga.loadThis(); - ga.loadArgs(0, superConstructorParameterTypes.length); - ga.invokeConstructor(toType(constructor.getDeclaringClass()), super_m); - - // do a void return - ga.returnValue(); - ga.endMethod(); - - return superConstructorParameterTypes; - } - - public static final class ClassInvocationHolder { - - private final Ruby runtime; - private final RubyClass clazz; - - ClassInvocationHolder(final Ruby runtime, final RubyClass self) { - this.runtime = runtime; this.clazz = self; - } - - public JavaProxyInvocationHandler init(JavaProxyClass jpc, Object jself, Object[] args) throws Throwable { - final IRubyObject self = clazz.allocate();// NOTE: this has to be done somewhere else in the code/I'm missing lots? - IRubyObject cjp = JavaObject.wrap(runtime, jself); - JavaUtilities.set_java_object(self, self, cjp); - //JavaProxyClass - self.getMetaClass().setInstanceVariable("@java_proxy_class", jpc); - //if ( ((JavaProxy) proxy).object == null ) - final JavaProxyInvocationHandler jpih = new MethodInvocationHandler(runtime, self);//FIXME: needs to be constant and sync with Java$JCreate - jpih.invoke_ctor(args); - return jpih; - } - - - public static IRubyObject sinit(Ruby ruby, RubyClass clazz, Object jself, Object[] args) throws Throwable { - final IRubyObject self = new ConcreteJavaProxy(ruby, clazz, jself); - //clazz.allocate();// NOTE: this has to be done somewhere else in the code/I'm missing lots? - /* - IRubyObject cjp = JavaObject.wrap(runtime, jself); - JavaUtilities.set_java_object(self, self, cjp);*/ - //JavaProxyClass - // self.getMetaClass().setInstanceVariable("@java_proxy_class", jpc); - //if ( ((JavaProxy) proxy).object == null ) - //final JavaProxyInvocationHandler jpih = new MethodInvocationHandler(runtime, self);//FIXME: needs to be constant and sync with Java$JCreate - // jpih.invoke_ctor(args); - //return jpih; - //TODO: initialize? - return self; - } - - } - 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()); - } - - private static String toInternalClassName(String name) { - return name.replace('.', '/'); - } - - 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 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 collectMethods( - final Class superClass, - final Class[] interfaces, - final Set names, Map extraMethods, Map>> annotations) { - - Map methods = new HashMap<>(); - - HashSet allClasses = new HashSet<>(); - addClass(allClasses, methods, superClass, names, annotations); - addInterfaces(allClasses, methods, interfaces, names, annotations); - - if (extraMethods != null) { - for(Map.Entry javaSigs : extraMethods.entrySet()) { - MethodKey mk = new MethodKey(javaSigs.getKey(), javaSigs.getValue()); - if (methods.containsKey(mk)) // already a proper java method there, don't disturb it - continue; - methods.put(mk, new LocalMethodData( - javaSigs.getKey(), - javaSigs.getValue(), - annotations.get(javaSigs.getKey()))); - // no need to call .add as java_signature doesn't support exceptions - } - } - - return methods; - } - - static abstract class MethodData { - - private final Map> annotations; - - public MethodData(Map> annotations) { - this.annotations = annotations; - } - - protected StringBuilder scrambledSignature() { - StringBuilder sb = new StringBuilder(); - for ( Class param : getParameterTypes() ) { - sb.append('$'); - final char[] name = param.getName().toCharArray(); - for (int i = 0; i < name.length; i++) { - final char c; - switch ( c = name[i] ) { - case '.' : sb.append('_'); break; - case '[' : sb.append('1'); break; - case ';' : sb.append('2'); break; - default : sb.append(c); - } - } - } - return sb; - } - - protected boolean hasAnnotations() { - return getAnnotations() != null; - } - - protected Map> getAnnotations() { - return annotations; - } - - protected org.objectweb.asm.commons.Method getMethod() { - return new org.objectweb.asm.commons.Method(getName(), Type - .getType(getReturnType()), getType(getParameterTypes())); - } - - public static Type[] getType(Class[] parameterTypes) { - Type[] result = new Type[parameterTypes.length]; - for (int i = 0; i < parameterTypes.length; i++) { - result[i] = Type.getType(parameterTypes[i]); - } - return result; - } - - protected abstract String getName(); - - protected abstract Class[] getParameterTypes(); - - protected abstract Class[] getExceptions(); - - protected abstract boolean generateProxyMethod(); - - protected abstract void add(Method method); - - abstract Class getReturnType(); - - abstract boolean isFinal(); - - abstract boolean isPrivate(); - - abstract boolean isImplemented(); - } - - /** - * Method data that is new (ie. we can't look it up in parent classes) - */ - static final class LocalMethodData extends MethodData { - - final private String name; - final private Class returnType; - final private Class[] paramTypes; - - public LocalMethodData(final String name, final Class[] signature, final Map> annos) { - super(annos); - this.name = name; - this.returnType = signature[0]; - this.paramTypes = Arrays.copyOfRange(signature, 1, signature.length); - } - - @Override - protected String getName() { - return name; - } - - @Override - protected Class[] getParameterTypes() { - return paramTypes; - } - - @Override - protected Class[] getExceptions(){ - return EMPTY_CLASS_ARRAY; - } - - @Override - protected boolean generateProxyMethod() { - return true; - } - - @Override - protected void add(Method method) { - // Nothing to worry about here, non-overriden methods don't care about exceptions - } - - @Override - Class getReturnType() { - return returnType; - } - - @Override - boolean isFinal() { - return false; - } - - @Override - boolean isPrivate() { - return false; - } - - @Override - boolean isImplemented() { - return false; - } - } - - /** - * Method data that comes from parent classes for methods we - * are overriding - */ - static final class OverridedMethodData extends MethodData { - - final Set methods = new HashSet(); - - final Method mostSpecificMethod; - final Class[] mostSpecificParameterTypes; - - OverridedMethodData(final Method method, final Map> annos) { - super(annos); - this.mostSpecificMethod = method; - this.mostSpecificParameterTypes = mostSpecificMethod.getParameterTypes(); - } - - protected String getName() { - return mostSpecificMethod.getName(); - } - - protected Class[] getParameterTypes() { - return mostSpecificParameterTypes; - } - - protected Class[] getExceptions() { - final IdentityHashMap exceptions = new IdentityHashMap<>(8); - - for ( final Method method : this.methods ) { - Class[] exTypes = method.getExceptionTypes(); - for (int i = 0; i < exTypes.length; i++) { - final Class exType = exTypes[i]; - - if ( exceptions.containsKey(exType) ) continue; - - boolean add = true; - Iterator it = exceptions.keySet().iterator(); - while ( it.hasNext() ) { - final Class curType = it.next(); - - if ( curType.isAssignableFrom(exType) ) { - add = false; - break; - } - if ( exType.isAssignableFrom(curType) ) { - it.remove(); - add = true; - } - } - - if ( add ) exceptions.put(exType, null); - } - } - return exceptions.isEmpty() ? EMPTY_CLASS_ARRAY : exceptions.keySet().toArray(new Class[ exceptions.size() ]); - } - - protected boolean generateProxyMethod() { - return ! isFinal() && ! isPrivate(); - } - - protected void add(Method method) { - methods.add(method); - } - - Class getReturnType() { - return mostSpecificMethod.getReturnType(); - } - - boolean isFinal() { - if ( mostSpecificMethod.getDeclaringClass().isInterface() ) { - return false; - } - return Modifier.isFinal( mostSpecificMethod.getModifiers() ); - } - - boolean isPrivate() { - if ( mostSpecificMethod.getDeclaringClass().isInterface() ) { - return false; - } - return Modifier.isPrivate( mostSpecificMethod.getModifiers() ); - } - - boolean isImplemented() { - if ( mostSpecificMethod.getDeclaringClass().isInterface() ) { - return false; - } - return ! Modifier.isAbstract( mostSpecificMethod.getModifiers() ); - } - } - - static final class MethodKey { - - private final String name; - private final Class[] arguments; - - MethodKey(final Method method) { - this.name = method.getName(); - this.arguments = method.getParameterTypes(); - } - - /** - * Build a key from saved java_signature arguments - * @param name - * @param signature [return value, params...] - */ - MethodKey(final String name, final Class[] signature) { - this.name = name; - this.arguments = Arrays.copyOfRange(signature, 1, signature.length); - } - - @Override - public boolean equals(Object obj) { - if ( obj instanceof MethodKey ) { - MethodKey key = (MethodKey) obj; - return name.equals(key.name) && Arrays.equals(arguments, key.arguments); - } - return false; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public String toString() { - final StringBuilder str = new StringBuilder().append(name); - str.append('('); - final int last = arguments.length - 1; - for ( int i=0; i= 0 ) str.append(arguments[last].getName()); - str.append(')'); - return str.toString(); - } - } - - private static void addInterfaces( - final Set allClasses, - final Map methods, - final Class[] ifaces, - final Set names, Map>> annotations) { - for ( int i = 0; i < ifaces.length; i++ ) { - addInterface(allClasses, methods, ifaces[i], names, annotations); - } - } - - private static void addInterface( - final Set allClasses, - final Map methods, - final Class iface, - final Set names, Map>> annotations) { - if ( allClasses.add(iface) ) { - addMethods(methods, iface, names, annotations); - addInterfaces(allClasses, methods, iface.getInterfaces(), names, annotations); - } - } - - private static void addMethods( - final Map methods, - final Class classOrIface, - final Set names, Map>> annotations) { - final Method[] decMethods = classOrIface.getDeclaredMethods(); - for ( int i = 0; i < decMethods.length; i++ ) { - final Method decMethod = decMethods[i]; - if ( names.contains(decMethod.getName()) ) { - addMethod(methods, decMethod, annotations.get(decMethod.getName())); - } - } - } - - private static void addMethod(final Map methods, final Method method, Map> annotation) { - final int mod = method.getModifiers(); - if ( Modifier.isStatic(mod) || Modifier.isPrivate(mod) ) { - return; - } - - MethodKey methodKey = new MethodKey(method); - MethodData methodData = methods.get(methodKey); - if (methodData == null) { - methodData = new OverridedMethodData(method, annotation); - methods.put(methodKey, methodData); - } - methodData.add(method); - } - - private static void addClass( - final Set allClasses, - final Map methods, - final Class clazz, - final Set names, Map>> annotations) { - if ( allClasses.add(clazz) ) { - addMethods(methods, clazz, names, annotations); - Class superClass = clazz.getSuperclass(); - if ( superClass != null ) { - addClass(allClasses, methods, superClass, names, annotations); - } - addInterfaces(allClasses, methods, clazz.getInterfaces(), names, annotations); - } - } - - private static void validateArgs(Ruby runtime, String targetClassName, Class superClass) { - if ( Modifier.isFinal(superClass.getModifiers()) ) { - throw runtime.newTypeError("cannot extend final class " + superClass.getName()); - } - - if ( ! hasPublicOrProtectedConstructor(superClass) ) { - throw runtime.newTypeError("class " + superClass.getName() + " doesn't have a public or protected constructor"); - } - - String targetPackage = packageName(targetClassName); - - String packagePath = targetPackage.replace('.', '/'); - if (packagePath.startsWith("java")) { - throw runtime.newTypeError("cannot add classes to package " + packagePath); - } - - final Package pkg = Package.getPackage(packagePath); - if ( pkg != null && pkg.isSealed() ) { - throw runtime.newTypeError("package " + pkg + " is sealed"); - } - } - - private static boolean hasPublicOrProtectedConstructor(final Class clazz) { - Constructor[] constructors = clazz.getDeclaredConstructors(); - for ( Constructor constructor : constructors ) { - final int mod = constructor.getModifiers(); - if ( Modifier.isPublic(mod) || Modifier.isProtected(mod) ) { - return true; - } - } - return false; - } - - private static String packageName(String clazzName) { - int idx = clazzName.lastIndexOf('.'); - if ( idx == -1 ) return ""; - return clazzName.substring(0, idx); - } - - private static StringBuilder proxyPackageName(final String className) { - final String proxyPackagePrefix = "org.jruby.proxy"; - final StringBuilder str = new StringBuilder(proxyPackagePrefix.length() + className.length() + 8); - final int idx = className.lastIndexOf('.'); - str.append(proxyPackagePrefix); - return idx == -1 ? str : str.append('.').append(className.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 {} - - @Deprecated - public final JavaProxyClass genProxyClass(final Ruby runtime, ClassLoader loader, - String targetClassName, Class superClass, Class[] interfaces, Set names) - throws InvocationTargetException { - if (loader instanceof ClassDefiningClassLoader) { - return genProxyClass(runtime, (ClassDefiningClassLoader) loader, targetClassName, superClass, interfaces, names, null, null, null); - } - - return genProxyClass(runtime, (ClassDefiningClassLoader) new OneShotClassLoader(loader), targetClassName, superClass, interfaces, names, null, null, null); - } - - @Deprecated - public JavaProxyClass newProxyClass(final Ruby runtime, ClassLoader loader, - String targetClassName, Class superClass, Class[] interfaces, Set names) - throws InvocationTargetException { - if (loader instanceof ClassDefiningClassLoader) { - return newProxyClass(runtime, (ClassDefiningClassLoader) loader, targetClassName, superClass, interfaces, names, null, null, null); - } - - return newProxyClass(runtime, (ClassDefiningClassLoader) new OneShotClassLoader(loader), targetClassName, superClass, interfaces, names, null, null, null); + return ctor.isVarArgs(); } } diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java index d073a019d9d..cc5a6ea5326 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java @@ -84,9 +84,9 @@ public static RubyClass createJavaProxyConstructorClass(Ruby runtime, RubyModule this.declaringProxyClass = proxyClass; this.proxyConstructor = constructor; Class[] parameterTypes = constructor.getParameterTypes(); - this.exportable = parameterTypes.length == 0;// || parameterTypes[parameterTypes.length - 1] != JavaProxyInvocationHandler.class; + this.exportable = parameterTypes.length == 0 || parameterTypes[parameterTypes.length - 1] != RubyClass.class; // see JavaProxyClassFactory's generateConstructor ... - this.actualParameterTypes = ArraySupport.newCopy(parameterTypes, parameterTypes.length - (exportable?0:1)); + this.actualParameterTypes = ArraySupport.newCopy(parameterTypes, parameterTypes.length - (exportable?0:2)); this.actualVarArgs = JavaProxyClassFactory.isVarArgs(proxyConstructor); } @@ -109,7 +109,7 @@ public JavaProxyClass getDeclaringClass() { return declaringProxyClass; } - public final Object newInstance(Object[] args, JavaProxyInvocationHandler handler) + public final Object newInstance(Object[] args, Ruby runtime, IRubyObject clazz) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { final int len = args.length; @@ -117,9 +117,9 @@ public final Object newInstance(Object[] args, JavaProxyInvocationHandler handle throw new IllegalArgumentException("wrong number of parameters"); } if (exportable) - return newInstanceImpl(args, null); + return newInstanceImpl(args, null, null); else - return newInstanceImpl(ArraySupport.newCopy(args, len + 1), handler); // does args[ len ] = handler; + return newInstanceImpl(ArraySupport.newCopy(args, len + 2), runtime, clazz); // does args[ len ] = handler; } /** * For exportable objects, argsPlus1 is not plus one @@ -131,10 +131,13 @@ public final Object newInstance(Object[] args, JavaProxyInvocationHandler handle * @throws IllegalAccessException * @throws InvocationTargetException */ - final Object newInstanceImpl(Object[] argsPlus1, JavaProxyInvocationHandler handler) + final Object newInstanceImpl(Object[] argsPlus1, Ruby runtime, IRubyObject clazz) throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException { if (!exportable) - argsPlus1[ argsPlus1.length - 1 ] = handler; + { + argsPlus1[ argsPlus1.length - 2 ] = runtime; + argsPlus1[ argsPlus1.length - 1 ] = (RubyClass)clazz; + } return proxyConstructor.newInstance(argsPlus1); } @@ -187,9 +190,9 @@ public RubyObject new_instance2(IRubyObject[] args, Block unusedBlock) { final IRubyObject self = args[0]; final Object[] convertedArgs = convertArguments((RubyArray) args[1]); // constructor arguments - JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); + //JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); try { - return JavaObject.wrap(runtime, newInstance(convertedArgs, handler)); + return JavaObject.wrap(runtime, newInstance(convertedArgs, runtime, self)); } catch (Exception ex) { throw mapInstantiationException(runtime, ex); } } @@ -197,9 +200,9 @@ public RubyObject new_instance2(IRubyObject[] args, Block unusedBlock) { public JavaObject newInstance(final IRubyObject self, Object[] args) throws RaiseException { final Ruby runtime = getRuntime(); - JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); + //JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); try { - return JavaObject.wrap(runtime, newInstance(args, handler)); + return JavaObject.wrap(runtime, newInstance(args, runtime, self)); } catch (Throwable ex) { throw mapInstantiationException(runtime, ex); } } @@ -207,10 +210,10 @@ public JavaObject newInstance(final IRubyObject self, Object[] args) throws Rais public final JavaObject newInstance(final IRubyObject self, IRubyObject[] args) throws RaiseException { final Ruby runtime = getRuntime(); - final Object[] javaArgsPlus1 = RubyToJavaInvoker.convertArguments(this, args, (exportable?0:+1)); - JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); + final Object[] javaArgsPlus1 = RubyToJavaInvoker.convertArguments(this, args, (exportable?0:+2)); + //JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); try { - return JavaObject.wrap(runtime, newInstanceImpl(javaArgsPlus1, handler)); + return JavaObject.wrap(runtime, newInstanceImpl(javaArgsPlus1, runtime, self)); } catch (Throwable ex) { throw mapInstantiationException(runtime, ex); } } @@ -218,10 +221,10 @@ public final JavaObject newInstance(final IRubyObject self, IRubyObject[] args) public final JavaObject newInstance(final IRubyObject self, IRubyObject arg0) throws RaiseException { final Ruby runtime = getRuntime(); - final Object[] javaArgsPlus1 = RubyToJavaInvoker.convertArguments(this, arg0, (exportable?0:+1)); - JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); + final Object[] javaArgsPlus1 = RubyToJavaInvoker.convertArguments(this, arg0, (exportable?0:+2)); + //JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); try { - return JavaObject.wrap(runtime, newInstanceImpl(javaArgsPlus1, handler)); + return JavaObject.wrap(runtime, newInstanceImpl(javaArgsPlus1, runtime, self)); } catch (Throwable ex) { throw mapInstantiationException(runtime, ex); } } @@ -236,7 +239,7 @@ private static RaiseException mapInstantiationException(final Ruby runtime, fina ex.initCause(e); throw ex; } - +/* static final class MethodInvocationHandler implements JavaProxyInvocationHandler { private final Ruby runtime; @@ -288,7 +291,7 @@ private IRubyObject invokeRuby(final DynamicMethod method, final JavaProxyMethod throw runtime.newArgumentError(newArgs.length, arity); } - } + }*/ @JRubyMethod(required = 1, optional = 1) public RubyObject new_instance(IRubyObject[] args, Block block) { @@ -306,9 +309,9 @@ public RubyObject new_instance(IRubyObject[] args, Block block) { final Object[] convertedArgs = convertArguments((RubyArray) args[0]); - JavaProxyInvocationHandler handler = new ProcInvocationHandler(runtime, proc); + //JavaProxyInvocationHandler handler = new ProcInvocationHandler(runtime, proc); try { - return JavaObject.wrap(runtime, newInstance(convertedArgs, handler)); + return JavaObject.wrap(runtime, newInstance(convertedArgs, runtime, proc)); } catch (Exception e) { RaiseException ex = runtime.newArgumentError("Constructor invocation failed: " + e.getMessage()); @@ -316,7 +319,7 @@ public RubyObject new_instance(IRubyObject[] args, Block block) { throw ex; } } - +/* private static final class ProcInvocationHandler implements JavaProxyInvocationHandler { private final Ruby runtime; @@ -347,7 +350,7 @@ public void invoke_ctor(Object[] args) throws Throwable throw new UnsupportedOperationException(); } - } + }*/ private Object[] convertArguments(final RubyArray arguments) { final int argsSize = arguments.size(); diff --git a/core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxy.java b/core/src/main/java/org/jruby/javasupport/proxy/ReifiedJavaProxy.java similarity index 82% rename from core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxy.java rename to core/src/main/java/org/jruby/javasupport/proxy/ReifiedJavaProxy.java index 5ca2cde4e84..260eaa6f415 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/InternalJavaProxy.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/ReifiedJavaProxy.java @@ -28,25 +28,25 @@ package org.jruby.javasupport.proxy; +import org.jruby.RubyClass; +import org.jruby.java.codegen.Reified; +import org.jruby.runtime.builtin.IRubyObject; + /** * Interface is implemented by proxies generated from a JavaProxyClass. * - * @see JavaProxyClassFactory + * @see RubyClass.ConcreteJavaReifier */ -public interface InternalJavaProxy { +public interface ReifiedJavaProxy extends Reified { /** * @return the corresponding JavaProxyClass */ - JavaProxyClass ___getProxyClass(); + JavaProxyClass ___jruby$proxyClass(); /** - * @return the invocation handler + * @return the ruby object associated with this java object */ - JavaProxyInvocationHandler ___getInvocationHandler(); - - // NOTE: used in JavaProxyClassFactory indirectly - // ... getStatic(JAVA_PROXY_TYPE, "NO_ARGS", ...) - static final Object[] NO_ARGS = new Object[0]; + IRubyObject ___jruby$rubyObject(); } diff --git a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java index 245b37bef7a..162f11d609a 100644 --- a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java +++ b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java @@ -8,4 +8,6 @@ public class JavaClassConfiguration // for java proxies public boolean allCtors = false; + public boolean rubyConstructable = true; // + public boolean splitSuper = true; } diff --git a/core/src/main/java/org/jruby/runtime/Helpers.java b/core/src/main/java/org/jruby/runtime/Helpers.java index 9fef24b2c4e..3f5091d6c4a 100644 --- a/core/src/main/java/org/jruby/runtime/Helpers.java +++ b/core/src/main/java/org/jruby/runtime/Helpers.java @@ -54,7 +54,7 @@ import org.jruby.ir.runtime.IRRuntimeHelpers; import org.jruby.javasupport.JavaClass; import org.jruby.javasupport.JavaUtil; -import org.jruby.javasupport.proxy.InternalJavaProxy; +import org.jruby.javasupport.proxy.ReifiedJavaProxy; import org.jruby.parser.StaticScope; import org.jruby.parser.StaticScopeFactory; import org.jruby.runtime.JavaSites.HelpersSites; @@ -943,8 +943,8 @@ public static boolean checkJavaException(final IRubyObject wrappedEx, final Thro } if (catchable instanceof RubyClass && JavaClass.isProxyType(context, (RubyClass) catchable)) { - if ( ex instanceof InternalJavaProxy ) { // Ruby sub-class of a Java exception type - final IRubyObject target = ((InternalJavaProxy) ex).___getInvocationHandler().getOrig(); + if ( ex instanceof ReifiedJavaProxy ) { // Ruby sub-class of a Java exception type + final IRubyObject target = ((ReifiedJavaProxy) ex).___jruby$rubyObject(); if ( target != null ) return ((RubyClass) catchable).isInstance(target); } return ((RubyClass) catchable).isInstance(wrappedEx); diff --git a/lib/ruby/stdlib/jruby/core_ext/class.rb b/lib/ruby/stdlib/jruby/core_ext/class.rb index 3d87f54b875..c34b366e1da 100644 --- a/lib/ruby/stdlib/jruby/core_ext/class.rb +++ b/lib/ruby/stdlib/jruby/core_ext/class.rb @@ -168,6 +168,24 @@ def java_field(signature_source) annotations = signature.annotations add_field_annotation signature.name, annotations if annotations end + + ## + # Valid options: + # call_init: true/false + # generate: :all/:explicit + # configure_java_class methods: :explicit/:all, call_init: true/false, java_constructable: true/false, ctors: :all/:minimal + def configure_java_class(**kwargs) + self_r = JRuby.reference0(self) + config = self_r.class_config + config.allMethods = kwargs[:methods] == :explicit if kwargs.has_key? :methods + config.callInitialize = kwargs[:call_init] if kwargs.has_key? :call_init + config.javaConstructable = kwargs[:java_constructable] if kwargs.has_key? :java_constructable + config.allCtors = kwargs[:ctors] == :all if kwargs.has_key? :ctors + config.rubyConstructable = kwargs[:ruby_constructable] if kwargs.has_key? :ruby_constructable + config.splitSuper = kwargs[:split_super] if kwargs.has_key? :split_super + self_r.class_config = config + kwargs + end def java_annotation(anno) warn "java_annotation is deprecated. Use java_signature '@#{anno} ...' instead. Called from: #{caller.first}" From 2998f7259d7611c5caa35d91f98fff6114fe035f Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Sat, 24 Oct 2020 03:20:56 -0400 Subject: [PATCH 04/49] Remove test class reference --- core/src/main/java/org/jruby/RubyClass.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index be737ec8edc..38a176c0265 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -110,7 +110,6 @@ import org.objectweb.asm.Label; import org.objectweb.asm.commons.GeneratorAdapter; -import com.test.patrick.AbstractClass; /** * From 4c611629ed49c2dd640c417df788e5ca765696be Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Sun, 1 Nov 2020 03:44:36 -0500 Subject: [PATCH 05/49] Reasonable multi-signature support (codegen, helpers missing) and horrifying AST rewriting to de-super() --- core/src/main/java/org/jruby/RubyClass.java | 227 +++++++++++++++-- core/src/main/java/org/jruby/ir/IRMethod.java | 5 + .../java/codegen/RealClassGenerator.java | 236 +++++++++++++++++- .../main/java/org/jruby/javasupport/Java.java | 5 + .../javasupport/proxy/JavaProxyClass.java | 4 +- 5 files changed, 449 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index 38a176c0265..2b63cccce69 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -32,11 +32,15 @@ package org.jruby; import org.jruby.javasupport.JavaClass; +import org.jruby.javasupport.JavaConstructor; import org.jruby.javasupport.ext.JavaExtensions; import org.jruby.javasupport.proxy.JavaProxyClass; +import org.jruby.javasupport.proxy.JavaProxyConstructor; import org.jruby.javasupport.proxy.ReifiedJavaProxy; import org.jruby.javasupport.util.JavaClassConfiguration; import org.jruby.parser.StaticScope; +import org.jruby.parser.StaticScopeFactory; +import org.jruby.parser.StaticScope.Type; import org.jruby.runtime.Arity; import org.jruby.runtime.JavaSites; import org.jruby.runtime.callsite.CachingCallSite; @@ -73,11 +77,30 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.jcodings.Encoding; import org.jruby.anno.JRubyClass; import org.jruby.anno.JRubyMethod; +import org.jruby.ast.ArgsNode; +import org.jruby.ast.ArrayNode; +import org.jruby.ast.BlockNode; +import org.jruby.ast.DefNode; +import org.jruby.ast.DefnNode; +import org.jruby.ast.FCallNode; +import org.jruby.ast.InstAsgnNode; +import org.jruby.ast.InstVarNode; +import org.jruby.ast.IterNode; +import org.jruby.ast.ListNode; +import org.jruby.ast.Node; +import org.jruby.ast.ReturnNode; +import org.jruby.ast.SuperNode; +import org.jruby.ast.visitor.AbstractNodeVisitor; +import org.jruby.ast.visitor.NodeVisitor; import org.jruby.compiler.impl.SkinnyMethodAdapter; import org.jruby.exceptions.RaiseException; +import org.jruby.internal.runtime.AbstractIRMethod; import org.jruby.internal.runtime.methods.DynamicMethod; +import org.jruby.internal.runtime.methods.MixedModeIRMethod; +import org.jruby.ir.IRMethod; import org.jruby.java.codegen.RealClassGenerator; import org.jruby.java.codegen.Reified; import org.jruby.java.proxies.ConcreteJavaProxy; @@ -91,7 +114,10 @@ import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.ObjectMarshal; import org.jruby.runtime.PositionAware; +import org.jruby.runtime.Signature; import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.Visibility; + import static org.jruby.runtime.Visibility.*; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.runtime.callsite.CacheEntry; @@ -1454,14 +1480,15 @@ interface Reificator { private abstract class BaseReificator implements Reificator { - protected static final String RUBY_INIT_ARGS_FIELD = "$rubyInitArgs"; - protected static final String RUBY_OBJECT_FIELD = "$rubyObject"; + public static final String RUBY_INIT_ARGS_FIELD = "$rubyInitArgs"; + public static final String RUBY_OBJECT_FIELD = "$rubyObject"; protected static final String RUBY_PROXY_CLASS_FIELD = "$rubyProxyClass"; + public static final String RUBY_CTORS_FIELD = "$rubyCtors"; - protected final Class reifiedParent; + public final Class reifiedParent; protected final String javaName; - protected final String javaPath; - protected final String rubyName; + public final String javaPath; + public final String rubyName; protected final String rubyPath; protected final JavaClassConfiguration jcc; protected final ClassWriter cw; @@ -1490,7 +1517,8 @@ public byte[] reify() { cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, "rubyClass", ci(RubyClass.class), null, null); if (!isRubyObject()) cw.visitField(ACC_PRIVATE, RUBY_OBJECT_FIELD, rubyName, null, null); if (!isRubyObject()) cw.visitField(ACC_PRIVATE, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class), null, null); - if (!isRubyObject()) cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class), null, null); + if (!isRubyObject()) cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class), null, null); + if (!isRubyObject()) cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_CTORS_FIELD, ci(JavaConstructor[].class), null, null); reifyConstructors(); @@ -1547,7 +1575,7 @@ protected void loadRubyObject(SkinnyMethodAdapter m) } - protected void rubycall(SkinnyMethodAdapter m, String signature) + public void rubycall(SkinnyMethodAdapter m, String signature) { m.invokevirtual(rubyPath, "callMethod", signature); } @@ -1585,7 +1613,7 @@ protected void allocAndInitialize(SkinnyMethodAdapter m, boolean initIfAllowed) m.end(); } - protected Class[] join(Class[] base, Class... extra) + public Class[] join(Class[] base, Class... extra) { Class[] more = ArraySupport.newCopy(base, base.length + extra.length); ArraySupport.copy(extra, more, base.length, extra.length); @@ -1595,7 +1623,9 @@ protected Class[] join(Class[] base, Class... extra) private class MethodReificator extends BaseReificator { - MethodReificator(Class reifiedParent, String javaName, String javaPath, String rubyName, String rubyPath) { + private Integer clinitKey; + + MethodReificator(Class reifiedParent, String javaName, String javaPath, String rubyName, String rubyPath) { super(reifiedParent, javaName, javaPath, rubyName, rubyPath); } @@ -1872,17 +1902,29 @@ public void reifyClinit(SkinnyMethodAdapter m) { /// i0, o[], i1, o[] // [] i0 [], i1 - m.ldc(1); // rubyclass index - m.ldc(JavaProxyClass.addStaticInitLookup(runtime, RubyClass.this)); + m.pushInt(1); // rubyclass index + this.clinitKey = JavaProxyClass.addStaticInitLookup(getExtraClinitInfo()); + m.ldc(clinitKey);//TODO: add ctors m.invokestatic(p(JavaProxyClass.class), "getStaticInitLookup", sig(Object[].class, int.class)); + extraClinitLookup(m); m.dup_x1(); // array - m.ldc(0); // ruby index + m.pushInt(0); // ruby index m.aaload(); // extract ruby m.checkcast(p(Ruby.class)); m.putstatic(javaPath, "ruby", ci(Ruby.class)); m.aaload(); // extract rubyclass m.checkcast(p(RubyClass.class)); m.putstatic(javaPath, "rubyClass", ci(RubyClass.class)); + } + + protected Object[] getExtraClinitInfo() + { + return new Object[]{runtime, RubyClass.this}; + } + + protected void extraClinitLookup(SkinnyMethodAdapter m) + { + } protected Class[] searchInheritedSignatures(String id) @@ -1897,10 +1939,104 @@ protected void generateObjectBarrier(SkinnyMethodAdapter m) } // class MethodReificator - private class ConcreteJavaReifier extends MethodReificator + //public or private? + public class ConcreteJavaReifier extends MethodReificator { - boolean rubyctor = false; + private final class FlatExtractor extends AbstractNodeVisitor + { + private final DefNode def; + boolean found = false; + BlockNode bn = null; + int level = 0; + boolean error = false; + boolean foundsuper = false; + + private FlatExtractor(DefNode def) + { + this.def = def; + } + + @Override + protected Node defaultVisit(Node node) + { + if (node == null) return null; + + if (error) + return null; + + level++; + + node.childNodes().forEach(this::defaultVisit); + level--; + + return node; + } + + @Override + public Node visitBlockNode(BlockNode node) + { + if (error) + return null; + if (found) + return node; + + BlockNode replacement = new BlockNode(node.getPosition()); + + level++; + + boolean seenReturn = false; + + for (Node child : node.children()) + { + Node newc = child.accept(this); + if (found) + { + if (seenReturn) + { + bn.add(newc); + continue; + } + else + { + seenReturn = true; + } + } + replacement.add(newc); + } + level--; + + return replacement; + } + + @Override + public Node visitSuperNode(SuperNode node) + { + foundsuper = true; + if (level != 1) + { + error = true; + return null; + } + ArrayNode ret = new ArrayNode(node.getPosition(), node.getArgsNode());// TODO: block args! + ArgsNode an = new ArgsNode(node.getPosition(), null, null, null, null,null); + StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); + scope.setSignature(Signature.from(an)); + ret.add( + new FCallNode(node.getPosition(), RubySymbol.newSymbol(runtime, "lambda"), null, + new IterNode(node.getPosition(), + an, + bn = new BlockNode(node.getPosition()), + scope, 1))); + found = true; + return new ReturnNode(node.getPosition(), ret); + // TODO Auto-generated method stub + //return super.visitSuperNode(node); + } + } + + boolean rubyctor = false; boolean simpleAlloc = false; + JavaConstructor[] savedCtors = null; ConcreteJavaReifier(Class reifiedParent, String javaName, String javaPath) { @@ -1926,6 +2062,12 @@ protected boolean isRubyObject() return false; } + @Override + protected Object[] getExtraClinitInfo() + { + return new Object[]{runtime, RubyClass.this, savedCtors}; + } + @Override public void reifyClinit(SkinnyMethodAdapter m) { @@ -1943,6 +2085,15 @@ public void reifyClinit(SkinnyMethodAdapter m) m.putstatic(javaPath, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class)); // Note: no end, that's in the parent call } + + protected void extraClinitLookup(SkinnyMethodAdapter m) + { + m.dup(); // don't modify the stack below us, use a new copy + m.pushInt(2); // ctor fields + m.aaload(); // extract ctors + m.checkcast(p(JavaConstructor[].class)); + m.putstatic(javaPath, RUBY_CTORS_FIELD, ci(JavaConstructor[].class)); + } protected Class[] searchInheritedSignatures(String id) { @@ -1975,6 +2126,36 @@ protected void reifyConstructors() zeroArg = Optional.of(constructor); } } + DynamicMethod dm = searchMethod("initialize"); + DefNode def = ((IRMethod)((AbstractIRMethod)dm).getIRScope()).desugar(); + FlatExtractor flat = new FlatExtractor(def); + Node body = def.getBodyNode().accept(flat); + if (!flat.foundsuper) + System.err.println("NO SUPER"); + if (flat.error) + System.err.println("error"); + System.err.println(def.toString()); + DefNode rdnbody = new DefnNode(def.getBodyNode().getPosition(), RubySymbol.newSymbol(runtime, "j_initialize"), def.getArgsNode(), def.getScope(), body, def.getEndLine()); + System.err.println(rdnbody.toString()); + + + IRMethod irm = ((IRMethod)((AbstractIRMethod)dm).getIRScope()); + irm.builtInterpreterContext(); + +irm = new IRMethod(irm.getManager(), irm.getLexicalParent(), rdnbody, new ByteList("j_initialize".getBytes(), runtime.getEncodingService().getJavaDefault()), true, irm.getLine(), irm.getStaticScope(), irm.getCoverageMode()); + dm = new MixedModeIRMethod(irm, Visibility.PUBLIC, RubyClass.this); + + RubyClass.this.addMethod("j_initialize", dm); + + dm = searchMethod("j_initialize"); + if (dm instanceof AbstractIRMethod) + { + IRMethod irm2 = ((IRMethod)((AbstractIRMethod)dm).getIRScope()); + + System.err.println(irm2.desugar().toString()); + irm2.builtInterpreterContext(); + System.err.println("found A SUPER!!!!!"); + } if (zeroArg.isPresent()) { @@ -2038,10 +2219,23 @@ protected void reifyConstructors() if (candidates.size() > 0 && jcc.allCtors) //TODO: doc: implies javaConstructable? { + List savedCtorsList = new ArrayList<>(candidates.size()); + for (Constructor constructor : candidates) + { + savedCtorsList.add(new JavaConstructor(runtime, constructor)); + } + savedCtors = savedCtorsList.toArray(new JavaConstructor[savedCtorsList.size()]); for (Constructor constructor : candidates) { + //TODO: do matching for zero arg? if (zeroArg.isPresent() && constructor == zeroArg.get()) continue; - + + if (jcc.rubyConstructable) + RealClassGenerator.makeConcreteConstructorSwitch(cw, true, true, this, constructor.getParameterTypes(), savedCtors); + + if (jcc.javaConstructable) + RealClassGenerator.makeConcreteConstructorSwitch(cw, false, true, this, constructor.getParameterTypes(), savedCtors); + /* if (jcc.rubyConstructable) { rubyctor = true; @@ -2103,7 +2297,8 @@ protected void reifyConstructors() m.voidreturn(); m.end(); - } + }*/ + } } } diff --git a/core/src/main/java/org/jruby/ir/IRMethod.java b/core/src/main/java/org/jruby/ir/IRMethod.java index 241c19bd11e..ad7d2beecc7 100644 --- a/core/src/main/java/org/jruby/ir/IRMethod.java +++ b/core/src/main/java/org/jruby/ir/IRMethod.java @@ -40,6 +40,11 @@ public IRMethod(IRManager manager, IRScope lexicalParent, DefNode defn, ByteList staticScope.setIRScope(this); } } + + public DefNode desugar() + { + return defNode; + } @Override public boolean hasBeenBuilt() { diff --git a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java index ed5a03651cf..acb13dab9a7 100644 --- a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java +++ b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java @@ -40,13 +40,19 @@ import java.util.Map; import java.util.Set; import org.jruby.Ruby; +import org.jruby.RubyArray; import org.jruby.RubyBasicObject; import org.jruby.RubyClass; +import org.jruby.RubyClass.ConcreteJavaReifier; import org.jruby.RubyModule; import org.jruby.ast.executable.RuntimeCache; import org.jruby.compiler.impl.SkinnyMethodAdapter; import org.jruby.compiler.util.BasicObjectStubGenerator; import org.jruby.internal.runtime.methods.DynamicMethod; +import org.jruby.java.proxies.ConcreteJavaProxy; +import org.jruby.javasupport.Java; +import org.jruby.javasupport.Java.JCreateMethod; +import org.jruby.javasupport.JavaConstructor; import org.jruby.javasupport.JavaUtil; import org.jruby.runtime.Block; import org.jruby.runtime.ThreadContext; @@ -776,6 +782,10 @@ public static void coerceArgumentsToRuby(SkinnyMethodAdapter mv, Class[] paramTy } public static void coerceResultAndReturn(SkinnyMethodAdapter mv, Class returnType) { + coerceResult(mv, returnType, true); + } + + public static void coerceResult(SkinnyMethodAdapter mv, Class returnType, boolean doReturn) { // if we expect a return value, unwrap it if (returnType != void.class) { // TODO: move the bulk of this logic to utility methods @@ -785,38 +795,38 @@ public static void coerceResultAndReturn(SkinnyMethodAdapter mv, Class returnTyp mv.invokeinterface(p(IRubyObject.class), "toJava", sig(Object.class, Class.class)); mv.checkcast(p(Boolean.class)); mv.invokevirtual(p(Boolean.class), "booleanValue", sig(boolean.class)); - mv.ireturn(); + if (doReturn) mv.ireturn(); } else { mv.getstatic(p(getBoxType(returnType)), "TYPE", ci(Class.class)); mv.invokeinterface(p(IRubyObject.class), "toJava", sig(Object.class, Class.class)); if (returnType == byte.class) { mv.checkcast(p(Number.class)); mv.invokevirtual(p(Number.class), "byteValue", sig(byte.class)); - mv.ireturn(); + if (doReturn) mv.ireturn(); } else if (returnType == short.class) { mv.checkcast(p(Number.class)); mv.invokevirtual(p(Number.class), "shortValue", sig(short.class)); - mv.ireturn(); + if (doReturn) mv.ireturn(); } else if (returnType == char.class) { mv.checkcast(p(Character.class)); mv.invokevirtual(p(Character.class), "charValue", sig(char.class)); - mv.ireturn(); + if (doReturn) mv.ireturn(); } else if (returnType == int.class) { mv.checkcast(p(Number.class)); mv.invokevirtual(p(Number.class), "intValue", sig(int.class)); - mv.ireturn(); + if (doReturn) mv.ireturn(); } else if (returnType == long.class) { mv.checkcast(p(Number.class)); mv.invokevirtual(p(Number.class), "longValue", sig(long.class)); - mv.lreturn(); + if (doReturn) mv.lreturn(); } else if (returnType == float.class) { mv.checkcast(p(Number.class)); mv.invokevirtual(p(Number.class), "floatValue", sig(float.class)); - mv.freturn(); + if (doReturn) mv.freturn(); } else if (returnType == double.class) { mv.checkcast(p(Number.class)); mv.invokevirtual(p(Number.class), "doubleValue", sig(double.class)); - mv.dreturn(); + if (doReturn) mv.dreturn(); } } } else { @@ -827,9 +837,9 @@ public static void coerceResultAndReturn(SkinnyMethodAdapter mv, Class returnTyp p(IRubyObject.class), "toJava", sig(Object.class, Class.class)); } mv.checkcast(p(returnType)); - mv.areturn(); + if (doReturn) mv.areturn(); } - } else { + } else if (doReturn) { mv.voidreturn(); } } @@ -844,6 +854,212 @@ public static int calcBaseIndex(final Class[] params, int baseIndex) { } return baseIndex; } + //:TODO: Add IRubyobject ctor? + // shouls this be in in this spot? + public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby, boolean callsInit, ConcreteJavaReifier cjr, Class[] ctorTypes, JavaConstructor[] constructors) + { + String sig = hasRuby ? sig(void.class, cjr.join(ctorTypes, Ruby.class, RubyClass.class)) : sig(void.class, ctorTypes); + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig, null, null); + m.aload(0); // uninitialized this + + // set args for init + // Ruby varRubyIndex = ruby; + final int baseIndex = RealClassGenerator.calcBaseIndex(ctorTypes, 1); + final int rubyIndex = baseIndex; + final int rubyArrayIndex = baseIndex+2; + final int rubyContinuation = baseIndex+3; + final int rubyInitArgs = baseIndex+4; + m.dup(); // uninitialized this + if (!hasRuby) // if java, extract and pretend we got a call with ruby on a local + { + m.getstatic(cjr.javaPath, "ruby", ci(Ruby.class)); + m.astore(rubyIndex); // save ruby in local + } + // this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; + RealClassGenerator.coerceArgumentsToRuby(m, ctorTypes, rubyIndex); + m.dup(); + m.astore(rubyInitArgs); + m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + + //this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); + { + //TODO: isInit? + m.newobj(p(ConcreteJavaProxy.class)); + m.dup(); // rubyobject + m.aload(rubyIndex); // ruby + if (hasRuby) + m.aload(rubyIndex+1); // rubyclass + else + m.getstatic(cjr.javaPath, "rubyClass", ci(RubyClass.class)); // rubyclass + m.invokespecial(p(ConcreteJavaProxy.class), "", sig(void.class, Ruby.class, RubyClass.class)); + m.dup(); // rubyobject + m.aload(0); // uninitialized this + m.swap(); + m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_OBJECT_FIELD, cjr.rubyName); + } + //IRubyObject c = this$rubyObject..callMethod("j_initialize", this.$rubyInitArgs); + if (callsInit) //TODO: should init be called when super calls an abstract method we implement? + { + m.ldc("j_initialize"); // TODO: consts? + m.aload(rubyInitArgs); + cjr.rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class) ); //pushes rubyobject + + //RubyArray ra = c.convertToArray(); + m.invokeinterface(p(IRubyObject.class), "convertToArray", sig(RubyArray.class)); + m.dup(); // rubyarray (results of j_initialize) + // m.dup(); // rubyarray (results of j_initialize) +// m.astore(rubyArrayIndex); + + + //// c = ra.entry(0); + ////IRubyObject continuation = ra.entry(1); + m.iconst_0(); // ..., rubyarray, rubyarray, 0 + m.swap(); // ..., rubyarray, 0, rubyarray + m.iconst_1(); // ..., rubyarray, 0, rubyarray, 1 + m.invokevirtual(p(RubyArray.class), "entry", sig(IRubyObject.class, int.class)); + m.astore(rubyContinuation); // ..., rubyarray, 0 + m.invokevirtual(p(RubyArray.class), "entry", sig(IRubyObject.class, int.class)); + // top of stack is now the arg list ruby array + + //// switch(Java.JCreateMethod.forTypes(constructors, c)) + m.dup(); + m.getstatic(cjr.javaPath, cjr.RUBY_CTORS_FIELD, ci(JavaConstructor[].class)); + m.swap(); // ... arglist, ctors, arglist //TODO: reorder args? + m.invokestatic(p(JCreateMethod.class), "forTypes", sig(int.class, JavaConstructor[].class, IRubyObject.class)); + // ..., arglist, index + Label defaultLabel = new Label(); + Label[] cases = new Label[constructors.length+1]; //note: offset by one from index + for (int i = 0; i <= constructors.length; i++) + { + cases[i] = new Label(); + } + Label endofswitch = new Label(); + GeneratorAdapter ga = RealClassGenerator.makeGenerator(m); + m.tableswitch(-1, constructors.length-1, defaultLabel, cases); + { + // default: throw new IllegalStateException("corruption"?) //TODO: type & message + m.label(defaultLabel); + m.newobj(p(IllegalStateException.class)); + m.dup(); + m.ldc("No available superconstructors match that type signature"); + m.invokespecial(p(IllegalStateException.class), "", sig(void.class, String.class)); + m.athrow(); + + // -1: super(*args), from `super` (no args) in ruby + m.label(cases[0]); + m.aload(0); + ga.loadArgs(0, ctorTypes.length); + m.invokespecial(p(cjr.reifiedParent), "", sig(void.class, ctorTypes)); + m.pop(); //c + m.go_to(endofswitch); + + // case n: + for (int i = 0; i < constructors.length; i++) + { + // + // + //thing(ra.entry(0).toJava(Integer.TYPE).longValue()); + m.label(cases[i+1]); // skip -1 + + //ra = c.convertToArray(); + // Note: can't pull this code up above the switch because of nils + m.invokeinterface(p(IRubyObject.class), "convertToArray", sig(RubyArray.class)); + m.astore(rubyArrayIndex); // .... + + // setup super call + m.aload(0); //..., uninitialized this + + Class[] destType = constructors[i].getParameterTypes(); + + // coerce args. No error checking as the forTypes() call should have done that for us + for (int argi = 0; argi < destType.length; argi++) + { + m.aload(rubyArrayIndex); + m.pushInt(argi); + m.invokevirtual(p(RubyArray.class), "entry", sig(IRubyObject.class, int.class)); + RealClassGenerator.coerceResult(m, destType[argi], false); + } + // super(*args) + m.invokespecial(p(cjr.reifiedParent), "", sig(void.class, destType)); + m.go_to(endofswitch); + } + } + + m.label(endofswitch); + + } + else + m.pop(); // TODO: ??? + + // TODO: is this field still needed? + // clear args to avoid holding refs to args + //// this.$rubyInitArgs = null; + m.aload(0); // initialized this + m.aconst_null(); + m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + + + //implied: if (this.$rubyObject.getObject() == null) // only checked on non-ctor paths + //(this.$rubyObject.setObject(this)) + + m.aload(0); // initialized this + m.dup(); + m.getfield(cjr.javaPath, ConcreteJavaReifier.RUBY_OBJECT_FIELD, cjr.rubyName);// ..., this, rubyobj + m.checkcast(p(ConcreteJavaProxy.class)); + m.swap(); // ..., rubyobject, this + m.invokevirtual(p(ConcreteJavaProxy.class), "setObject", sig(void.class, Object.class)); + + // finish init if started + if (callsInit) + { + //continuation.callMethod(ruby.getTheadContext(), "call") + m.aload(rubyContinuation); + m.aload(rubyIndex); + m.invokevirtual(p(Ruby.class), "getCurrentContext", sig(ThreadContext.class)); + m.ldc("call"); + // not rubycall as using IRubyObject vs RubyBasicObject + m.invokeinterface(p(IRubyObject.class), "callMethod", sig(IRubyObject.class, ThreadContext.class, String.class)); + m.pop(); + } + + + m.voidreturn(); + m.end(); + /* + * public Demo(int var1, String var2) { + Ruby var3 = ruby; + this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; + this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); + IRubyObject c = this$rubyObject..callMethod("j_initialize", this.$rubyInitArgs); + RubyArray ra = c.convertToArray(); + c = ra.entry(0); + IRubyObject continuation = ra.entry(1); + switch(Java.JCreateMethod.forTypes(constructors, c)) + { + case -1: + ra = c.convertToArray(); + thing(ra.entry(0).toJava(Integer.TYPE).longValue()); + //return; + break; + case 0: + //ra = c.convertToArray(); + thing(id); + break; + default: + throw new RuntimeException("NANANANANANA"); + } + + if (this.$rubyObject.getObject() == null) { + (this.$rubyObject.setObject(this)) + //?#? this.$rubyObject.callMethod("initialize", this.$rubyInitArgs); + } + this.$rubyInitArgs = null; + + continuation.callMethod(null, "call") + + }*/ + + } public static GeneratorAdapter makeGenerator(SkinnyMethodAdapter m) { diff --git a/core/src/main/java/org/jruby/javasupport/Java.java b/core/src/main/java/org/jruby/javasupport/Java.java index 0050de85dbe..d606d39dd08 100644 --- a/core/src/main/java/org/jruby/javasupport/Java.java +++ b/core/src/main/java/org/jruby/javasupport/Java.java @@ -642,6 +642,11 @@ public final IRubyObject call(ThreadContext context, IRubyObject self, RubyModul JavaObject newObject = matching.newInstance(self, arg0); return newObject; } + + public static int forTypes(JavaConstructor[] constructors, IRubyObject type) + { + return 1; //TODO: implement + } @Override public final IRubyObject call(final ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) { diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java index 2978fab4747..a6a0c68c84b 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java @@ -644,10 +644,10 @@ public static JavaProxyClass setProxyClassReified(final Ruby runtime, final Ruby private static final AtomicInteger lookupIdNext = new AtomicInteger(0); private static final Map lookup = new ConcurrentHashMap<>(); - public static Integer addStaticInitLookup(Ruby runtime, RubyClass clazz) + public static Integer addStaticInitLookup(Object... objects) { int val = lookupIdNext.incrementAndGet(); - lookup.put(val, new Object[]{runtime, clazz}); + lookup.put(val, objects); return val; } // used by reified code in RubyClass From d1f30e03b1cf9f618bbcb2f237ebdccac160ffd6 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Wed, 11 Nov 2020 20:24:09 -0500 Subject: [PATCH 06/49] Refactorings & actually detect which ctor to call. Also supports super w/o args --- core/src/main/java/org/jruby/RubyClass.java | 317 +++++++----------- .../java/codegen/RealClassGenerator.java | 71 ++-- .../jruby/java/proxies/ConcreteJavaProxy.java | 15 + .../main/java/org/jruby/javasupport/Java.java | 70 +++- .../util/JavaClassConfiguration.java | 2 + lib/ruby/stdlib/jruby/core_ext/class.rb | 7 + 6 files changed, 263 insertions(+), 219 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index 2b63cccce69..703f56decc5 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -90,9 +90,11 @@ import org.jruby.ast.InstVarNode; import org.jruby.ast.IterNode; import org.jruby.ast.ListNode; +import org.jruby.ast.NilNode; import org.jruby.ast.Node; import org.jruby.ast.ReturnNode; import org.jruby.ast.SuperNode; +import org.jruby.ast.ZSuperNode; import org.jruby.ast.visitor.AbstractNodeVisitor; import org.jruby.ast.visitor.NodeVisitor; import org.jruby.compiler.impl.SkinnyMethodAdapter; @@ -102,9 +104,11 @@ import org.jruby.internal.runtime.methods.MixedModeIRMethod; import org.jruby.ir.IRMethod; import org.jruby.java.codegen.RealClassGenerator; +import org.jruby.java.codegen.RealClassGenerator.CtorFlags; import org.jruby.java.codegen.Reified; import org.jruby.java.proxies.ConcreteJavaProxy; import org.jruby.javasupport.Java; +import org.jruby.javasupport.Java.JCtorCache; import org.jruby.runtime.Helpers; import org.jruby.runtime.Block; import org.jruby.runtime.CallSite; @@ -1480,16 +1484,12 @@ interface Reificator { private abstract class BaseReificator implements Reificator { - public static final String RUBY_INIT_ARGS_FIELD = "$rubyInitArgs"; - public static final String RUBY_OBJECT_FIELD = "$rubyObject"; - protected static final String RUBY_PROXY_CLASS_FIELD = "$rubyProxyClass"; - public static final String RUBY_CTORS_FIELD = "$rubyCtors"; public final Class reifiedParent; protected final String javaName; public final String javaPath; public final String rubyName; - protected final String rubyPath; + public final String rubyPath; protected final JavaClassConfiguration jcc; protected final ClassWriter cw; @@ -1515,10 +1515,6 @@ public byte[] reify() { // fields to hold Ruby and RubyClass references cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, "ruby", ci(Ruby.class), null, null); cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, "rubyClass", ci(RubyClass.class), null, null); - if (!isRubyObject()) cw.visitField(ACC_PRIVATE, RUBY_OBJECT_FIELD, rubyName, null, null); - if (!isRubyObject()) cw.visitField(ACC_PRIVATE, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class), null, null); - if (!isRubyObject()) cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class), null, null); - if (!isRubyObject()) cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_CTORS_FIELD, ci(JavaConstructor[].class), null, null); reifyConstructors(); @@ -1565,13 +1561,7 @@ protected boolean isRubyObject() */ protected void loadRubyObject(SkinnyMethodAdapter m) { - if (isRubyObject()) - m.aload(0); // self - else - { - m.aload(0); // self - m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); // rubyObject - } + m.aload(0); // self } @@ -1906,8 +1896,8 @@ public void reifyClinit(SkinnyMethodAdapter m) this.clinitKey = JavaProxyClass.addStaticInitLookup(getExtraClinitInfo()); m.ldc(clinitKey);//TODO: add ctors m.invokestatic(p(JavaProxyClass.class), "getStaticInitLookup", sig(Object[].class, int.class)); - extraClinitLookup(m); m.dup_x1(); // array + m.dup_x2(); // array m.pushInt(0); // ruby index m.aaload(); // extract ruby m.checkcast(p(Ruby.class)); @@ -1915,6 +1905,7 @@ public void reifyClinit(SkinnyMethodAdapter m) m.aaload(); // extract rubyclass m.checkcast(p(RubyClass.class)); m.putstatic(javaPath, "rubyClass", ci(RubyClass.class)); + extraClinitLookup(m); } protected Object[] getExtraClinitInfo() @@ -1924,7 +1915,7 @@ protected Object[] getExtraClinitInfo() protected void extraClinitLookup(SkinnyMethodAdapter m) { - + m.pop(); } protected Class[] searchInheritedSignatures(String id) @@ -2017,7 +2008,10 @@ public Node visitSuperNode(SuperNode node) error = true; return null; } - ArrayNode ret = new ArrayNode(node.getPosition(), node.getArgsNode());// TODO: block args! + Node sarg = node.getArgsNode(); + if (sarg == null) + sarg = new ArrayNode(node.getPosition()); + ArrayNode ret = new ArrayNode(node.getPosition(), sarg);// TODO: block args! ArgsNode an = new ArgsNode(node.getPosition(), null, null, null, null,null); StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); scope.setSignature(Signature.from(an)); @@ -2032,17 +2026,47 @@ public Node visitSuperNode(SuperNode node) // TODO Auto-generated method stub //return super.visitSuperNode(node); } + + @Override + public Node visitZSuperNode(ZSuperNode node) + { + foundsuper = true; + if (level != 1) + { + error = true; + return null; + } + Node sarg = new NilNode(node.getPosition()); + ArrayNode ret = new ArrayNode(node.getPosition(), sarg);// TODO: block args! + ArgsNode an = new ArgsNode(node.getPosition(), null, null, null, null,null); + StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); + scope.setSignature(Signature.from(an)); + ret.add( + new FCallNode(node.getPosition(), RubySymbol.newSymbol(runtime, "lambda"), null, + new IterNode(node.getPosition(), + an, + bn = new BlockNode(node.getPosition()), + scope, 1))); + found = true; + return new ReturnNode(node.getPosition(), ret); + } } + + //public static final String RUBY_INIT_ARGS_FIELD = "$rubyInitArgs"; + public static final String RUBY_OBJECT_FIELD = "$rubyObject"; + protected static final String RUBY_PROXY_CLASS_FIELD = "$rubyProxyClass"; + public static final String RUBY_CTOR_CACHE_FIELD = "$rubyCtorCache"; + boolean rubyctor = false; boolean simpleAlloc = false; - JavaConstructor[] savedCtors = null; + JavaConstructor[] savedSuperCtors = null; ConcreteJavaReifier(Class reifiedParent, String javaName, String javaPath) { // In theory, we should operate on IRubyObject, but everything // that we need is a RubyBasicObject, and it has a nicer interface to boot - super(reifiedParent, javaName, javaPath, ci(RubyBasicObject.class), p(RubyBasicObject.class)); + super(reifiedParent, javaName, javaPath, ci(ConcreteJavaProxy.class), p(ConcreteJavaProxy.class)); } @Override @@ -2057,21 +2081,50 @@ public void customReify() } @Override - protected boolean isRubyObject() + protected void loadRubyObject(SkinnyMethodAdapter m) { - return false; + m.aload(0); // self + m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); // rubyObject } @Override - protected Object[] getExtraClinitInfo() + public byte[] reify() { - return new Object[]{runtime, RubyClass.this, savedCtors}; + cw.visitField(ACC_FINAL | ACC_PRIVATE, RUBY_OBJECT_FIELD, rubyName, null, null); + //cw.visitField(ACC_PRIVATE, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class), null, null); + cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class), null, null); + cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_CTOR_CACHE_FIELD, ci(JCtorCache.class), null, null); + return super.reify(); } @Override - public void reifyClinit(SkinnyMethodAdapter m) + protected boolean isRubyObject() { - super.reifyClinit(m); + return false; + } + + @Override + protected Object[] getExtraClinitInfo() + { + return new Object[]{runtime, RubyClass.this, savedSuperCtors}; + } + // TODO: disable alloc? + + protected void extraClinitLookup(SkinnyMethodAdapter m) + { + // extract cached ctors for lookup ordering + + // note: consume top of stack, lookuparray + m.newobj(p(JCtorCache.class)); + m.dup_x1(); // jccache, lookuparray, jccache + m.swap();// jccache, jccache, lookuparray + m.pushInt(2); // ctor fields = index 2 + m.aaload(); // extract ctors, -> jccache, jccache, ctor[] + m.checkcast(p(JavaConstructor[].class)); + m.invokespecial(p(JCtorCache.class), "", sig(void.class, JavaConstructor[].class)); + m.putstatic(javaPath, RUBY_CTOR_CACHE_FIELD, ci(JCtorCache.class)); + + // now create proxy class m.getstatic(javaPath, "ruby", ci(Ruby.class)); m.getstatic(javaPath, "rubyClass", ci(RubyClass.class)); m.ldc(org.objectweb.asm.Type.getType("L"+javaPath+";")); @@ -2084,20 +2137,10 @@ public void reifyClinit(SkinnyMethodAdapter m) m.invokestatic(p(JavaProxyClass.class), "setProxyClassReified", sig(JavaProxyClass.class, Ruby.class, RubyClass.class, Class.class, boolean.class)); m.putstatic(javaPath, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class)); // Note: no end, that's in the parent call - } - - protected void extraClinitLookup(SkinnyMethodAdapter m) - { - m.dup(); // don't modify the stack below us, use a new copy - m.pushInt(2); // ctor fields - m.aaload(); // extract ctors - m.checkcast(p(JavaConstructor[].class)); - m.putstatic(javaPath, RUBY_CTORS_FIELD, ci(JavaConstructor[].class)); } protected Class[] searchInheritedSignatures(String id) { - for (Method method : reifiedParent.getDeclaredMethods()) { //TODO: java <-> ruby conversion? @@ -2156,151 +2199,80 @@ protected void reifyConstructors() irm2.builtInterpreterContext(); System.err.println("found A SUPER!!!!!"); } + + + if (candidates.size() > 0 ) //TODO: doc: implies javaConstructable? + { + List savedCtorsList = new ArrayList<>(candidates.size()); + for (Constructor constructor : candidates) + { + savedCtorsList.add(new JavaConstructor(runtime, constructor)); + } + savedSuperCtors = savedCtorsList.toArray(new JavaConstructor[savedCtorsList.size()]); + } + else + { + //TODO: no ctors = error? + //TODO ???? + throw new RuntimeException("Class doesn't have any invokeable ctors"); + } if (zeroArg.isPresent()) { rubyctor = true; - simpleAlloc = true; + //TODO: simpleAlloc = true; // standard constructor that accepts Ruby, RubyClass. For use by JRuby (internally) - SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class, Ruby.class, RubyClass.class), null, null); - m.aload(0); // uninitialized this - - // set args for init - m.dup(); - m.getstatic(p(IRubyObject.class), "NULL_ARRAY", ci(IRubyObject[].class)); - m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); - - // call super - m.invokespecial(p(reifiedParent), "", sig(void.class)); - - // call init (if not already called) - generateObjectBarrier(m, 0, false); - - // clear args to avoid holding refs to args - m.aload(0); - m.aconst_null(); - m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); - - m.voidreturn(); - m.end(); +if (!jcc.allCtors) //TODO: fix logic +{ + //if (jcc.rubyConstructable) + RealClassGenerator.makeConcreteConstructorSwitch(cw, true, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); if (jcc.javaConstructable) { - // no-arg constructor using static references to Ruby and RubyClass. For use by java - m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class), null, null); - m.aload(0); // uninitialized this - - // set args for init - m.dup(); - m.getstatic(p(IRubyObject.class), "NULL_ARRAY", ci(IRubyObject[].class)); - m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); - - // call super - m.invokespecial(p(reifiedParent), "", sig(void.class)); - - // call init (if not already called) - generateObjectBarrier(m, -1, true); - - // clear args to avoid holding refs to args - m.aload(0); - m.aconst_null(); - m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); - - m.voidreturn(); - m.end(); + RealClassGenerator.makeConcreteConstructorSwitch(cw, false, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); } +} } /*else { //TODO: copy jpcf logic for alloc throw new RuntimeException("Class doesn't have no-arg ctor"); }*/ - - if (candidates.size() > 0 && jcc.allCtors) //TODO: doc: implies javaConstructable? + if (jcc.allCtors) { - List savedCtorsList = new ArrayList<>(candidates.size()); for (Constructor constructor : candidates) { - savedCtorsList.add(new JavaConstructor(runtime, constructor)); + //TODO: do matching for zero arg? + // if (zeroArg.isPresent() && constructor == zeroArg.get()) continue; + + if (jcc.rubyConstructable) + RealClassGenerator.makeConcreteConstructorSwitch(cw, true, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); + + if (jcc.javaConstructable) + RealClassGenerator.makeConcreteConstructorSwitch(cw, false, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); + } - savedCtors = savedCtorsList.toArray(new JavaConstructor[savedCtorsList.size()]); - for (Constructor constructor : candidates) + } + + if (jcc.extraCtors != null && jcc.extraCtors.length > 0) + { + for (Class[] constructor : jcc.extraCtors) { //TODO: do matching for zero arg? - if (zeroArg.isPresent() && constructor == zeroArg.get()) continue; + if (constructor.length == 0 && zeroArg.isPresent()) continue; + // TODO: ensure not existing already + // TODO: support annotations if (jcc.rubyConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, true, true, this, constructor.getParameterTypes(), savedCtors); + RealClassGenerator.makeConcreteConstructorSwitch(cw, true, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); if (jcc.javaConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, false, true, this, constructor.getParameterTypes(), savedCtors); - /* - if (jcc.rubyConstructable) - { - rubyctor = true; - - //TODO: create empty object before super call??? - // standard constructor that accepts Ruby, RubyClass. For use by JRuby (internally) - SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class, join(constructor.getParameterTypes(), Ruby.class, RubyClass.class)), null, null); - m.aload(0); // uninitialized this - - // set args for init - m.dup(); - RealClassGenerator.coerceArgumentsToRuby(m, constructor.getParameterTypes(), RealClassGenerator.calcBaseIndex(constructor.getParameterTypes(), 1)); - m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); - - // call super - GeneratorAdapter ga = RealClassGenerator.makeGenerator(m); - ga.loadArgs(0, constructor.getParameterCount()); - m.invokespecial(p(reifiedParent), "", sig(void.class, constructor.getParameterTypes())); - - // call init (if not already called) - generateObjectBarrier(m, constructor.getParameterCount(), true);// NOTE: these ones we DO call init from - - // clear args to avoid holding refs to args - m.aload(0); - m.aconst_null(); - m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); - - m.voidreturn(); - m.end(); - } - - if (jcc.javaConstructable) - { - // no-arg constructor using static references to Ruby and RubyClass. For use by java - SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig(void.class, constructor.getParameterTypes()), null, null); - m.aload(0); - - // set args for init - final int baseIndex = RealClassGenerator.calcBaseIndex(constructor.getParameterTypes(), 1); - final int rubyIndex = baseIndex; - m.getstatic(javaPath, "ruby", ci(Ruby.class)); - m.astore(rubyIndex); // save ruby in local - m.dup(); - RealClassGenerator.coerceArgumentsToRuby(m, constructor.getParameterTypes(), rubyIndex); - m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); - - // call super - GeneratorAdapter ga = RealClassGenerator.makeGenerator(m); - ga.loadArgs(0, constructor.getParameterCount()); - m.invokespecial(p(reifiedParent), "", sig(void.class, constructor.getParameterTypes())); - - // call init (if not already called) - generateObjectBarrier(m, -1, true); - - // clear args to avoid holding refs to args - m.aload(0); - m.aconst_null(); - m.putfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); - - m.voidreturn(); - m.end(); - }*/ + RealClassGenerator.makeConcreteConstructorSwitch(cw, false, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); } - } + } + // TODO: rubyargs } /** @@ -2320,43 +2292,10 @@ protected void generateObjectBarrier(SkinnyMethodAdapter m) protected void generateObjectBarrier(SkinnyMethodAdapter m, int ctorArg, boolean initialize) { // For non-concrete things, we check, as this is not a RubyObject - Label end = new Label(); m.aload(0); m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); - m.ifnonnull(end); //TODO: no nulls for ctors? - { - m.newobj(p(ConcreteJavaProxy.class)); - m.dup(); // rubyobject - if (ctorArg >= 0) - { - m.aload(1+ctorArg); // ruby - m.aload(2+ctorArg); // rubyclass - } - else - { - m.getstatic(javaPath, "ruby", ci(Ruby.class)); - m.getstatic(javaPath, "rubyClass", ci(RubyClass.class)); - } - - m.aload(0); // initialized this - m.invokespecial(p(ConcreteJavaProxy.class), "", sig(void.class, Ruby.class, RubyClass.class, Object.class)); - m.dup(); // rubyobject - m.aload(0); // this - m.swap(); - - m.putfield(javaPath, RUBY_OBJECT_FIELD, rubyName); - if (jcc.callInitialize && initialize) //TODO: should init be called when super calls an abstract method we implement? - { - m.ldc("initialize"); // TODO: consts? - m.aload(0); // this - m.getfield(javaPath, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); - rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class) ); - m.pop(); - } - else - m.pop(); - } - m.label(end); + m.aload(0); + m.invokevirtual(rubyPath, "ensureThis", sig(void.class, Object.class)); } private void defineAllocator() diff --git a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java index acb13dab9a7..05bf1695069 100644 --- a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java +++ b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java @@ -52,6 +52,7 @@ import org.jruby.java.proxies.ConcreteJavaProxy; import org.jruby.javasupport.Java; import org.jruby.javasupport.Java.JCreateMethod; +import org.jruby.javasupport.Java.JCtorCache; import org.jruby.javasupport.JavaConstructor; import org.jruby.javasupport.JavaUtil; import org.jruby.runtime.Block; @@ -854,9 +855,19 @@ public static int calcBaseIndex(final Class[] params, int baseIndex) { } return baseIndex; } + + public static enum CtorFlags + { + Normal, + NoSuper, // implicit `super` not allowed, no parent ctor with this signature + RubyArgs, // NoSuper + args don't need converting + RubyArgsBlock, // RubyArgs, plus has a block arg + + } + // TODO: add CJP ctor for alloc sep? //:TODO: Add IRubyobject ctor? // shouls this be in in this spot? - public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby, boolean callsInit, ConcreteJavaReifier cjr, Class[] ctorTypes, JavaConstructor[] constructors) + public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby, boolean callsInit, ConcreteJavaReifier cjr, Class[] ctorTypes, JavaConstructor[] constructors, CtorFlags flag) { String sig = hasRuby ? sig(void.class, cjr.join(ctorTypes, Ruby.class, RubyClass.class)) : sig(void.class, ctorTypes); SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig, null, null); @@ -877,9 +888,10 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby } // this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; RealClassGenerator.coerceArgumentsToRuby(m, ctorTypes, rubyIndex); - m.dup(); +// m.dup(); m.astore(rubyInitArgs); - m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + m.pop(); // pop the this. TODO: dont push the this +// m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); //this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); { @@ -897,18 +909,12 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby m.swap(); m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_OBJECT_FIELD, cjr.rubyName); } - //IRubyObject c = this$rubyObject..callMethod("j_initialize", this.$rubyInitArgs); + //IRubyObject c = this$rubyObject.splitInitialized(this.$rubyInitArgs); if (callsInit) //TODO: should init be called when super calls an abstract method we implement? { - m.ldc("j_initialize"); // TODO: consts? - m.aload(rubyInitArgs); - cjr.rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class) ); //pushes rubyobject - - //RubyArray ra = c.convertToArray(); - m.invokeinterface(p(IRubyObject.class), "convertToArray", sig(RubyArray.class)); - m.dup(); // rubyarray (results of j_initialize) - // m.dup(); // rubyarray (results of j_initialize) -// m.astore(rubyArrayIndex); + m.aload(rubyInitArgs); + m.invokevirtual(cjr.rubyPath, "splitInitialized", sig(RubyArray.class, IRubyObject[].class) ); //pushes rubyarray + m.dup(); // rubyarray (results of splitInitialized) //// c = ra.entry(0); @@ -923,9 +929,9 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby //// switch(Java.JCreateMethod.forTypes(constructors, c)) m.dup(); - m.getstatic(cjr.javaPath, cjr.RUBY_CTORS_FIELD, ci(JavaConstructor[].class)); - m.swap(); // ... arglist, ctors, arglist //TODO: reorder args? - m.invokestatic(p(JCreateMethod.class), "forTypes", sig(int.class, JavaConstructor[].class, IRubyObject.class)); + m.getstatic(cjr.javaPath, cjr.RUBY_CTOR_CACHE_FIELD, ci(JCtorCache.class));// ... arglist, arglist, ctors, + m.aload(rubyIndex); // ruby + m.invokestatic(p(JCreateMethod.class), "forTypes", sig(int.class, IRubyObject.class, JCtorCache.class, Ruby.class)); // ..., arglist, index Label defaultLabel = new Label(); Label[] cases = new Label[constructors.length+1]; //note: offset by one from index @@ -946,12 +952,24 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby m.athrow(); // -1: super(*args), from `super` (no args) in ruby - m.label(cases[0]); - m.aload(0); - ga.loadArgs(0, ctorTypes.length); - m.invokespecial(p(cjr.reifiedParent), "", sig(void.class, ctorTypes)); - m.pop(); //c - m.go_to(endofswitch); + if (flag == CtorFlags.Normal) + { + m.label(cases[0]); + m.aload(0); + ga.loadArgs(0, ctorTypes.length); + m.invokespecial(p(cjr.reifiedParent), "", sig(void.class, ctorTypes)); + m.pop(); //c + m.go_to(endofswitch); + } + else // no super constructor exists, throw + { + m.label(cases[0]); + m.newobj(p(IllegalStateException.class)); + m.dup(); + m.ldc("Superclass does not have a constructor with the same signature, please provide arguments: `super()`"); + m.invokespecial(p(IllegalStateException.class), "", sig(void.class, String.class)); + m.athrow(); + } // case n: for (int i = 0; i < constructors.length; i++) @@ -994,9 +1012,9 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby // TODO: is this field still needed? // clear args to avoid holding refs to args //// this.$rubyInitArgs = null; - m.aload(0); // initialized this - m.aconst_null(); - m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); +// m.aload(0); // initialized this +// m.aconst_null(); +// m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); //implied: if (this.$rubyObject.getObject() == null) // only checked on non-ctor paths @@ -1005,7 +1023,6 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby m.aload(0); // initialized this m.dup(); m.getfield(cjr.javaPath, ConcreteJavaReifier.RUBY_OBJECT_FIELD, cjr.rubyName);// ..., this, rubyobj - m.checkcast(p(ConcreteJavaProxy.class)); m.swap(); // ..., rubyobject, this m.invokevirtual(p(ConcreteJavaProxy.class), "setObject", sig(void.class, Object.class)); @@ -1030,7 +1047,7 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby Ruby var3 = ruby; this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); - IRubyObject c = this$rubyObject..callMethod("j_initialize", this.$rubyInitArgs); + IRubyObject c = this$rubyObject.splitInitialized(this.$rubyInitArgs); RubyArray ra = c.convertToArray(); c = ra.entry(0); IRubyObject continuation = ra.entry(1); diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index c9f7f61d732..e235889bb77 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -3,6 +3,7 @@ import java.lang.reflect.Field; import org.jruby.Ruby; +import org.jruby.RubyArray; import org.jruby.RubyClass; import org.jruby.RubyModule; import org.jruby.internal.runtime.methods.DynamicMethod; @@ -187,6 +188,20 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz } } + + // used by reified classes + public RubyArray splitInitialized(IRubyObject[] args) + { + /// TODO: move gen here + return callMethod("j_initialize", args).convertToArray(); + } + + // used by reified classes + public void ensureThis(Object self) + { + if (getObject() == null) + setObject(self); + } protected static void initialize(final RubyClass concreteJavaProxy) { concreteJavaProxy.addMethod("initialize", new InitializeMethod(concreteJavaProxy)); diff --git a/core/src/main/java/org/jruby/javasupport/Java.java b/core/src/main/java/org/jruby/javasupport/Java.java index d606d39dd08..81c4b17b8be 100644 --- a/core/src/main/java/org/jruby/javasupport/Java.java +++ b/core/src/main/java/org/jruby/javasupport/Java.java @@ -51,6 +51,7 @@ import java.util.Arrays; import java.util.HashSet; import java.util.Iterator; +import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -82,6 +83,7 @@ import org.jruby.java.addons.StringJavaAddons; import org.jruby.java.codegen.RealClassGenerator; import org.jruby.java.dispatch.CallableSelector; +import org.jruby.java.dispatch.CallableSelector.CallableCache; import org.jruby.java.proxies.ArrayJavaProxy; import org.jruby.java.proxies.ArrayJavaProxyCreator; import org.jruby.java.proxies.ConcreteJavaProxy; @@ -602,6 +604,33 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz subclass.addMethod("__jcreate!", new JCreateMethod(subclassSingleton)); } + + public static class JCtorCache implements CallableSelector.CallableCache { + + private final NonBlockingHashMapLong cache = new NonBlockingHashMapLong<>(8); + final JavaConstructor[] constructors; + private final List constructorList; + + public JCtorCache(JavaConstructor[] constructors) + { + this.constructors = constructors; + constructorList = Arrays.asList(constructors); + + } + + public int indexOf(JavaConstructor ctor) + { + return constructorList.indexOf(ctor); + } + + public final ParameterTypes getSignature(int signatureCode) { + return cache.get(signatureCode); + } + + public final void putSignature(int signatureCode, ParameterTypes callable) { + cache.put(signatureCode, callable); + } + } public static class JCreateMethod extends JavaMethodN implements CallableSelector.CallableCache { @@ -643,9 +672,22 @@ public final IRubyObject call(ThreadContext context, IRubyObject self, RubyModul return newObject; } - public static int forTypes(JavaConstructor[] constructors, IRubyObject type) + public static int forTypes(IRubyObject argarray, JCtorCache cache, Ruby runtime) { - return 1; //TODO: implement + if (argarray.isNil()) // super (no args) + { + System.out.println("Will be retuing icx + " + -1); + return -1; + } + RubyArray ra = argarray.convertToArray(); + IRubyObject[] args = ra.toJavaArrayMaybeUnsafe(); // TODO: which method unpacking the array? + + + JavaConstructor ctor = matchConstructorIndex(runtime.getCurrentContext(), cache.constructors, cache, args.length, args); + int index = cache.indexOf(ctor); + if (index < 0) throw runtime.newArgumentError("index error finding superconstructor"); + System.out.println("Will be retuing icx + " + index); + return index; } @Override @@ -741,7 +783,7 @@ public JavaProxyConstructor matchConstructor(final ThreadContext context, final JavaProxyConstructor[] constructors, final int arity, final IRubyObject... args) { ArrayList forArity = findCallablesForArity(arity, constructors); - // remove java-only methods + // remove java-only methods: //TODO: ??? Iterator iter = forArity.iterator(); while (iter.hasNext()) { if(iter.next().isExportable()) @@ -761,6 +803,28 @@ public JavaProxyConstructor matchConstructor(final ThreadContext context, return matching; } + // generic (slowest) path + public static T matchConstructorIndex(final ThreadContext context, + final T[] constructors, final CallableCache cache, final int arity, final IRubyObject... args) { + ArrayList forArity = findCallablesForArity(arity, constructors); + + // remove java-only methods: //TODO: ??? + + //new JavaProxyConstructor(); + + if ( forArity.size() == 0 ) { + throw context.runtime.newArgumentError("wrong number of arguments for constructor"); + } + final ParameterTypes matching = CallableSelector.matchingCallableArityN( + context.runtime, cache, forArity.toArray(new ParameterTypes[forArity.size()]), args + ); + + if ( matching == null ) { + throw context.runtime.newArgumentError("wrong number of arguments for constructor"); + } + return (T) matching; + } + public final JavaProxyConstructor getSignature(int signatureCode) { return cache.get(signatureCode); } diff --git a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java index 162f11d609a..d7c4c8ecc77 100644 --- a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java +++ b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java @@ -5,9 +5,11 @@ public class JavaClassConfiguration public boolean callInitialize = false; public boolean allMethods = true; public boolean javaConstructable = true; + public Class[][] extraCtors = null; // for java proxies public boolean allCtors = false; public boolean rubyConstructable = true; // public boolean splitSuper = true; + public boolean IroCtors = true; } diff --git a/lib/ruby/stdlib/jruby/core_ext/class.rb b/lib/ruby/stdlib/jruby/core_ext/class.rb index c34b366e1da..977f1f85a05 100644 --- a/lib/ruby/stdlib/jruby/core_ext/class.rb +++ b/lib/ruby/stdlib/jruby/core_ext/class.rb @@ -186,6 +186,13 @@ def configure_java_class(**kwargs) self_r.class_config = config kwargs end + + #Temporary, TODO: nicer api + def extra_ctor(*clz) + self_r = JRuby.reference0(self) + config = self_r.class_config + config.extraCtors = [clz.to_java(java.lang.Class)].to_java(java.lang.Class[]) + end def java_annotation(anno) warn "java_annotation is deprecated. Use java_signature '@#{anno} ...' instead. Called from: #{caller.first}" From 99239f05bb3c654aacb1cc6976af8e7b700459f8 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Wed, 11 Nov 2020 20:36:06 -0500 Subject: [PATCH 07/49] Update hack to use most recent api --- core/src/main/java/org/jruby/RubyClass.java | 33 +++++++++++---------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index 703f56decc5..efb96808601 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -1971,7 +1971,7 @@ public Node visitBlockNode(BlockNode node) if (found) return node; - BlockNode replacement = new BlockNode(node.getPosition()); + BlockNode replacement = new BlockNode(node.getLine()); level++; @@ -2010,19 +2010,20 @@ public Node visitSuperNode(SuperNode node) } Node sarg = node.getArgsNode(); if (sarg == null) - sarg = new ArrayNode(node.getPosition()); - ArrayNode ret = new ArrayNode(node.getPosition(), sarg);// TODO: block args! - ArgsNode an = new ArgsNode(node.getPosition(), null, null, null, null,null); + sarg = new ArrayNode(node.getLine()); + ArrayNode ret = new ArrayNode(node.getLine(), sarg);// TODO: block args! + ArgsNode an = new ArgsNode(node.getLine(), null, null, null, null,null); StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); scope.setSignature(Signature.from(an)); + //TODO: capture args? ret.add( - new FCallNode(node.getPosition(), RubySymbol.newSymbol(runtime, "lambda"), null, - new IterNode(node.getPosition(), + new FCallNode(node.getLine(), RubySymbol.newSymbol(runtime, "lambda"), null, + new IterNode(node.getLine(), an, - bn = new BlockNode(node.getPosition()), + bn = new BlockNode(node.getLine()), scope, 1))); found = true; - return new ReturnNode(node.getPosition(), ret); + return new ReturnNode(node.getLine(), ret); // TODO Auto-generated method stub //return super.visitSuperNode(node); } @@ -2036,19 +2037,19 @@ public Node visitZSuperNode(ZSuperNode node) error = true; return null; } - Node sarg = new NilNode(node.getPosition()); - ArrayNode ret = new ArrayNode(node.getPosition(), sarg);// TODO: block args! - ArgsNode an = new ArgsNode(node.getPosition(), null, null, null, null,null); + Node sarg = new NilNode(node.getLine()); + ArrayNode ret = new ArrayNode(node.getLine(), sarg);// TODO: block args! + ArgsNode an = new ArgsNode(node.getLine(), null, null, null, null,null); StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); scope.setSignature(Signature.from(an)); ret.add( - new FCallNode(node.getPosition(), RubySymbol.newSymbol(runtime, "lambda"), null, - new IterNode(node.getPosition(), + new FCallNode(node.getLine(), RubySymbol.newSymbol(runtime, "lambda"), null, + new IterNode(node.getLine(), an, - bn = new BlockNode(node.getPosition()), + bn = new BlockNode(node.getLine()), scope, 1))); found = true; - return new ReturnNode(node.getPosition(), ret); + return new ReturnNode(node.getLine(), ret); } } @@ -2178,7 +2179,7 @@ protected void reifyConstructors() if (flat.error) System.err.println("error"); System.err.println(def.toString()); - DefNode rdnbody = new DefnNode(def.getBodyNode().getPosition(), RubySymbol.newSymbol(runtime, "j_initialize"), def.getArgsNode(), def.getScope(), body, def.getEndLine()); + DefNode rdnbody = new DefnNode(def.getBodyNode().getLine(), RubySymbol.newSymbol(runtime, "j_initialize"), def.getArgsNode(), def.getScope(), body, def.getEndLine()); System.err.println(rdnbody.toString()); From 5f9b61881eb9a750245a795cd98c325260447095 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Wed, 11 Nov 2020 21:17:39 -0500 Subject: [PATCH 08/49] Make more tests work and be less verbose --- .../java/org/jruby/java/proxies/ConcreteJavaProxy.java | 9 +++++++-- core/src/main/java/org/jruby/javasupport/JavaUtil.java | 8 ++++---- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index e235889bb77..0292a07be03 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -103,7 +103,7 @@ private static final class NewMethod extends org.jruby.internal.runtime.methods. private DynamicMethod reifyAndNewMethod(IRubyObject clazz) { RubyClass parent = ((RubyClass)clazz); - System.err.println(parent.getName() + " is " + parent.getJavaProxy()); + System.err.println(parent.getName() + " is " + parent.getJavaProxy());// TODO: remove if (parent.getJavaProxy()) return newMethod; // overridden class: reify and re-lookup new as reification changes it @@ -205,7 +205,12 @@ public void ensureThis(Object self) protected static void initialize(final RubyClass concreteJavaProxy) { concreteJavaProxy.addMethod("initialize", new InitializeMethod(concreteJavaProxy)); - System.err.println("adding to " + concreteJavaProxy.getName()); + if (concreteJavaProxy.getName().equals("ConcreteJavaProxy")) + {} + else if (concreteJavaProxy.getName().equals("MapJavaProxy")) + {} + else + System.err.println("adding to " + concreteJavaProxy.getName()); //TODO: remove // We define a custom "new" method to ensure that __jcreate! is getting called, // so that if the user doesn't call super in their subclasses, the object will // still get set up properly. See JRUBY-4704. diff --git a/core/src/main/java/org/jruby/javasupport/JavaUtil.java b/core/src/main/java/org/jruby/javasupport/JavaUtil.java index cf820dc7c0d..f4b7df4065e 100644 --- a/core/src/main/java/org/jruby/javasupport/JavaUtil.java +++ b/core/src/main/java/org/jruby/javasupport/JavaUtil.java @@ -182,10 +182,10 @@ public static IRubyObject convertJavaToRuby(Ruby runtime, boolean b) { * @return corresponding Ruby type, or a functional Java proxy */ public static IRubyObject convertJavaToUsableRubyObject(Ruby runtime, Object object) { - if (object.getClass().getName().startsWith("rubyobj")) - { - - } +// if (object.getClass().getName().startsWith("rubyobj")) +// { +// +// } IRubyObject result = trySimpleConversions(runtime, object); if (result != null) return result; From 996197f06c0fd573913fd594b5cfb117b2526721 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Sat, 14 Nov 2020 19:54:28 -0500 Subject: [PATCH 09/49] Minor cleanups of type safety and move AST transform to new file --- core/src/main/java/org/jruby/RubyClass.java | 178 +++--------------- .../org/jruby/ext/ffi/ReifyingAllocator.java | 14 +- .../jruby/java/addons/ClassJavaAddons.java | 6 +- .../java/codegen/RealClassGenerator.java | 4 +- .../jruby/java/proxies/ConcreteJavaProxy.java | 48 ++++- .../org/jruby/java/proxies/FlatExtractor.java | 129 +++++++++++++ .../java/proxies/JavaInterfaceTemplate.java | 6 +- .../main/java/org/jruby/javasupport/Java.java | 9 +- .../javasupport/proxy/JavaProxyClass.java | 11 +- .../proxy/JavaProxyConstructor.java | 94 +-------- .../proxy/JavaProxyInvocationHandler.java | 63 ------- 11 files changed, 217 insertions(+), 345 deletions(-) create mode 100644 core/src/main/java/org/jruby/java/proxies/FlatExtractor.java delete mode 100644 core/src/main/java/org/jruby/javasupport/proxy/JavaProxyInvocationHandler.java diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index efb96808601..4e978791832 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -1391,7 +1391,7 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) { final String javaName = "rubyobj." + StringSupport.replaceAll(name, "::", "."); final String javaPath = "rubyobj/" + StringSupport.replaceAll(name, "::", "/"); - final Class parentReified = superClass.getRealClass().getReifiedClass(); + final Class parentReified = superClass.getRealClass().getReifiedClass(); if (parentReified == null) { throw getClassRuntime().newTypeError(getName() + "'s parent class is not yet reified"); } @@ -1933,125 +1933,7 @@ protected void generateObjectBarrier(SkinnyMethodAdapter m) //public or private? public class ConcreteJavaReifier extends MethodReificator { - private final class FlatExtractor extends AbstractNodeVisitor - { - private final DefNode def; - boolean found = false; - BlockNode bn = null; - int level = 0; - boolean error = false; - boolean foundsuper = false; - - private FlatExtractor(DefNode def) - { - this.def = def; - } - - @Override - protected Node defaultVisit(Node node) - { - if (node == null) return null; - - if (error) - return null; - - level++; - - node.childNodes().forEach(this::defaultVisit); - level--; - - return node; - } - - @Override - public Node visitBlockNode(BlockNode node) - { - if (error) - return null; - if (found) - return node; - - BlockNode replacement = new BlockNode(node.getLine()); - - level++; - - boolean seenReturn = false; - - for (Node child : node.children()) - { - Node newc = child.accept(this); - if (found) - { - if (seenReturn) - { - bn.add(newc); - continue; - } - else - { - seenReturn = true; - } - } - replacement.add(newc); - } - level--; - - return replacement; - } - - @Override - public Node visitSuperNode(SuperNode node) - { - foundsuper = true; - if (level != 1) - { - error = true; - return null; - } - Node sarg = node.getArgsNode(); - if (sarg == null) - sarg = new ArrayNode(node.getLine()); - ArrayNode ret = new ArrayNode(node.getLine(), sarg);// TODO: block args! - ArgsNode an = new ArgsNode(node.getLine(), null, null, null, null,null); - StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); - scope.setSignature(Signature.from(an)); - //TODO: capture args? - ret.add( - new FCallNode(node.getLine(), RubySymbol.newSymbol(runtime, "lambda"), null, - new IterNode(node.getLine(), - an, - bn = new BlockNode(node.getLine()), - scope, 1))); - found = true; - return new ReturnNode(node.getLine(), ret); - // TODO Auto-generated method stub - //return super.visitSuperNode(node); - } - - @Override - public Node visitZSuperNode(ZSuperNode node) - { - foundsuper = true; - if (level != 1) - { - error = true; - return null; - } - Node sarg = new NilNode(node.getLine()); - ArrayNode ret = new ArrayNode(node.getLine(), sarg);// TODO: block args! - ArgsNode an = new ArgsNode(node.getLine(), null, null, null, null,null); - StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); - scope.setSignature(Signature.from(an)); - ret.add( - new FCallNode(node.getLine(), RubySymbol.newSymbol(runtime, "lambda"), null, - new IterNode(node.getLine(), - an, - bn = new BlockNode(node.getLine()), - scope, 1))); - found = true; - return new ReturnNode(node.getLine(), ret); - } - } + //public static final String RUBY_INIT_ARGS_FIELD = "$rubyInitArgs"; @@ -2170,36 +2052,7 @@ protected void reifyConstructors() zeroArg = Optional.of(constructor); } } - DynamicMethod dm = searchMethod("initialize"); - DefNode def = ((IRMethod)((AbstractIRMethod)dm).getIRScope()).desugar(); - FlatExtractor flat = new FlatExtractor(def); - Node body = def.getBodyNode().accept(flat); - if (!flat.foundsuper) - System.err.println("NO SUPER"); - if (flat.error) - System.err.println("error"); - System.err.println(def.toString()); - DefNode rdnbody = new DefnNode(def.getBodyNode().getLine(), RubySymbol.newSymbol(runtime, "j_initialize"), def.getArgsNode(), def.getScope(), body, def.getEndLine()); - System.err.println(rdnbody.toString()); - - - IRMethod irm = ((IRMethod)((AbstractIRMethod)dm).getIRScope()); - irm.builtInterpreterContext(); - -irm = new IRMethod(irm.getManager(), irm.getLexicalParent(), rdnbody, new ByteList("j_initialize".getBytes(), runtime.getEncodingService().getJavaDefault()), true, irm.getLine(), irm.getStaticScope(), irm.getCoverageMode()); - dm = new MixedModeIRMethod(irm, Visibility.PUBLIC, RubyClass.this); - - RubyClass.this.addMethod("j_initialize", dm); - - dm = searchMethod("j_initialize"); - if (dm instanceof AbstractIRMethod) - { - IRMethod irm2 = ((IRMethod)((AbstractIRMethod)dm).getIRScope()); - - System.err.println(irm2.desugar().toString()); - irm2.builtInterpreterContext(); - System.err.println("found A SUPER!!!!!"); - } + // TODO: guess from arity? if (candidates.size() > 0 ) //TODO: doc: implies javaConstructable? @@ -2348,14 +2201,30 @@ public void setReifiedClass(Class reifiedClass) { } //FIXME: no longer IRubyObject anymore - public Class getReifiedClass() { + public Class getReifiedClass() { return reifiedClass; } - public static Class nearestReifiedClass(final RubyClass klass) { + public Class getReifiedRubyClass() { + if (reifiedClassJava == Boolean.TRUE) + // TODO: error type + throw runtime.newTypeError("Attempted to get a Ruby class for a Java class"); + else + return reifiedClass; + } + + public Class getReifiedJavaClass() { + if (reifiedClassJava == Boolean.FALSE) + // TODO: error type + throw runtime.newTypeError("Attempted to get a Ruby class for a Java class"); + else + return reifiedClass; + } + + public static Class nearestReifiedClass(final RubyClass klass) { RubyClass current = klass; do { - Class reified = current.getReifiedClass(); + Class reified = current.getReifiedClass(); if ( reified != null ) return reified; current = current.getSuperClass(); } @@ -2481,7 +2350,7 @@ public T toJava(Class target) { IRubyObject javaClass = JavaClass.java_class(context, this); if ( ! javaClass.isNil() ) return javaClass.toJava(target); - Class reifiedClass = nearestReifiedClass(this); + Class reifiedClass = nearestReifiedClass(this); if ( reifiedClass != null ) return target.cast(reifiedClass); // should never fall through, since RubyObject has a reified class } @@ -3106,6 +2975,7 @@ public static CS_NAMES fromOrdinal(int ordinal) { private CallSite[] extraCallSites; private Class reifiedClass; + private Boolean reifiedClassJava; private Map>>> parameterAnnotations; diff --git a/core/src/main/java/org/jruby/ext/ffi/ReifyingAllocator.java b/core/src/main/java/org/jruby/ext/ffi/ReifyingAllocator.java index c3c23fde941..6881731416d 100644 --- a/core/src/main/java/org/jruby/ext/ffi/ReifyingAllocator.java +++ b/core/src/main/java/org/jruby/ext/ffi/ReifyingAllocator.java @@ -10,10 +10,10 @@ import java.lang.reflect.InvocationTargetException; class ReifyingAllocator implements ObjectAllocator { - private final Class klass; - private final Constructor cons; + private final Class klass; + private final Constructor cons; - public ReifyingAllocator(Class klass) { + public ReifyingAllocator(Class klass) { this.klass = klass; try { this.cons = klass.getDeclaredConstructor(Ruby.class, RubyClass.class); @@ -24,8 +24,8 @@ public ReifyingAllocator(Class klass) { public IRubyObject allocate(Ruby runtime, RubyClass klazz) { try { - if (klazz.getReifiedClass() == this.klass) { - return (IRubyObject) cons.newInstance(runtime, klazz); + if (klazz.getReifiedRubyClass() == this.klass) { + return cons.newInstance(runtime, klazz); } reifyWithAncestors(klazz); @@ -44,10 +44,10 @@ private static void reifyWithAncestors(RubyClass klazz) { RubyClass realSuper = klazz.getSuperClass().getRealClass(); - if (realSuper.getReifiedClass() == null) reifyWithAncestors(realSuper); + if (realSuper.getReifiedRubyClass() == null) reifyWithAncestors(realSuper); synchronized (klazz) { klazz.reify(); - klazz.setAllocator(new ReifyingAllocator(klazz.getReifiedClass())); + klazz.setAllocator(new ReifyingAllocator(klazz.getReifiedRubyClass())); } } } diff --git a/core/src/main/java/org/jruby/java/addons/ClassJavaAddons.java b/core/src/main/java/org/jruby/java/addons/ClassJavaAddons.java index 7a16924a75b..fd3c156f959 100644 --- a/core/src/main/java/org/jruby/java/addons/ClassJavaAddons.java +++ b/core/src/main/java/org/jruby/java/addons/ClassJavaAddons.java @@ -23,7 +23,7 @@ public abstract class ClassJavaAddons { // Get the native (or reified) (a la become_java!) class for this Ruby class. @JRubyMethod public static IRubyObject java_class(ThreadContext context, final IRubyObject self) { - Class reifiedClass = RubyClass.nearestReifiedClass((RubyClass) self); + Class reifiedClass = RubyClass.nearestReifiedClass((RubyClass) self); if ( reifiedClass == null ) return context.nil; // TODO: java_class is used for different things with Java proxy modules/classes return asJavaClass(context.runtime, reifiedClass); @@ -67,9 +67,7 @@ private static IRubyObject becomeJava(final ThreadContext context, final RubyCla klass.reifyWithAncestors(dumpDir, useChildLoader); Class reifiedClass = klass.getReifiedClass(); - if (reifiedClass == null) { // java proxies can't be reified, but they deserve field accessors too - reifiedClass = JavaProxyClass.getProxyClass(context.getRuntime(), klass).getJavaClass(); - } + // TODO: ensure deleted line is no longer necessary if (reifiedClass == null) { throw context.runtime.newTypeError("requested class " + klass.getName() + " was not reifiable"); } diff --git a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java index 05bf1695069..fbbdf10042f 100644 --- a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java +++ b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java @@ -146,7 +146,7 @@ public static Class createOldStyleImplClass(Class[] superTypes, RubyClass rubyCl } // NOTE: only used for interface class generation from ... Java.generateRealClass - public static Class createRealImplClass(Class superClass, Class[] interfaces, RubyClass rubyClass, Ruby ruby, String name) { + public static Class createRealImplClass(Class superClass, Class[] interfaces, RubyClass rubyClass, Ruby ruby, String name) { String[] superTypeNames = new String[interfaces.length]; // interfaces now do have a convention that they only override an interface default method @@ -156,7 +156,7 @@ public static Class createRealImplClass(Class superClass, Class[] interfaces, Ru Class newClass = defineRealImplClass(ruby, name, superClass, superTypeNames, simpleToAll); // Confirm all interfaces got implemented - for (Class ifc : interfaces) { + for (Class ifc : interfaces) { assert ifc.isAssignableFrom(newClass); } diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index 0292a07be03..2ca0bbb86c6 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -2,11 +2,11 @@ import java.lang.reflect.Field; -import org.jruby.Ruby; -import org.jruby.RubyArray; -import org.jruby.RubyClass; -import org.jruby.RubyModule; -import org.jruby.internal.runtime.methods.DynamicMethod; +import org.jruby.*; +import org.jruby.ast.*; +import org.jruby.internal.runtime.AbstractIRMethod; +import org.jruby.internal.runtime.methods.*; +import org.jruby.ir.IRMethod; import org.jruby.javasupport.Java; import org.jruby.javasupport.JavaObject; import org.jruby.javasupport.proxy.ReifiedJavaProxy; @@ -17,6 +17,7 @@ import org.jruby.runtime.ThreadContext; import org.jruby.runtime.Visibility; import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.util.ByteList; public class ConcreteJavaProxy extends JavaProxy { @@ -108,10 +109,10 @@ private DynamicMethod reifyAndNewMethod(IRubyObject clazz) { // overridden class: reify and re-lookup new as reification changes it if (parent.getReifiedClass() == null) { - parent.reifyWithAncestors(); + parent.reifyWithAncestors(); // TODO: is this good? } //System.err.println(parent.getName() + " is " + parent.getJavaProxy()); - return new NewMethodReified(parent, parent.getReifiedClass()); + return new NewMethodReified(parent); } @Override @@ -169,11 +170,10 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz //TODO: cleanup public static final class NewMethodReified extends org.jruby.internal.runtime.methods.JavaMethod.JavaMethodN { - private Field rubyObject; private final DynamicMethod initialize; //TODO: package? - public NewMethodReified(final RubyClass clazz, final Class reified) { + public NewMethodReified(final RubyClass clazz) { super(clazz, Visibility.PUBLIC, "new"); initialize = clazz.searchMethod("__jcreate!"); } @@ -192,6 +192,36 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz // used by reified classes public RubyArray splitInitialized(IRubyObject[] args) { + + DynamicMethod dm = this.getMetaClass().searchMethod("j_initialize"); + if (!(dm instanceof AbstractIRMethod)) + { + dm = getMetaClass().searchMethod("initialize"); + DefNode def = ((IRMethod)((AbstractIRMethod)dm).getIRScope()).desugar(); + FlatExtractor flat = new FlatExtractor(this.getRuntime(), def); + Node body = def.getBodyNode().accept(flat); + if (!flat.foundsuper) + System.err.println("NO SUPER"); + if (flat.error) + System.err.println("error"); + System.err.println(def.toString()); + DefNode rdnbody = new DefnNode(def.getBodyNode().getLine(), RubySymbol.newSymbol(this.getRuntime(),"j_initialize"), def.getArgsNode(), def.getScope(), body, def.getEndLine()); + System.err.println(rdnbody.toString()); + + + IRMethod irm = ((IRMethod)((AbstractIRMethod)dm).getIRScope()); + irm.builtInterpreterContext(); + + irm = new IRMethod(irm.getManager(), irm.getLexicalParent(), rdnbody, + new ByteList("j_initialize".getBytes(), getRuntime().getEncodingService().getJavaDefault()), true, + irm.getLine(), irm.getStaticScope(), irm.getCoverageMode()); + dm = new MixedModeIRMethod(irm, Visibility.PUBLIC, this.getMetaClass()); + + this.getMetaClass().addMethod("j_initialize", dm); + + + } + /// TODO: move gen here return callMethod("j_initialize", args).convertToArray(); } diff --git a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java new file mode 100644 index 00000000000..e54e3fe904e --- /dev/null +++ b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java @@ -0,0 +1,129 @@ +package org.jruby.java.proxies; + +import org.jruby.*; +import org.jruby.ast.*; +import org.jruby.ast.visitor.AbstractNodeVisitor; +import org.jruby.parser.*; +import org.jruby.runtime.Signature; + +class FlatExtractor extends AbstractNodeVisitor + { + private final DefNode def; + boolean found = false; + BlockNode bn = null; + int level = 0; + boolean error = false; + boolean foundsuper = false; + private Ruby runtime; + + FlatExtractor(Ruby runtime, DefNode def) + { + this.runtime = runtime; + this.def = def; + } + + @Override + protected Node defaultVisit(Node node) + { + if (node == null) return null; + + if (error) + return null; + + level++; + + node.childNodes().forEach(this::defaultVisit); + level--; + + return node; + } + + @Override + public Node visitBlockNode(BlockNode node) + { + if (error) + return null; + if (found) + return node; + + BlockNode replacement = new BlockNode(node.getLine()); + + level++; + + boolean seenReturn = false; + + for (Node child : node.children()) + { + Node newc = child.accept(this); + if (found) + { + if (seenReturn) + { + bn.add(newc); + continue; + } + else + { + seenReturn = true; + } + } + replacement.add(newc); + } + level--; + + return replacement; + } + + @Override + public Node visitSuperNode(SuperNode node) + { + foundsuper = true; + if (level != 1) + { + error = true; + return null; + } + Node sarg = node.getArgsNode(); + if (sarg == null) + sarg = new ArrayNode(node.getLine()); + ArrayNode ret = new ArrayNode(node.getLine(), sarg);// TODO: block args! + ArgsNode an = new ArgsNode(node.getLine(), null, null, null, null,null); + StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); + scope.setSignature(Signature.from(an)); + //TODO: capture args? + ret.add( + new FCallNode(node.getLine(), RubySymbol.newSymbol(runtime, "lambda"), null, + new IterNode(node.getLine(), + an, + bn = new BlockNode(node.getLine()), + scope, 1))); + found = true; + return new ReturnNode(node.getLine(), ret); + // TODO Auto-generated method stub + //return super.visitSuperNode(node); + } + + @Override + public Node visitZSuperNode(ZSuperNode node) + { + foundsuper = true; + if (level != 1) + { + error = true; + return null; + } + Node sarg = new NilNode(node.getLine()); + ArrayNode ret = new ArrayNode(node.getLine(), sarg);// TODO: block args! + ArgsNode an = new ArgsNode(node.getLine(), null, null, null, null,null); + StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); + scope.setSignature(Signature.from(an)); + ret.add( + new FCallNode(node.getLine(), RubySymbol.newSymbol(runtime, "lambda"), null, + new IterNode(node.getLine(), + an, + bn = new BlockNode(node.getLine()), + scope, 1))); + found = true; + return new ReturnNode(node.getLine(), ret); + } + } \ No newline at end of file diff --git a/core/src/main/java/org/jruby/java/proxies/JavaInterfaceTemplate.java b/core/src/main/java/org/jruby/java/proxies/JavaInterfaceTemplate.java index edf5f5af706..7552906427e 100644 --- a/core/src/main/java/org/jruby/java/proxies/JavaInterfaceTemplate.java +++ b/core/src/main/java/org/jruby/java/proxies/JavaInterfaceTemplate.java @@ -261,14 +261,14 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz public static void addRealImplClassNew(final RubyClass clazz) { clazz.setAllocator(new ObjectAllocator() { - private Constructor proxyConstructor; + private Constructor proxyConstructor; public IRubyObject allocate(Ruby runtime, RubyClass klazz) { // if we haven't been here before, reify the class - Class reifiedClass = klazz.getReifiedClass(); + Class reifiedClass = klazz.getReifiedRubyClass(); if (proxyConstructor == null || proxyConstructor.getDeclaringClass() != reifiedClass) { if (reifiedClass == null) { - reifiedClass = Java.generateRealClass(klazz); + reifiedClass = Java.generateRealClass(klazz); //TODO: test concrete } proxyConstructor = Java.getRealClassConstructor(runtime, reifiedClass); } diff --git a/core/src/main/java/org/jruby/javasupport/Java.java b/core/src/main/java/org/jruby/javasupport/Java.java index 81c4b17b8be..fd7312f3345 100644 --- a/core/src/main/java/org/jruby/javasupport/Java.java +++ b/core/src/main/java/org/jruby/javasupport/Java.java @@ -1600,7 +1600,8 @@ public static Class generateRealClass(final RubyClass clazz) { } catch (ClassNotFoundException ex) { // try to use super's reified class; otherwise, RubyObject (for now) - Class superClass = clazz.getSuperClass().getRealClass().getReifiedClass(); + //TODO: test java reified? + Class superClass = clazz.getSuperClass().getRealClass().getReifiedClass(); if ( superClass == null ) superClass = RubyObject.class; proxyImplClass = RealClassGenerator.createRealImplClass(superClass, interfaces, clazz, runtime, implClassName); @@ -1627,7 +1628,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz } - public static Constructor getRealClassConstructor(final Ruby runtime, Class proxyImplClass) { + public static Constructor getRealClassConstructor(final Ruby runtime, Class proxyImplClass) { try { return proxyImplClass.getConstructor(Ruby.class, RubyClass.class); } @@ -1636,9 +1637,9 @@ public static Constructor getRealClassConstructor(final Ruby runtime, Class p } } - public static IRubyObject constructProxy(Ruby runtime, Constructor proxyConstructor, RubyClass clazz) { + public static IRubyObject constructProxy(Ruby runtime, Constructor proxyConstructor, RubyClass clazz) { try { - return (IRubyObject) proxyConstructor.newInstance(runtime, clazz); + return proxyConstructor.newInstance(runtime, clazz); } catch (InvocationTargetException e) { throw mapGeneratedProxyException(runtime, e); diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java index a6a0c68c84b..c3300aada53 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java @@ -224,9 +224,10 @@ public JavaProxyConstructor[] getConstructors() { public JavaProxyConstructor getConstructor(final Class[] args) throws SecurityException, NoSuchMethodException { - final Class[] realArgs = new Class[args.length + 1]; + final Class[] realArgs = new Class[args.length + 2]; System.arraycopy(args, 0, realArgs, 0, args.length); - realArgs[ args.length ] = JavaProxyInvocationHandler.class; //TODO: fix! + realArgs[ args.length ] = Ruby.class; + realArgs[ args.length + 1 ] = RubyClass.class; @SuppressWarnings("unchecked") Constructor constructor = proxyClass.getConstructor(realArgs); @@ -628,12 +629,6 @@ public static JavaProxyClass setProxyClassReified(final Ruby runtime, final Ruby singleton.setInstanceVariable("@java_proxy_class", proxyClass); singleton.setInstanceVariable("@java_class", Java.wrapJavaObject(runtime, reified)); if (allocator) singleton.addMethod("new", new NewMethodReified(clazz, reified)); -// -// //TODO: wait, what???? -// singleton = clazz.getMetaClass(); -// singleton.setInstanceVariable("@java_proxy_class", proxyClass); -// singleton.setInstanceVariable("@java_class", Java.wrapJavaObject(runtime, reified)); -// if (allocator) singleton.addMethod("new", new NewMethodReified(clazz, reified)); return proxyClass; } diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java index cc5a6ea5326..64cd9c7055e 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java @@ -190,7 +190,6 @@ public RubyObject new_instance2(IRubyObject[] args, Block unusedBlock) { final IRubyObject self = args[0]; final Object[] convertedArgs = convertArguments((RubyArray) args[1]); // constructor arguments - //JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); try { return JavaObject.wrap(runtime, newInstance(convertedArgs, runtime, self)); } @@ -199,8 +198,7 @@ public RubyObject new_instance2(IRubyObject[] args, Block unusedBlock) { public JavaObject newInstance(final IRubyObject self, Object[] args) throws RaiseException { final Ruby runtime = getRuntime(); - - //JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); + try { return JavaObject.wrap(runtime, newInstance(args, runtime, self)); } @@ -211,7 +209,7 @@ public final JavaObject newInstance(final IRubyObject self, IRubyObject[] args) final Ruby runtime = getRuntime(); final Object[] javaArgsPlus1 = RubyToJavaInvoker.convertArguments(this, args, (exportable?0:+2)); - //JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); + try { return JavaObject.wrap(runtime, newInstanceImpl(javaArgsPlus1, runtime, self)); } @@ -222,7 +220,7 @@ public final JavaObject newInstance(final IRubyObject self, IRubyObject arg0) th final Ruby runtime = getRuntime(); final Object[] javaArgsPlus1 = RubyToJavaInvoker.convertArguments(this, arg0, (exportable?0:+2)); - //JavaProxyInvocationHandler handler = new MethodInvocationHandler(runtime, self); + try { return JavaObject.wrap(runtime, newInstanceImpl(javaArgsPlus1, runtime, self)); } @@ -239,59 +237,6 @@ private static RaiseException mapInstantiationException(final Ruby runtime, fina ex.initCause(e); throw ex; } -/* - static final class MethodInvocationHandler implements JavaProxyInvocationHandler { - - private final Ruby runtime; - private final IRubyObject self; - - MethodInvocationHandler(final Ruby runtime, final IRubyObject self) { - this.runtime = runtime; this.self = self; - } - - public IRubyObject getOrig() { return self; } - public final Ruby getRuntime() { return runtime; } - - public Object invoke(Object proxy, JavaProxyMethod proxyMethod, Object[] nargs) throws Throwable { - final RubyClass metaClass = self.getMetaClass(); - 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 ); - } - - public void invoke_ctor(Object[] args) throws Throwable { - final RubyClass metaClass = self.getMetaClass(); - final DynamicMethod method = metaClass.searchMethod("initialize");// is this the right method? any optimization? - // TODO: sketchy... might call super! - invokeRuby(method, null, metaClass, "initialize", args);// TODO: only if last arg isn't ruby - } - - 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(); - - if ( arity < 0 || arity == newArgs.length ) { - final ThreadContext context = runtime.getCurrentContext(); - return method.call(context, self, metaClass, name, newArgs); - } - if ( proxyMethod != null && proxyMethod.hasSuperImplementation() ) { - final ThreadContext context = runtime.getCurrentContext(); - final RubyClass superClass = metaClass.getSuperClass(); - return Helpers.invokeAs(context, superClass, self, name, newArgs, Block.NULL_BLOCK); - } - throw runtime.newArgumentError(newArgs.length, arity); - } - - }*/ @JRubyMethod(required = 1, optional = 1) public RubyObject new_instance(IRubyObject[] args, Block block) { @@ -309,7 +254,6 @@ public RubyObject new_instance(IRubyObject[] args, Block block) { final Object[] convertedArgs = convertArguments((RubyArray) args[0]); - //JavaProxyInvocationHandler handler = new ProcInvocationHandler(runtime, proc); try { return JavaObject.wrap(runtime, newInstance(convertedArgs, runtime, proc)); } @@ -319,38 +263,6 @@ public RubyObject new_instance(IRubyObject[] args, Block block) { throw ex; } } -/* - private static final class ProcInvocationHandler implements JavaProxyInvocationHandler { - - private final Ruby runtime; - private final RubyProc proc; - - ProcInvocationHandler(final Ruby runtime, final RubyProc proc) { - this.runtime = runtime; this.proc = proc; - } - - public IRubyObject getOrig() { return null; } - public final Ruby getRuntime() { return runtime; } - - public Object invoke(Object proxy, JavaProxyMethod method, Object[] nargs) throws Throwable { - final int length = nargs == null ? 0 : nargs.length; - final IRubyObject[] rubyArgs = new IRubyObject[length + 2]; - rubyArgs[0] = JavaObject.wrap(runtime, proxy); - rubyArgs[1] = method; - for ( int i = 0; i < length; i++ ) { - rubyArgs[i + 2] = JavaUtil.convertJavaToRuby(runtime, nargs[i]); - } - IRubyObject procResult = proc.call(runtime.getCurrentContext(), rubyArgs); - return procResult.toJava( method.getReturnType() ); - } - - @Override - public void invoke_ctor(Object[] args) throws Throwable - { - throw new UnsupportedOperationException(); - } - - }*/ private Object[] convertArguments(final RubyArray arguments) { final int argsSize = arguments.size(); diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyInvocationHandler.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyInvocationHandler.java deleted file mode 100644 index 3b2e2aab316..00000000000 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyInvocationHandler.java +++ /dev/null @@ -1,63 +0,0 @@ -/***** BEGIN LICENSE BLOCK ***** - * Version: EPL 2.0/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Eclipse Public - * License Version 2.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/epl-v20.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) 2006 Kresten Krab Thorup - * - * 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.proxy; - -import org.jruby.runtime.builtin.IRubyObject; - -/** - * A proxy invocation handler for JRuby. - * - * @see java.lang.reflect.InvocationHandler - */ -public interface JavaProxyInvocationHandler { - - IRubyObject getOrig(); // rename to getTarget() ? returns self or null for proc based handler - - // org.jruby.Ruby getRuntime(); - - /** - * Similar to {@link java.lang.reflect.InvocationHandler} - * - * @param receiver - * @param method - * @param args - * @return - * @throws Throwable - * @see java.lang.reflect.InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[]) - */ - Object invoke(Object receiver, JavaProxyMethod method, Object[] args) throws Throwable; - - - /** - * Calls the initialize method - * @param args - * @throws Throwable - */ - void invoke_ctor(Object[] args) throws Throwable; -} \ No newline at end of file From c0995d5e13882b237212837ca98826be8de1df3d Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Sat, 14 Nov 2020 20:00:48 -0500 Subject: [PATCH 10/49] missed a file --- .../main/java/org/jruby/javasupport/proxy/JavaProxyClass.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java index c3300aada53..2c75fc15232 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java @@ -613,7 +613,7 @@ public static RubyObject get_with_class(final IRubyObject self, IRubyObject obj) } //Note: called from of reified classes - public static JavaProxyClass setProxyClassReified(final Ruby runtime, final RubyClass clazz, final Class reified, final boolean allocator) { + public static JavaProxyClass setProxyClassReified(final Ruby runtime, final RubyClass clazz, final Class reified, final boolean allocator) { JavaProxyClass proxyClass = new JavaProxyClass(runtime, reified); // TODO: don't duplicate this code from above // NOTE: currently we regenerate proxy classes when a Ruby method is added on the type @@ -628,7 +628,7 @@ public static JavaProxyClass setProxyClassReified(final Ruby runtime, final Ruby System.err.println("Setting on " + clazz.toString() + " + single " + singleton.toString()); singleton.setInstanceVariable("@java_proxy_class", proxyClass); singleton.setInstanceVariable("@java_class", Java.wrapJavaObject(runtime, reified)); - if (allocator) singleton.addMethod("new", new NewMethodReified(clazz, reified)); + if (allocator) singleton.addMethod("new", new NewMethodReified(clazz)); return proxyClass; } From 89f90db4a2c92d34a0078672b7d04054a37c419c Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Sun, 15 Nov 2020 02:12:38 -0500 Subject: [PATCH 11/49] General cleanup, fixes, and flailing around trying to get the scope working --- core/src/main/java/org/jruby/RubyClass.java | 287 +++++++----------- .../jruby/java/addons/ClassJavaAddons.java | 2 +- .../java/codegen/RealClassGenerator.java | 154 ++++------ .../jruby/java/proxies/ConcreteJavaProxy.java | 117 +++++-- .../org/jruby/java/proxies/FlatExtractor.java | 255 +++++++++------- .../java/proxies/JavaInterfaceTemplate.java | 2 +- .../main/java/org/jruby/javasupport/Java.java | 24 +- .../proxy/JavaProxyConstructor.java | 2 + .../util/JavaClassConfiguration.java | 4 + 9 files changed, 412 insertions(+), 435 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index 4e978791832..26080081ceb 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -31,114 +31,38 @@ package org.jruby; -import org.jruby.javasupport.JavaClass; -import org.jruby.javasupport.JavaConstructor; -import org.jruby.javasupport.ext.JavaExtensions; -import org.jruby.javasupport.proxy.JavaProxyClass; -import org.jruby.javasupport.proxy.JavaProxyConstructor; -import org.jruby.javasupport.proxy.ReifiedJavaProxy; -import org.jruby.javasupport.util.JavaClassConfiguration; -import org.jruby.parser.StaticScope; -import org.jruby.parser.StaticScopeFactory; -import org.jruby.parser.StaticScope.Type; -import org.jruby.runtime.Arity; -import org.jruby.runtime.JavaSites; -import org.jruby.runtime.callsite.CachingCallSite; -import org.jruby.runtime.callsite.RespondToCallSite; -import org.jruby.runtime.ivars.VariableAccessor; -import static org.jruby.util.CodegenUtils.ci; -import static org.jruby.util.CodegenUtils.p; -import static org.jruby.util.CodegenUtils.sig; -import static org.jruby.util.RubyStringBuilder.str; -import static org.jruby.util.RubyStringBuilder.types; -import static org.objectweb.asm.Opcodes.ACC_PRIVATE; -import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static org.objectweb.asm.Opcodes.ACC_STATIC; -import static org.objectweb.asm.Opcodes.ACC_SUPER; -import static org.objectweb.asm.Opcodes.ACC_VARARGS; -import static org.objectweb.asm.Opcodes.ACC_FINAL; +import static org.jruby.runtime.Visibility.*; +import static org.jruby.util.CodegenUtils.*; +import static org.jruby.util.RubyStringBuilder.*; +import static org.objectweb.asm.Opcodes.*; import java.io.IOException; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import org.jcodings.Encoding; -import org.jruby.anno.JRubyClass; -import org.jruby.anno.JRubyMethod; -import org.jruby.ast.ArgsNode; -import org.jruby.ast.ArrayNode; -import org.jruby.ast.BlockNode; -import org.jruby.ast.DefNode; -import org.jruby.ast.DefnNode; -import org.jruby.ast.FCallNode; -import org.jruby.ast.InstAsgnNode; -import org.jruby.ast.InstVarNode; -import org.jruby.ast.IterNode; -import org.jruby.ast.ListNode; -import org.jruby.ast.NilNode; -import org.jruby.ast.Node; -import org.jruby.ast.ReturnNode; -import org.jruby.ast.SuperNode; -import org.jruby.ast.ZSuperNode; -import org.jruby.ast.visitor.AbstractNodeVisitor; -import org.jruby.ast.visitor.NodeVisitor; +import java.lang.reflect.*; +import java.util.*; + +import org.jruby.anno.*; import org.jruby.compiler.impl.SkinnyMethodAdapter; import org.jruby.exceptions.RaiseException; -import org.jruby.internal.runtime.AbstractIRMethod; import org.jruby.internal.runtime.methods.DynamicMethod; -import org.jruby.internal.runtime.methods.MixedModeIRMethod; -import org.jruby.ir.IRMethod; -import org.jruby.java.codegen.RealClassGenerator; +import org.jruby.java.codegen.*; import org.jruby.java.codegen.RealClassGenerator.CtorFlags; -import org.jruby.java.codegen.Reified; import org.jruby.java.proxies.ConcreteJavaProxy; -import org.jruby.javasupport.Java; +import org.jruby.javasupport.*; import org.jruby.javasupport.Java.JCtorCache; -import org.jruby.runtime.Helpers; -import org.jruby.runtime.Block; -import org.jruby.runtime.CallSite; -import org.jruby.runtime.CallType; -import org.jruby.runtime.ClassIndex; -import org.jruby.runtime.MethodIndex; -import org.jruby.runtime.ObjectAllocator; -import org.jruby.runtime.ObjectMarshal; -import org.jruby.runtime.PositionAware; -import org.jruby.runtime.Signature; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.Visibility; - -import static org.jruby.runtime.Visibility.*; +import org.jruby.javasupport.proxy.*; +import org.jruby.javasupport.util.JavaClassConfiguration; +import org.jruby.lexer.yacc.SimpleSourcePosition; +import org.jruby.parser.StaticScope; +import org.jruby.runtime.*; import org.jruby.runtime.builtin.IRubyObject; -import org.jruby.runtime.callsite.CacheEntry; -import org.jruby.runtime.ivars.VariableAccessorField; -import org.jruby.runtime.ivars.VariableTableManager; -import org.jruby.runtime.marshal.MarshalStream; -import org.jruby.runtime.marshal.UnmarshalStream; +import org.jruby.runtime.callsite.*; +import org.jruby.runtime.ivars.*; +import org.jruby.runtime.marshal.*; import org.jruby.runtime.opto.Invalidator; import org.jruby.util.*; import org.jruby.util.collections.ConcurrentWeakHashMap; -import org.jruby.util.log.Logger; -import org.jruby.util.log.LoggerFactory; -import org.objectweb.asm.AnnotationVisitor; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.FieldVisitor; -import org.objectweb.asm.Label; -import org.objectweb.asm.commons.GeneratorAdapter; +import org.jruby.util.log.*; +import org.objectweb.asm.*; /** @@ -1315,7 +1239,7 @@ public boolean isReifiable(boolean[] java) { boolean result = reifiedSuper == RubyObject.class || reifiedSuper == RubyBasicObject.class || Reified.class.isAssignableFrom(reifiedSuper); - + // TODO: check for nested java classes if (result) return true; else @@ -1391,7 +1315,7 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) { final String javaName = "rubyobj." + StringSupport.replaceAll(name, "::", "."); final String javaPath = "rubyobj/" + StringSupport.replaceAll(name, "::", "/"); - final Class parentReified = superClass.getRealClass().getReifiedClass(); + final Class parentReified = superClass.getRealClass().getReifiedAnyClass(); if (parentReified == null) { throw getClassRuntime().newTypeError(getName() + "'s parent class is not yet reified"); } @@ -1480,12 +1404,21 @@ interface Reificator { byte[] reify(); } // interface Reificator + private final static PositionAware defaultSimplePosition = new SimpleSourcePosition("", 0); + + public PositionAware getPositionOrDefault(DynamicMethod method) + { + if (method instanceof PositionAware) + return (PositionAware) method; + else + return defaultSimplePosition; + } private abstract class BaseReificator implements Reificator { - public final Class reifiedParent; + public final Class reifiedParent; protected final String javaName; public final String javaPath; public final String rubyName; @@ -1500,21 +1433,18 @@ private abstract class BaseReificator implements Reificator { this.rubyName = rubyName; this.rubyPath = rubyPath; jcc = getClassConfig(); - //jcc.callInitialize = true; - //jcc.allCtors = true; - //jcc.allMethods = false; cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); cw.visit(RubyInstanceConfig.JAVA_VERSION, ACC_PUBLIC + ACC_SUPER, javaPath, null, p(reifiedParent), interfaces()); - cw.visitSource("Reifier.gen", null);//TODO: name of RB? + cw.visitSource("generated:Reificator@" + this.getClass().getName(), null); } @Override public byte[] reify() { // fields to hold Ruby and RubyClass references - cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, "ruby", ci(Ruby.class), null, null); - cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, "rubyClass", ci(RubyClass.class), null, null); + cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_STATIC | ACC_PRIVATE, "ruby", ci(Ruby.class), null, null); + cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_STATIC | ACC_PRIVATE, "rubyClass", ci(RubyClass.class), null, null); reifyConstructors(); @@ -1613,7 +1543,6 @@ public Class[] join(Class[] base, Class... extra) private class MethodReificator extends BaseReificator { - private Integer clinitKey; MethodReificator(Class reifiedParent, String javaName, String javaPath, String rubyName, String rubyPath) { super(reifiedParent, javaName, javaPath, rubyName, rubyPath); @@ -1655,6 +1584,7 @@ private void defineFields() { Class type = fieldSignature.getValue(); Map> fieldAnnos = getFieldAnnotations().get(fieldName); + // TODO: public? FieldVisitor fieldVisitor = cw.visitField(ACC_PUBLIC, fieldName, ci(type), null, null); if (fieldAnnos == null) continue; @@ -1676,6 +1606,8 @@ private void defineClassMethods(Set instanceMethods) { String id = methodEntry.getKey(); String javaMethodName = JavaNameMangler.mangleMethodName(id); + PositionAware position = getPositionOrDefault(methodEntry.getValue()); + cw.visitSource(position.getFile(), null);//TODO: ctors, etc Map> methodAnnos = getMetaClass().getMethodAnnotations().get(id); List>> parameterAnnos = getMetaClass().getParameterAnnotations().get(id); @@ -1683,7 +1615,7 @@ private void defineClassMethods(Set instanceMethods) { String signature; if (methodSignature == null) { - if (!jcc.allMethods) continue; + if (!jcc.allClassMethods) continue; final Arity arity = methodEntry.getValue().getArity(); // non-signature signature with just IRubyObject switch (arity.getValue()) { @@ -1691,10 +1623,10 @@ private void defineClassMethods(Set instanceMethods) { signature = sig(IRubyObject.class); if (instanceMethods.contains(javaMethodName + signature)) continue; m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, javaMethodName, signature, null, null); + m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); m.getstatic(javaPath, "rubyClass", ci(RubyClass.class)); - //m.invokevirtual("org/jruby/RubyClass", "getMetaClass", sig(RubyClass.class) ); m.ldc(id); m.invokevirtual("org/jruby/RubyClass", "callMethod", sig(IRubyObject.class, String.class) ); break; @@ -1702,11 +1634,12 @@ private void defineClassMethods(Set instanceMethods) { signature = sig(IRubyObject.class, IRubyObject[].class); if (instanceMethods.contains(javaMethodName + signature)) continue; m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS | ACC_STATIC, javaMethodName, signature, null, null); + m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); m.getstatic(javaPath, "rubyClass", ci(RubyClass.class)); m.ldc(id); - m.aload(0); //TODO: is this supposed to be? loadRubyObject(m); // self/rubyObject + m.aload(0); // load the parameter array m.invokevirtual("org/jruby/RubyClass", "callMethod", sig(IRubyObject.class, String.class, IRubyObject[].class) ); } m.areturn(); @@ -1722,6 +1655,7 @@ private void defineClassMethods(Set instanceMethods) { signature = sig(methodSignature[0], params); if (instanceMethods.contains(javaMethodName + signature)) continue; m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS | ACC_STATIC, javaMethodName, signature, null, null); + m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); m.getstatic(javaPath, "ruby", ci(Ruby.class)); @@ -1747,43 +1681,24 @@ private void defineInstanceMethods(Set instanceMethods) { SkinnyMethodAdapter m; for (Map.Entry methodEntry : getMethods().entrySet()) { final String id = methodEntry.getKey(); - if (id.equals("initialize")) - { - DynamicMethod dm = methodEntry.getValue(); - System.out.println(dm.getClass().getName()); - System.out.println(dm); - } + final Arity arity = methodEntry.getValue().getArity(); String javaMethodName = JavaNameMangler.mangleMethodName(id); - //MethodData md = methodEntry.getValue().getMethodData(); - PositionAware position = methodEntry.getValue() instanceof PositionAware ? (PositionAware) methodEntry.getValue() : new PositionAware() - { - - @Override - public int getLine() - { - return 0; // TODO: condense - } - - @Override - public String getFile() - { - return ""; - } - }; + PositionAware position = getPositionOrDefault(methodEntry.getValue()); cw.visitSource(position.getFile(), null);//TODO: ctors, etc Map> methodAnnos = getMethodAnnotations().get(id); List>> parameterAnnos = getParameterAnnotations().get(id); Class[] methodSignature = getMethodSignatures().get(id); + // for concrete extension, see if the method is one we are overriding, + // even if we didn't specify it manually if (methodSignature == null) - methodSignature = searchInheritedSignatures(id); + methodSignature = searchInheritedSignatures(id, arity); final String signature; if (methodSignature == null) { // non-signature signature with just IRubyObject - if (!jcc.allMethods) continue; - final Arity arity = methodEntry.getValue().getArity(); + if (!jcc.allMethods) continue; switch (arity.getValue()) { case 0: signature = sig(IRubyObject.class); // return IRubyObject foo() @@ -1868,7 +1783,7 @@ public String getFile() generateMethodAnnotations(methodAnnos, m, parameterAnnos); generateObjectBarrier(m); - m.getstatic(javaPath, "ruby", ci(Ruby.class)); // runtime + m.getstatic(javaPath, "ruby", ci(Ruby.class)); // runtime m.astore(rubyIndex); loadRubyObject(m); // self/rubyObject @@ -1887,14 +1802,20 @@ public String getFile() } } + /** + * This method generates <clinit> by marshaling the Ruby, RubyClass, etc variables + * through a static map identified by integer in JavaProxyClass. Integers are serializable + * through bytecode generation so we can share arbitrary objects with the generated class + * by saving them in {@link #getExtraClinitInfo()} via {@link JavaProxyClass#addStaticInitLookup(Object...)} + * and {@link JavaProxyClass#getStaticInitLookup(int)} + */ @Override public void reifyClinit(SkinnyMethodAdapter m) - { /// i0, o[], i1, o[] - // [] i0 [], i1 + { + // top stack layout: ..., i0, o[], i1, o[] m.pushInt(1); // rubyclass index - this.clinitKey = JavaProxyClass.addStaticInitLookup(getExtraClinitInfo()); - m.ldc(clinitKey);//TODO: add ctors + m.ldc(JavaProxyClass.addStaticInitLookup(getExtraClinitInfo())); m.invokestatic(p(JavaProxyClass.class), "getStaticInitLookup", sig(Object[].class, int.class)); m.dup_x1(); // array m.dup_x2(); // array @@ -1913,12 +1834,15 @@ protected Object[] getExtraClinitInfo() return new Object[]{runtime, RubyClass.this}; } + /** + * Override to save more values from the array in {@link #reifyClinit(SkinnyMethodAdapter)} + */ protected void extraClinitLookup(SkinnyMethodAdapter m) { m.pop(); } - protected Class[] searchInheritedSignatures(String id) + protected Class[] searchInheritedSignatures(String id, Arity arity) { return null; } @@ -1933,13 +1857,11 @@ protected void generateObjectBarrier(SkinnyMethodAdapter m) //public or private? public class ConcreteJavaReifier extends MethodReificator { - - - - //public static final String RUBY_INIT_ARGS_FIELD = "$rubyInitArgs"; - public static final String RUBY_OBJECT_FIELD = "$rubyObject"; - protected static final String RUBY_PROXY_CLASS_FIELD = "$rubyProxyClass"; - public static final String RUBY_CTOR_CACHE_FIELD = "$rubyCtorCache"; + // names follow pattern of `this$0` from javac nested classes to hopefully be ignored by + // sane reflection tools. Also similarly marked as synthetic + public static final String RUBY_OBJECT_FIELD = "this$rubyObject"; + protected static final String RUBY_PROXY_CLASS_FIELD = "this$rubyProxyClass"; + public static final String RUBY_CTOR_CACHE_FIELD = "this$rubyCtorCache"; boolean rubyctor = false; boolean simpleAlloc = false; @@ -1948,7 +1870,7 @@ public class ConcreteJavaReifier extends MethodReificator ConcreteJavaReifier(Class reifiedParent, String javaName, String javaPath) { // In theory, we should operate on IRubyObject, but everything - // that we need is a RubyBasicObject, and it has a nicer interface to boot + // that we need is a ConcreteJavaProxy, and it (via RubyBasicObject) has a nicer interface to boot super(reifiedParent, javaName, javaPath, ci(ConcreteJavaProxy.class), p(ConcreteJavaProxy.class)); } @@ -1958,7 +1880,7 @@ public void customReify() super.customReify(); if (simpleAlloc && rubyctor) - defineAllocator(); + defineAllocator();//TODO defineInterfaceMethods(); } @@ -1973,10 +1895,9 @@ protected void loadRubyObject(SkinnyMethodAdapter m) @Override public byte[] reify() { - cw.visitField(ACC_FINAL | ACC_PRIVATE, RUBY_OBJECT_FIELD, rubyName, null, null); - //cw.visitField(ACC_PRIVATE, RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class), null, null); - cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class), null, null); - cw.visitField(ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_CTOR_CACHE_FIELD, ci(JCtorCache.class), null, null); + cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_PRIVATE, RUBY_OBJECT_FIELD, rubyName, null, null); + cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class), null, null); + cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_CTOR_CACHE_FIELD, ci(JCtorCache.class), null, null); return super.reify(); } @@ -1986,6 +1907,7 @@ protected boolean isRubyObject() return false; } + // also save the ordered array of constructors @Override protected Object[] getExtraClinitInfo() { @@ -1993,6 +1915,7 @@ protected Object[] getExtraClinitInfo() } // TODO: disable alloc? + @Override protected void extraClinitLookup(SkinnyMethodAdapter m) { // extract cached ctors for lookup ordering @@ -2012,9 +1935,9 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) m.getstatic(javaPath, "rubyClass", ci(RubyClass.class)); m.ldc(org.objectweb.asm.Type.getType("L"+javaPath+";")); if (simpleAlloc) // if simple, don't init, if complex, do init - m.iconst_0(); + m.iconst_0(); // false (as int) else - m.iconst_1(); + m.iconst_1(); // true (as int) //TODO: eww, where should this call go? here? there? I dislike the leaking of the abstraction across a larger part of the code than necessary m.invokestatic(p(JavaProxyClass.class), "setProxyClassReified", sig(JavaProxyClass.class, Ruby.class, RubyClass.class, Class.class, boolean.class)); @@ -2022,8 +1945,10 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) // Note: no end, that's in the parent call } - protected Class[] searchInheritedSignatures(String id) + @Override + protected Class[] searchInheritedSignatures(String id, Arity arity) { + // Note: not stable. May flicker between different arities. TODO: sort? for (Method method : reifiedParent.getDeclaredMethods()) { //TODO: java <-> ruby conversion? @@ -2031,6 +1956,13 @@ protected Class[] searchInheritedSignatures(String id) final int mod = method.getModifiers(); if ( !Modifier.isPublic(mod) && !Modifier.isProtected(mod) ) continue; + // ensure arity is reasonable (ignores java varargs) + if (arity.isFixed()) + { + if (arity.required() != method.getParameterCount()) continue; + } + else if (arity.required() > method.getParameterCount()) continue; + // found! built a signature to return return join(new Class[]{method.getReturnType()}, method.getParameterTypes()); } @@ -2129,21 +2061,13 @@ protected void reifyConstructors() // TODO: rubyargs } - /** - * Generates an init barrier. NOT Thread-safe, but hopefully nobody has threads in their constructor? - */ - protected void generateObjectBarrier(SkinnyMethodAdapter m) - { - generateObjectBarrier(m, -1, false); - } - /** * Generates an init barrier. NOT Thread-safe, but hopefully nobody has threads in their constructor? - * - * @param m - * @param ctorArgInitialize If this is a ctor, has args not in locals, and if we should thus call initialize() + * This is used to ensure that self.to_java is valid if the super ctor calls an abstract + * method that is re-implemented by ruby */ - protected void generateObjectBarrier(SkinnyMethodAdapter m, int ctorArg, boolean initialize) + @Override + protected void generateObjectBarrier(SkinnyMethodAdapter m) { // For non-concrete things, we check, as this is not a RubyObject m.aload(0); @@ -2154,7 +2078,7 @@ protected void generateObjectBarrier(SkinnyMethodAdapter m, int ctorArg, boolean private void defineAllocator() { - SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_STATIC, "__allocate__", sig(IRubyObject.class, Ruby.class, RubyClass.class), null, null); + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_SYNTHETIC | ACC_PUBLIC | ACC_STATIC, "__allocate__", sig(IRubyObject.class, Ruby.class, RubyClass.class), null, null); m.newobj(javaPath); m.dup(); m.aload(0); // ruby @@ -2167,20 +2091,20 @@ private void defineAllocator() private void defineInterfaceMethods() { - SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "___jruby$rubyObject", sig(IRubyObject.class), null, null); + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_SYNTHETIC | ACC_PUBLIC, "___jruby$rubyObject", sig(IRubyObject.class), null, null); m.aload(0); // this m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); m.areturn(); m.end(); - m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "___jruby$proxyClass", sig(JavaProxyClass.class), null, null); + m = new SkinnyMethodAdapter(cw,ACC_SYNTHETIC | ACC_PUBLIC, "___jruby$proxyClass", sig(JavaProxyClass.class), null, null); m.getstatic(javaPath, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class)); m.areturn(); m.end(); } - } // class MethodReificator + } // class ConcreteJavaReifier private boolean isVarArgsSignature(final String method, final Class[] methodSignature) { // TODO we should simply detect "java.lang.Object m1(java.lang.Object... args)" @@ -2200,11 +2124,17 @@ public void setReifiedClass(Class reifiedClass) { this.reifiedClass = reifiedClass; } - //FIXME: no longer IRubyObject anymore - public Class getReifiedClass() { + /** + * Gets a reified Ruby or Java class. + * To ensure a specific type, see {@link #getReifiedRubyClass()} or {@link #getReifiedJavaClass()} + */ + public Class getReifiedAnyClass() { return reifiedClass; } + /** + * Gets a reified Ruby class. Throws if this is a Java class + */ public Class getReifiedRubyClass() { if (reifiedClassJava == Boolean.TRUE) // TODO: error type @@ -2212,11 +2142,14 @@ public Class getReifiedRubyClass() { else return reifiedClass; } - + + /** + * Gets a reified Java class. Throws if this is a Ruby class + */ public Class getReifiedJavaClass() { if (reifiedClassJava == Boolean.FALSE) // TODO: error type - throw runtime.newTypeError("Attempted to get a Ruby class for a Java class"); + throw runtime.newTypeError("Attempted to get a Java class for a Ruby class"); else return reifiedClass; } @@ -2224,7 +2157,7 @@ public Class getReifiedJavaClass() { public static Class nearestReifiedClass(final RubyClass klass) { RubyClass current = klass; do { - Class reified = current.getReifiedClass(); + Class reified = current.getReifiedAnyClass(); if ( reified != null ) return reified; current = current.getSuperClass(); } diff --git a/core/src/main/java/org/jruby/java/addons/ClassJavaAddons.java b/core/src/main/java/org/jruby/java/addons/ClassJavaAddons.java index fd3c156f959..99fe87dbf02 100644 --- a/core/src/main/java/org/jruby/java/addons/ClassJavaAddons.java +++ b/core/src/main/java/org/jruby/java/addons/ClassJavaAddons.java @@ -66,7 +66,7 @@ private static IRubyObject becomeJava(final ThreadContext context, final RubyCla klass.reifyWithAncestors(dumpDir, useChildLoader); - Class reifiedClass = klass.getReifiedClass(); + Class reifiedClass = klass.getReifiedAnyClass(); // TODO: ensure deleted line is no longer necessary if (reifiedClass == null) { throw context.runtime.newTypeError("requested class " + klass.getName() + " was not reifiable"); diff --git a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java index fbbdf10042f..73fdf792940 100644 --- a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java +++ b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java @@ -29,58 +29,30 @@ package org.jruby.java.codegen; -import java.io.FileOutputStream; -import java.io.IOException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; -import org.jruby.Ruby; -import org.jruby.RubyArray; -import org.jruby.RubyBasicObject; -import org.jruby.RubyClass; +import static org.jruby.RubyInstanceConfig.JAVA_VERSION; +import static org.jruby.util.CodegenUtils.*; +import static org.objectweb.asm.Opcodes.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import org.jruby.*; import org.jruby.RubyClass.ConcreteJavaReifier; -import org.jruby.RubyModule; import org.jruby.ast.executable.RuntimeCache; import org.jruby.compiler.impl.SkinnyMethodAdapter; import org.jruby.compiler.util.BasicObjectStubGenerator; import org.jruby.internal.runtime.methods.DynamicMethod; import org.jruby.java.proxies.ConcreteJavaProxy; -import org.jruby.javasupport.Java; -import org.jruby.javasupport.Java.JCreateMethod; -import org.jruby.javasupport.Java.JCtorCache; -import org.jruby.javasupport.JavaConstructor; -import org.jruby.javasupport.JavaUtil; -import org.jruby.runtime.Block; -import org.jruby.runtime.ThreadContext; +import org.jruby.javasupport.*; +import org.jruby.javasupport.Java.*; +import org.jruby.runtime.*; import org.jruby.runtime.builtin.IRubyObject; -import org.jruby.util.ASM; -import org.jruby.util.ClassDefiningClassLoader; -import org.jruby.util.ClassDefiningJRubyClassLoader; -import org.jruby.util.Loader; -import org.objectweb.asm.ClassWriter; -import org.objectweb.asm.Label; -import org.objectweb.asm.Opcodes; +import org.jruby.util.*; +import org.objectweb.asm.*; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; -import static org.jruby.RubyInstanceConfig.JAVA_VERSION; -import static org.jruby.util.CodegenUtils.ci; -import static org.jruby.util.CodegenUtils.getBoxType; -import static org.jruby.util.CodegenUtils.p; -import static org.jruby.util.CodegenUtils.params; -import static org.jruby.util.CodegenUtils.prettyParams; -import static org.jruby.util.CodegenUtils.sig; -import static org.objectweb.asm.Opcodes.ACC_FINAL; -import static org.objectweb.asm.Opcodes.ACC_PRIVATE; -import static org.objectweb.asm.Opcodes.ACC_PUBLIC; -import static org.objectweb.asm.Opcodes.ACC_STATIC; -import static org.objectweb.asm.Opcodes.ACC_SUPER; - /** * On fly .class generator (used for Ruby interface impls). * @@ -869,12 +841,44 @@ public static enum CtorFlags // shouls this be in in this spot? public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby, boolean callsInit, ConcreteJavaReifier cjr, Class[] ctorTypes, JavaConstructor[] constructors, CtorFlags flag) { + + + /* This generates this code template. Note that lines of //// show what code is being generated + * public Demo(int var1, String var2) { + Ruby var3 = ruby; + this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; + this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); + IRubyObject c = this$rubyObject.splitInitialized(this.$rubyInitArgs); + RubyArray ra = c.convertToArray(); + c = ra.entry(0); + IRubyObject continuation = ra.entry(1); + switch(Java.JCreateMethod.forTypes(constructors, c)) + { + case -1: + ra = c.convertToArray(); + thing(ra.entry(0).toJava(Integer.TYPE).longValue()); + //return; + break; + case 0: + //ra = c.convertToArray(); + thing(id); + break; + default: + throw new RuntimeException("NANANANANANA"); + } + + (this.$rubyObject.setObject(this)) + + continuation.callMethod(null, "call") + + }*/ + String sig = hasRuby ? sig(void.class, cjr.join(ctorTypes, Ruby.class, RubyClass.class)) : sig(void.class, ctorTypes); SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig, null, null); m.aload(0); // uninitialized this // set args for init - // Ruby varRubyIndex = ruby; + //// Ruby varRubyIndex = ruby; final int baseIndex = RealClassGenerator.calcBaseIndex(ctorTypes, 1); final int rubyIndex = baseIndex; final int rubyArrayIndex = baseIndex+2; @@ -886,14 +890,12 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby m.getstatic(cjr.javaPath, "ruby", ci(Ruby.class)); m.astore(rubyIndex); // save ruby in local } - // this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; + //// this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; RealClassGenerator.coerceArgumentsToRuby(m, ctorTypes, rubyIndex); -// m.dup(); m.astore(rubyInitArgs); - m.pop(); // pop the this. TODO: dont push the this -// m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); + m.pop(); // pop the `this`. TODO: dont push the this - //this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); + ////this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); { //TODO: isInit? m.newobj(p(ConcreteJavaProxy.class)); @@ -909,7 +911,7 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby m.swap(); m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_OBJECT_FIELD, cjr.rubyName); } - //IRubyObject c = this$rubyObject.splitInitialized(this.$rubyInitArgs); + ////IRubyObject c = this$rubyObject.splitInitialized(this.$rubyInitArgs); if (callsInit) //TODO: should init be called when super calls an abstract method we implement? { m.aload(rubyInitArgs); @@ -974,12 +976,9 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby // case n: for (int i = 0; i < constructors.length; i++) { - // - // - //thing(ra.entry(0).toJava(Integer.TYPE).longValue()); m.label(cases[i+1]); // skip -1 - //ra = c.convertToArray(); + ////ra = c.convertToArray(); // Note: can't pull this code up above the switch because of nils m.invokeinterface(p(IRubyObject.class), "convertToArray", sig(RubyArray.class)); m.astore(rubyArrayIndex); // .... @@ -990,6 +989,7 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby Class[] destType = constructors[i].getParameterTypes(); // coerce args. No error checking as the forTypes() call should have done that for us + ////thing(ra.entry(0).toJava(Integer.TYPE).longValue()); for (int argi = 0; argi < destType.length; argi++) { m.aload(rubyArrayIndex); @@ -997,7 +997,7 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby m.invokevirtual(p(RubyArray.class), "entry", sig(IRubyObject.class, int.class)); RealClassGenerator.coerceResult(m, destType[argi], false); } - // super(*args) + //// super(*args) m.invokespecial(p(cjr.reifiedParent), "", sig(void.class, destType)); m.go_to(endofswitch); } @@ -1009,16 +1009,9 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby else m.pop(); // TODO: ??? - // TODO: is this field still needed? - // clear args to avoid holding refs to args - //// this.$rubyInitArgs = null; -// m.aload(0); // initialized this -// m.aconst_null(); -// m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_INIT_ARGS_FIELD, ci(IRubyObject[].class)); - //implied: if (this.$rubyObject.getObject() == null) // only checked on non-ctor paths - //(this.$rubyObject.setObject(this)) + ////(this.$rubyObject.setObject(this)) m.aload(0); // initialized this m.dup(); @@ -1029,7 +1022,7 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby // finish init if started if (callsInit) { - //continuation.callMethod(ruby.getTheadContext(), "call") + ////continuation.callMethod(ruby.getTheadContext(), "call") m.aload(rubyContinuation); m.aload(rubyIndex); m.invokevirtual(p(Ruby.class), "getCurrentContext", sig(ThreadContext.class)); @@ -1038,44 +1031,9 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby m.invokeinterface(p(IRubyObject.class), "callMethod", sig(IRubyObject.class, ThreadContext.class, String.class)); m.pop(); } - m.voidreturn(); m.end(); - /* - * public Demo(int var1, String var2) { - Ruby var3 = ruby; - this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; - this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); - IRubyObject c = this$rubyObject.splitInitialized(this.$rubyInitArgs); - RubyArray ra = c.convertToArray(); - c = ra.entry(0); - IRubyObject continuation = ra.entry(1); - switch(Java.JCreateMethod.forTypes(constructors, c)) - { - case -1: - ra = c.convertToArray(); - thing(ra.entry(0).toJava(Integer.TYPE).longValue()); - //return; - break; - case 0: - //ra = c.convertToArray(); - thing(id); - break; - default: - throw new RuntimeException("NANANANANANA"); - } - - if (this.$rubyObject.getObject() == null) { - (this.$rubyObject.setObject(this)) - //?#? this.$rubyObject.callMethod("initialize", this.$rubyInitArgs); - } - this.$rubyInitArgs = null; - - continuation.callMethod(null, "call") - - }*/ - } public static GeneratorAdapter makeGenerator(SkinnyMethodAdapter m) diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index 2ca0bbb86c6..ac5003109dd 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -10,12 +10,7 @@ import org.jruby.javasupport.Java; import org.jruby.javasupport.JavaObject; import org.jruby.javasupport.proxy.ReifiedJavaProxy; -import org.jruby.runtime.Block; -import org.jruby.runtime.CallSite; -import org.jruby.runtime.MethodIndex; -import org.jruby.runtime.ObjectAllocator; -import org.jruby.runtime.ThreadContext; -import org.jruby.runtime.Visibility; +import org.jruby.runtime.*; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ByteList; @@ -104,11 +99,11 @@ private static final class NewMethod extends org.jruby.internal.runtime.methods. private DynamicMethod reifyAndNewMethod(IRubyObject clazz) { RubyClass parent = ((RubyClass)clazz); - System.err.println(parent.getName() + " is " + parent.getJavaProxy());// TODO: remove + System.err.println(parent.getName() + " is, (from NewMethod, original, a proxy) " + parent.getJavaProxy());// TODO: remove if (parent.getJavaProxy()) return newMethod; // overridden class: reify and re-lookup new as reification changes it - if (parent.getReifiedClass() == null) { + if (parent.getReifiedAnyClass() == null) { parent.reifyWithAncestors(); // TODO: is this good? } //System.err.println(parent.getName() + " is " + parent.getJavaProxy()); @@ -166,46 +161,101 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz } } - -//TODO: cleanup - public static final class NewMethodReified extends org.jruby.internal.runtime.methods.JavaMethod.JavaMethodN { - private final DynamicMethod initialize; + //TODO: cleanup + public static final class NewMethodReified extends org.jruby.internal.runtime.methods.JavaMethod.JavaMethodN { - //TODO: package? - public NewMethodReified(final RubyClass clazz) { - super(clazz, Visibility.PUBLIC, "new"); - initialize = clazz.searchMethod("__jcreate!"); - } + private final DynamicMethod initialize; + + //TODO: package? + public NewMethodReified(final RubyClass clazz) { + super(clazz, Visibility.PUBLIC, "new"); + initialize = clazz.searchMethod("__jcreate!"); + } + + @Override + public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, + IRubyObject[] args) + { + + JavaObject jo = (JavaObject)initialize.call(context, self, clazz, "new", args); + return ((ReifiedJavaProxy)jo.getValue()).___jruby$rubyObject(); + } - @Override - public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, + } + + + //TODO: cleanup + public static final class SimpleJavaInitializes { + + public static RubyArray freshMethodArray(DynamicMethod initialize, Ruby runtime, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) - { - - JavaObject jo = (JavaObject)initialize.call(context, self, clazz, "new", args); - return ((ReifiedJavaProxy)jo.getValue()).___jruby$rubyObject(); - } + { + + return runtime.newArray( + runtime.getNil(), + runtime.newProc(Block.Type.LAMBDA, new Block(new JavaInternalBlockBody(runtime, Signature.from(initialize.getArity())) + { + + @Override + public IRubyObject yield(ThreadContext _context, IRubyObject[] _args) + { + return initialize.call(_context, self, clazz, name, args); + } + + })) + ); + } + + public static RubyArray freshNopArray(Ruby runtime) + { + + return runtime.newArray( + runtime.getNil(), + runtime.newProc(Block.Type.LAMBDA, new Block(new JavaInternalBlockBody(runtime, Signature.OPTIONAL) + { + @Override + public IRubyObject yield(ThreadContext _context, IRubyObject[] _args) + { + return _context.nil; // no body/super is java + } + + })) + ); + } } // used by reified classes public RubyArray splitInitialized(IRubyObject[] args) { - + DynamicMethod dm = this.getMetaClass().searchMethod("j_initialize"); - if (!(dm instanceof AbstractIRMethod)) + DynamicMethod dma = this.getMetaClass().searchMethod("j_initialize"); + if (true || !(dm instanceof AbstractIRMethod)) { dm = getMetaClass().searchMethod("initialize"); + DynamicMethod dm1 = getMetaClass().retrieveMethod("initialize"); // only on ourself + if (true)//if (dm1 != null && !(dm instanceof InitializeMethod)) + { + //TODO: if not defined, then ctors = all valid superctors DefNode def = ((IRMethod)((AbstractIRMethod)dm).getIRScope()).desugar(); FlatExtractor flat = new FlatExtractor(this.getRuntime(), def); Node body = def.getBodyNode().accept(flat); if (!flat.foundsuper) + { System.err.println("NO SUPER"); + body = flat.buildRewrite(def.getBodyNode().getLine(), new NilNode(def.getBodyNode().getLine()), def.getBodyNode()); + } if (flat.error) System.err.println("error"); System.err.println(def.toString()); - DefNode rdnbody = new DefnNode(def.getBodyNode().getLine(), RubySymbol.newSymbol(this.getRuntime(),"j_initialize"), def.getArgsNode(), def.getScope(), body, def.getEndLine()); + DefNode rdnbody = new DefnNode(def.getBodyNode().getLine(), + RubySymbol.newSymbol(this.getRuntime(),"j_initialize"), + def.getArgsNode(), + def.getScope(), + body, + def.getEndLine()); System.err.println(rdnbody.toString()); @@ -215,10 +265,19 @@ public RubyArray splitInitialized(IRubyObject[] args) irm = new IRMethod(irm.getManager(), irm.getLexicalParent(), rdnbody, new ByteList("j_initialize".getBytes(), getRuntime().getEncodingService().getJavaDefault()), true, irm.getLine(), irm.getStaticScope(), irm.getCoverageMode()); + dm1 = dm; dm = new MixedModeIRMethod(irm, Visibility.PUBLIC, this.getMetaClass()); - + this.getMetaClass().addMethod("j_initialize", dm); - + } + else + { + //TODO: pass ruby into this + if (dm instanceof InitializeMethod) + return SimpleJavaInitializes.freshNopArray(this.getRuntime()); + else + return SimpleJavaInitializes.freshMethodArray(dm, this.getRuntime(), this, getMetaClass(), "initialize", args); + } } diff --git a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java index e54e3fe904e..b7042869920 100644 --- a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java +++ b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java @@ -3,127 +3,166 @@ import org.jruby.*; import org.jruby.ast.*; import org.jruby.ast.visitor.AbstractNodeVisitor; +import org.jruby.ir.IRScopeType; import org.jruby.parser.*; import org.jruby.runtime.Signature; class FlatExtractor extends AbstractNodeVisitor - { - private final DefNode def; - boolean found = false; - BlockNode bn = null; - int level = 0; - boolean error = false; - boolean foundsuper = false; - private Ruby runtime; - - FlatExtractor(Ruby runtime, DefNode def) - { - this.runtime = runtime; - this.def = def; - } +{ + private final DefNode def; + boolean found = false; + BlockNode bn = null; + int level = 0; + boolean error = false; + boolean foundsuper = false; + private Ruby runtime; - @Override - protected Node defaultVisit(Node node) - { - if (node == null) return null; + FlatExtractor(Ruby runtime, DefNode def) + { + this.runtime = runtime; + this.def = def; + } - if (error) - return null; - - level++; + @Override + protected Node defaultVisit(Node node) + { + if (node == null) + return null; - node.childNodes().forEach(this::defaultVisit); - level--; + if (error) + return null; - return node; - } + level++; + System.out.println("Node " + node.getLine() + "/" + node.getClass().getSimpleName()); - @Override - public Node visitBlockNode(BlockNode node) - { - if (error) - return null; - if (found) - return node; - - BlockNode replacement = new BlockNode(node.getLine()); - - level++; - - boolean seenReturn = false; - - for (Node child : node.children()) - { - Node newc = child.accept(this); - if (found) - { - if (seenReturn) - { - bn.add(newc); - continue; - } - else - { - seenReturn = true; - } - } - replacement.add(newc); - } - level--; - - return replacement; - } + node.childNodes().forEach(this::defaultVisit); + level--; + + return node; + } + + @Override + public Node visitBlockNode(BlockNode node) + { + if (error) + return null; + if (found) + return node; - @Override - public Node visitSuperNode(SuperNode node) + BlockNode replacement = new BlockNode(node.getLine()); + + level++; + + boolean seenReturn = false; + + for (Node child : node.children()) + { + Node newc = child.accept(this); + if (found) { - foundsuper = true; - if (level != 1) + if (seenReturn) { - error = true; - return null; + bn.add(newc); + continue; } - Node sarg = node.getArgsNode(); - if (sarg == null) - sarg = new ArrayNode(node.getLine()); - ArrayNode ret = new ArrayNode(node.getLine(), sarg);// TODO: block args! - ArgsNode an = new ArgsNode(node.getLine(), null, null, null, null,null); - StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); - scope.setSignature(Signature.from(an)); - //TODO: capture args? - ret.add( - new FCallNode(node.getLine(), RubySymbol.newSymbol(runtime, "lambda"), null, - new IterNode(node.getLine(), - an, - bn = new BlockNode(node.getLine()), - scope, 1))); - found = true; - return new ReturnNode(node.getLine(), ret); - // TODO Auto-generated method stub - //return super.visitSuperNode(node); - } - - @Override - public Node visitZSuperNode(ZSuperNode node) - { - foundsuper = true; - if (level != 1) + else { - error = true; - return null; + seenReturn = true; } - Node sarg = new NilNode(node.getLine()); - ArrayNode ret = new ArrayNode(node.getLine(), sarg);// TODO: block args! - ArgsNode an = new ArgsNode(node.getLine(), null, null, null, null,null); - StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope()); - scope.setSignature(Signature.from(an)); - ret.add( - new FCallNode(node.getLine(), RubySymbol.newSymbol(runtime, "lambda"), null, - new IterNode(node.getLine(), - an, - bn = new BlockNode(node.getLine()), - scope, 1))); - found = true; - return new ReturnNode(node.getLine(), ret); } - } \ No newline at end of file + replacement.add(newc); + } + level--; + + return replacement; + } + + @Override + public Node visitSuperNode(SuperNode node) + { + foundsuper = true; + if (level != 1) + { + error = true; + return null; + } + Node sarg = node.getArgsNode(); + if (sarg == null) + sarg = new ArrayNode(node.getLine()); + return buildRewrite(node.getLine(), sarg, bn = new BlockNode(node.getLine())); + } + + @Override + public Node visitZSuperNode(ZSuperNode node) + { + foundsuper = true; + if (level != 1) + { + error = true; + return null; + } + Node sarg = new NilNode(node.getLine()); + return buildRewrite(node.getLine(), sarg, bn = new BlockNode(node.getLine())); + } + + public ReturnNode buildRewrite(int line, Node sarg, Node blk) + { + ArrayNode ret = new ArrayNode(line, sarg);// TODO: block args! + ArgsNode an = new ArgsNode(line, null, null, null, null, null); + StaticScope scope = StaticScopeFactory.newIRBlockScope(def.getScope());// .duplicate() + scope.setScopeType(IRScopeType.CLOSURE); + scope.setSignature(Signature.from(an)); + // def.getArgsNode(). + def.getArgsNode().accept(new ScopeKeeper(scope)); + + // TODO: capture args? + ret.add(new LambdaNode(line, an, blk, scope, line) + /* + * new FCallNode(line, RubySymbol.newSymbol(runtime, "lambda"), null, new + * IterNode(line, an, blk, scope, 1)) + */); + found = true; + return new ReturnNode(line, ret); + } + + public static class ScopeKeeper extends AbstractNodeVisitor + { + private StaticScope scope; + + ScopeKeeper(StaticScope ss) + { + this.scope = ss; + } + + @Override + protected StaticScope defaultVisit(Node node) + { + if (node == null) + return null; + + node.childNodes().forEach(n -> n.accept(this)); + return scope; + } + + @Override + public StaticScope visitArgumentNode(ArgumentNode node) + { + scope.addVariableThisScope(node.getName().toString()); + return scope; + } + + @Override + public StaticScope visitRestArgNode(RestArgNode node) + { + scope.addVariableThisScope(node.getName().toString()); + return scope; + } + + @Override + public StaticScope visitKeywordRestArgNode(KeywordRestArgNode node) + { + scope.addVariableThisScope(node.getName().toString()); + return scope; + } + } +} \ No newline at end of file diff --git a/core/src/main/java/org/jruby/java/proxies/JavaInterfaceTemplate.java b/core/src/main/java/org/jruby/java/proxies/JavaInterfaceTemplate.java index 7552906427e..30fa7cd75fe 100644 --- a/core/src/main/java/org/jruby/java/proxies/JavaInterfaceTemplate.java +++ b/core/src/main/java/org/jruby/java/proxies/JavaInterfaceTemplate.java @@ -130,7 +130,7 @@ private static void checkAlreadyReified(final RubyClass clazz, Ruby runtime) thr // not allowed for original (non-generated) Java classes // note: not allowing for any previously created class right now; // this restriction might be loosened later for generated classes - if ( ( Java.NEW_STYLE_EXTENSION && clazz.getReifiedClass() != null ) + if ( ( Java.NEW_STYLE_EXTENSION && clazz.getReifiedAnyClass() != null ) || ( clazz.hasInstanceVariable("@java_class") && clazz.getInstanceVariable("@java_class").isTrue() diff --git a/core/src/main/java/org/jruby/javasupport/Java.java b/core/src/main/java/org/jruby/javasupport/Java.java index fd7312f3345..900d103a8d1 100644 --- a/core/src/main/java/org/jruby/javasupport/Java.java +++ b/core/src/main/java/org/jruby/javasupport/Java.java @@ -703,6 +703,7 @@ public final IRubyObject call(final ThreadContext context, IRubyObject self, Rub if (self instanceof JavaProxy) { + System.err.println("Failed nil Init for JavaProxy: " + self.asJavaString()); return context.nil; } JavaObject newObject = matching.newInstance(self, args); @@ -736,8 +737,7 @@ private JavaProxyConstructor matchConstructor0ArityOne(final ThreadContext conte // assumes only 1 constructor exists! private JavaProxyConstructor matchConstructor0(final ThreadContext context, final JavaProxyConstructor[] constructors, final int arity, final IRubyObject[] args) { - int index = constructors[0].isExportable() ? 1 : 0; - JavaProxyConstructor forArity = checkCallableForArity(arity, constructors, index); + JavaProxyConstructor forArity = checkCallableForArity(arity, constructors, 0); if ( forArity == null ) { throw context.runtime.newArgumentError("wrong number of arguments for constructor"); @@ -757,13 +757,6 @@ private JavaProxyConstructor matchConstructorArityOne(final ThreadContext contex final JavaProxyConstructor[] constructors, final IRubyObject arg0) { ArrayList forArity = findCallablesForArity(1, constructors); - // remove java-only methods - Iterator iter = forArity.iterator(); - while (iter.hasNext()) { - if(iter.next().isExportable()) - iter.remove(); - } - if ( forArity.size() == 0 ) { throw context.runtime.newArgumentError("wrong number of arguments for constructor"); } @@ -782,13 +775,6 @@ private JavaProxyConstructor matchConstructorArityOne(final ThreadContext contex public JavaProxyConstructor matchConstructor(final ThreadContext context, final JavaProxyConstructor[] constructors, final int arity, final IRubyObject... args) { ArrayList forArity = findCallablesForArity(arity, constructors); - - // remove java-only methods: //TODO: ??? - Iterator iter = forArity.iterator(); - while (iter.hasNext()) { - if(iter.next().isExportable()) - iter.remove(); - } if ( forArity.size() == 0 ) { throw context.runtime.newArgumentError("wrong number of arguments for constructor"); @@ -807,10 +793,6 @@ public JavaProxyConstructor matchConstructor(final ThreadContext context, public static T matchConstructorIndex(final ThreadContext context, final T[] constructors, final CallableCache cache, final int arity, final IRubyObject... args) { ArrayList forArity = findCallablesForArity(arity, constructors); - - // remove java-only methods: //TODO: ??? - - //new JavaProxyConstructor(); if ( forArity.size() == 0 ) { throw context.runtime.newArgumentError("wrong number of arguments for constructor"); @@ -1601,7 +1583,7 @@ public static Class generateRealClass(final RubyClass clazz) { catch (ClassNotFoundException ex) { // try to use super's reified class; otherwise, RubyObject (for now) //TODO: test java reified? - Class superClass = clazz.getSuperClass().getRealClass().getReifiedClass(); + Class superClass = clazz.getSuperClass().getRealClass().getReifiedAnyClass(); if ( superClass == null ) superClass = RubyObject.class; proxyImplClass = RealClassGenerator.createRealImplClass(superClass, interfaces, clazz, runtime, implClassName); diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java index 64cd9c7055e..954375bbb20 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java @@ -229,6 +229,7 @@ public final JavaObject newInstance(final IRubyObject self, IRubyObject arg0) th private static RaiseException mapInstantiationException(final Ruby runtime, final Throwable e) { Throwable cause = e; + e.printStackTrace(); while ( cause.getCause() != null ) cause = cause.getCause(); final String MSG = "Constructor invocation failed: "; String msg = cause.getLocalizedMessage(); @@ -258,6 +259,7 @@ public RubyObject new_instance(IRubyObject[] args, Block block) { return JavaObject.wrap(runtime, newInstance(convertedArgs, runtime, proc)); } catch (Exception e) { + e.printStackTrace(); RaiseException ex = runtime.newArgumentError("Constructor invocation failed: " + e.getMessage()); ex.initCause(e); throw ex; diff --git a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java index d7c4c8ecc77..a3f164d4eea 100644 --- a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java +++ b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java @@ -4,6 +4,7 @@ public class JavaClassConfiguration { public boolean callInitialize = false; public boolean allMethods = true; + public boolean allClassMethods = false; // TODO: ensure defaults are sane public boolean javaConstructable = true; public Class[][] extraCtors = null; @@ -12,4 +13,7 @@ public class JavaClassConfiguration public boolean rubyConstructable = true; // public boolean splitSuper = true; public boolean IroCtors = true; + + //TODO: init method name? + //TODO: renames? } From be1653ab224b184b5094ee1bc4e2eeaea9f7e676 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Tue, 17 Nov 2020 00:28:36 -0500 Subject: [PATCH 12/49] Add support for split allocate+initialize and arbitrary ruby argument fallback. Also some misc cleanup --- core/src/main/java/org/jruby/RubyClass.java | 45 +++-- .../java/codegen/RealClassGenerator.java | 102 ++++++----- .../jruby/java/proxies/ConcreteJavaProxy.java | 161 ++++++++++++++++-- .../org/jruby/java/proxies/FlatExtractor.java | 5 +- .../main/java/org/jruby/javasupport/Java.java | 5 +- .../javasupport/proxy/JavaProxyClass.java | 8 +- .../proxy/JavaProxyConstructor.java | 4 +- .../util/JavaClassConfiguration.java | 3 +- .../java/org/jruby/runtime/PositionAware.java | 2 +- 9 files changed, 251 insertions(+), 84 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index 26080081ceb..2c8e9937e14 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -1361,10 +1361,17 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) { { setRubyStaticAllocator(result); } + else if (((ConcreteJavaReifier)reifier).jcc.IroCtors) + { + //TODO: ensure CJP allocator is set + //setAllocator(ConcreteJavaProxy.ALLOCATOR); + //setRubyStaticAllocator(result); + } else { - /**Allocator "set" via clinit @see JavaProxyClass.setProxyClassReified + /**Allocator "set" via clinit {@see JavaProxyClass#setProxyClassReified()} */ + //TODO: disable alloc method } // update java_class @@ -1409,7 +1416,10 @@ interface Reificator { public PositionAware getPositionOrDefault(DynamicMethod method) { if (method instanceof PositionAware) - return (PositionAware) method; + { + PositionAware pos = (PositionAware) method; + return new SimpleSourcePosition(pos.getFile(), pos.getLine()+1); // convert from 0-based to 1-based that the JVM requires + } else return defaultSimplePosition; } @@ -1519,7 +1529,7 @@ protected void reifyConstructors() allocAndInitialize(m, true); } } - + // TODO: allow java to pass args? protected void allocAndInitialize(SkinnyMethodAdapter m, boolean initIfAllowed) { m.invokespecial(p(reifiedParent), "", sig(void.class, Ruby.class, RubyClass.class)); @@ -1607,7 +1617,7 @@ private void defineClassMethods(Set instanceMethods) { String javaMethodName = JavaNameMangler.mangleMethodName(id); PositionAware position = getPositionOrDefault(methodEntry.getValue()); - cw.visitSource(position.getFile(), null);//TODO: ctors, etc + if (position.getLine() > 1) cw.visitSource(position.getFile(), null); Map> methodAnnos = getMetaClass().getMethodAnnotations().get(id); List>> parameterAnnos = getMetaClass().getParameterAnnotations().get(id); @@ -1685,7 +1695,7 @@ private void defineInstanceMethods(Set instanceMethods) { String javaMethodName = JavaNameMangler.mangleMethodName(id); PositionAware position = getPositionOrDefault(methodEntry.getValue()); - cw.visitSource(position.getFile(), null);//TODO: ctors, etc + if (position.getLine() > 1) cw.visitSource(position.getFile(), null); Map> methodAnnos = getMethodAnnotations().get(id); List>> parameterAnnos = getParameterAnnotations().get(id); @@ -1985,6 +1995,12 @@ protected void reifyConstructors() } } // TODO: guess from arity? + + // update the source location + DynamicMethod methodEntry = searchMethod("initialize"); + PositionAware position = getPositionOrDefault(methodEntry); + cw.visitSource(position.getFile(), null); + int superpos = ConcreteJavaProxy.findSuperLine(runtime, methodEntry, position.getLine()); if (candidates.size() > 0 ) //TODO: doc: implies javaConstructable? @@ -2011,12 +2027,12 @@ protected void reifyConstructors() if (!jcc.allCtors) //TODO: fix logic { //if (jcc.rubyConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, true, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); if (jcc.javaConstructable) { - RealClassGenerator.makeConcreteConstructorSwitch(cw, false, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); } } } @@ -2033,10 +2049,10 @@ protected void reifyConstructors() // if (zeroArg.isPresent() && constructor == zeroArg.get()) continue; if (jcc.rubyConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, true, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); if (jcc.javaConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, false, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); } } @@ -2051,14 +2067,17 @@ protected void reifyConstructors() // TODO: support annotations if (jcc.rubyConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, true, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); if (jcc.javaConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, false, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); } - } - // TODO: rubyargs + } + if (jcc.IroCtors) + { + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, new Class[] {ConcreteJavaProxy.class, IRubyObject[].class, Block.class}, savedSuperCtors, CtorFlags.RubyArgs); + } } /** diff --git a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java index 73fdf792940..760f4acf70b 100644 --- a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java +++ b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java @@ -832,42 +832,42 @@ public static enum CtorFlags { Normal, NoSuper, // implicit `super` not allowed, no parent ctor with this signature - RubyArgs, // NoSuper + args don't need converting - RubyArgsBlock, // RubyArgs, plus has a block arg + RubyArgs, // NoSuper + args don't need converting (also includes the CJP and block args) } // TODO: add CJP ctor for alloc sep? //:TODO: Add IRubyobject ctor? // shouls this be in in this spot? - public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby, boolean callsInit, ConcreteJavaReifier cjr, Class[] ctorTypes, JavaConstructor[] constructors, CtorFlags flag) + public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware initPosition, int superpos, boolean hasRuby, boolean callsInit, ConcreteJavaReifier cjr, Class[] ctorTypes, JavaConstructor[] constructors, CtorFlags flag) { + // TODO: add source position of super call /* This generates this code template. Note that lines of //// show what code is being generated * public Demo(int var1, String var2) { - Ruby var3 = ruby; - this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; - this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); - IRubyObject c = this$rubyObject.splitInitialized(this.$rubyInitArgs); - RubyArray ra = c.convertToArray(); - c = ra.entry(0); - IRubyObject continuation = ra.entry(1); - switch(Java.JCreateMethod.forTypes(constructors, c)) - { - case -1: - ra = c.convertToArray(); - thing(ra.entry(0).toJava(Integer.TYPE).longValue()); - //return; - break; - case 0: - //ra = c.convertToArray(); - thing(id); - break; - default: - throw new RuntimeException("NANANANANANA"); - } - - (this.$rubyObject.setObject(this)) + Ruby var3 = ruby; + this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; + this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); + IRubyObject c = this$rubyObject.splitInitialized(this.$rubyInitArgs); + RubyArray ra = c.convertToArray(); + c = ra.entry(0); + IRubyObject continuation = ra.entry(1); + switch(Java.JCreateMethod.forTypes(constructors, c)) + { + case -1: + ra = c.convertToArray(); + thing(ra.entry(0).toJava(Integer.TYPE).longValue()); + //return; + break; + case 0: + //ra = c.convertToArray(); + thing(id); + break; + default: + throw new RuntimeException("NANANANANANA"); + } + + (this.$rubyObject.setObject(this)) continuation.callMethod(null, "call") @@ -875,6 +875,7 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby String sig = hasRuby ? sig(void.class, cjr.join(ctorTypes, Ruby.class, RubyClass.class)) : sig(void.class, ctorTypes); SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, "", sig, null, null); + m.line(initPosition.getLine()); m.aload(0); // uninitialized this // set args for init @@ -884,28 +885,37 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby final int rubyArrayIndex = baseIndex+2; final int rubyContinuation = baseIndex+3; final int rubyInitArgs = baseIndex+4; - m.dup(); // uninitialized this + //m.dup(); // uninitialized this if (!hasRuby) // if java, extract and pretend we got a call with ruby on a local { m.getstatic(cjr.javaPath, "ruby", ci(Ruby.class)); m.astore(rubyIndex); // save ruby in local } //// this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; - RealClassGenerator.coerceArgumentsToRuby(m, ctorTypes, rubyIndex); + if (flag == CtorFlags.Normal || flag == CtorFlags.NoSuper) + RealClassGenerator.coerceArgumentsToRuby(m, ctorTypes, rubyIndex); + else + m.aload(2); // RubyArgs is arg 2 m.astore(rubyInitArgs); - m.pop(); // pop the `this`. TODO: dont push the this + //m.pop(); // pop the `this`. TODO: dont push the this ////this.$rubyObject = new ConcreteJavaProxy(ruby, rubyClass); { - //TODO: isInit? - m.newobj(p(ConcreteJavaProxy.class)); - m.dup(); // rubyobject - m.aload(rubyIndex); // ruby - if (hasRuby) - m.aload(rubyIndex+1); // rubyclass - else - m.getstatic(cjr.javaPath, "rubyClass", ci(RubyClass.class)); // rubyclass - m.invokespecial(p(ConcreteJavaProxy.class), "", sig(void.class, Ruby.class, RubyClass.class)); + if (flag == CtorFlags.Normal || flag == CtorFlags.NoSuper) + { + m.newobj(p(ConcreteJavaProxy.class)); + m.dup(); // rubyobject + m.aload(rubyIndex); // ruby + if (hasRuby) + m.aload(rubyIndex+1); // rubyclass + else + m.getstatic(cjr.javaPath, "rubyClass", ci(RubyClass.class)); // rubyclass + m.invokespecial(p(ConcreteJavaProxy.class), "", sig(void.class, Ruby.class, RubyClass.class)); + } + else + { + m.aload(1); // cjp is at arg 1 (to support alloc+initialize seperation) + } m.dup(); // rubyobject m.aload(0); // uninitialized this m.swap(); @@ -915,9 +925,18 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby if (callsInit) //TODO: should init be called when super calls an abstract method we implement? { m.aload(rubyInitArgs); - m.invokevirtual(cjr.rubyPath, "splitInitialized", sig(RubyArray.class, IRubyObject[].class) ); //pushes rubyarray + if (flag == CtorFlags.Normal || flag == CtorFlags.NoSuper) + { + m.getstatic(p(Block.class), "NULL_BLOCK", ci(Block.class)); + } + else + { + m.aload(3); // load block from arg 3 + } + m.invokevirtual(cjr.rubyPath, "splitInitialized", sig(RubyArray.class, IRubyObject[].class, Block.class) ); //pushes rubyarray m.dup(); // rubyarray (results of splitInitialized) + m.line(superpos); // mark this line as the super call, so the stack trace is slightly accurate. //// c = ra.entry(0); ////IRubyObject continuation = ra.entry(1); @@ -1007,9 +1026,10 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, boolean hasRuby } else - m.pop(); // TODO: ??? + m.pop(); // RubyObject + + m.line(initPosition.getLine()); // This is the start of the method, but lets move it away from the super call to be slightly nicer to stack traces - //implied: if (this.$rubyObject.getObject() == null) // only checked on non-ctor paths ////(this.$rubyObject.setObject(this)) diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index ac5003109dd..c4c54efbd6a 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -1,18 +1,26 @@ package org.jruby.java.proxies; -import java.lang.reflect.Field; +import static org.jruby.runtime.Visibility.PUBLIC; + +import java.lang.reflect.*; +import java.util.ArrayList; import org.jruby.*; import org.jruby.ast.*; +import org.jruby.exceptions.ArgumentError; import org.jruby.internal.runtime.AbstractIRMethod; import org.jruby.internal.runtime.methods.*; +import org.jruby.internal.runtime.methods.JavaMethod.*; import org.jruby.ir.IRMethod; -import org.jruby.javasupport.Java; -import org.jruby.javasupport.JavaObject; -import org.jruby.javasupport.proxy.ReifiedJavaProxy; +import org.jruby.java.dispatch.CallableSelector; +import org.jruby.java.dispatch.CallableSelector.CallableCache; +import org.jruby.javasupport.*; +import org.jruby.javasupport.Java.JCtorCache; +import org.jruby.javasupport.proxy.*; import org.jruby.runtime.*; import org.jruby.runtime.builtin.IRubyObject; import org.jruby.util.ByteList; +import org.jruby.util.collections.NonBlockingHashMapLong; public class ConcreteJavaProxy extends JavaProxy { @@ -24,7 +32,7 @@ public ConcreteJavaProxy(Ruby runtime, RubyClass klazz, Object object) { super(runtime, klazz, object); } - private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { + public static final ObjectAllocator ALLOCATOR = new ObjectAllocator() { public IRubyObject allocate(Ruby runtime, RubyClass klazz) { return new ConcreteJavaProxy(runtime, klazz); } @@ -107,7 +115,7 @@ private DynamicMethod reifyAndNewMethod(IRubyObject clazz) { parent.reifyWithAncestors(); // TODO: is this good? } //System.err.println(parent.getName() + " is " + parent.getJavaProxy()); - return new NewMethodReified(parent); + return new NewMethodReified(parent, parent.getReifiedJavaClass()); } @Override @@ -161,25 +169,129 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz } } + + public static class StaticJCreateMethod extends JavaMethodNBlock { + + private Constructor withBlock; + private DynamicMethod oldInit; + + StaticJCreateMethod(RubyModule cls, Constructor withBlock2, DynamicMethod oldinit) { + super(cls, PUBLIC, "__jcreate_static!"); + this.withBlock = withBlock2; + this.oldInit = oldinit; + } + + @Override + public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, + IRubyObject[] args, Block block) + { + try + { + withBlock.newInstance((ConcreteJavaProxy)self, args, block, context.runtime, clazz); + // note: the generated ctor sets self.object = our discarded return of the new object + } + catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return self; + } + + + public DynamicMethod getOriginal() + { + return oldInit; + } + + + + public static void tryInstall(Ruby runtime, RubyClass clazz, JavaProxyClass proxyClass, + Class reified) + { + try + { + Constructor withBlock = reified.getConstructor( + new Class[] { ConcreteJavaProxy.class, IRubyObject[].class, Block.class, + Ruby.class, RubyClass.class}); + //TODO: move initialize to real_initialize + //TODO: don't lock in this initialize method + clazz.addMethod("initialize", new StaticJCreateMethod(clazz, withBlock, clazz.searchMethod("initialize"))); + } + catch (SecurityException | NoSuchMethodException e) + { + // TODO log? + e.printStackTrace(); + // ignore, don't install + } + } + } //TODO: cleanup - public static final class NewMethodReified extends org.jruby.internal.runtime.methods.JavaMethod.JavaMethodN { + public static final class NewMethodReified extends org.jruby.internal.runtime.methods.JavaMethod.JavaMethodNBlock { private final DynamicMethod initialize; + private final Constructor ctor; //TODO: package? - public NewMethodReified(final RubyClass clazz) { + public NewMethodReified(final RubyClass clazz, Class reified) { super(clazz, Visibility.PUBLIC, "new"); initialize = clazz.searchMethod("__jcreate!"); + + Constructor withBlock; + try + { + withBlock = reified.getConstructor( + new Class[] { ConcreteJavaProxy.class, IRubyObject[].class, Block.class, + Ruby.class, RubyClass.class}); + } + catch (SecurityException | NoSuchMethodException e) + { + // ignore, don't install + withBlock = null; + } + ctor = withBlock; } @Override public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, - IRubyObject[] args) + IRubyObject[] args, Block blk) { - - JavaObject jo = (JavaObject)initialize.call(context, self, clazz, "new", args); - return ((ReifiedJavaProxy)jo.getValue()).___jruby$rubyObject(); + //TODO: deduplicate this method, and decide on order of preference after testing + if (ctor == null) + { + JavaObject jo = (JavaObject)initialize.call(context, self, clazz, "new", args); + return ((ReifiedJavaProxy)jo.getValue()).___jruby$rubyObject(); + } + else + { + try + { + + JavaObject jo = (JavaObject)initialize.call(context, self, clazz, "new", args); + return ((ReifiedJavaProxy)jo.getValue()).___jruby$rubyObject(); + } + catch (ArgumentError ae) + { + System.out.println("AE"); + // assume no easy conversions, use ruby fallback. + ConcreteJavaProxy object = new ConcreteJavaProxy(context.runtime, (RubyClass) self); + try + { + ctor.newInstance(object, args, blk, context.runtime, clazz); + // note: the generated ctor sets self.object = our discarded return of the new object + } + catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) + { + //TODO: ??? + //TODO: throw if the ctor did this. Copy JPConstructor code here + throw context.runtime.newStandardError("Implementation did something wrong. Please file a bug. " + e.getMessage()); + } + return object; + } + } } } @@ -226,17 +338,33 @@ public IRubyObject yield(ThreadContext _context, IRubyObject[] _args) } + public static int findSuperLine(Ruby runtime, DynamicMethod dm, int start) + { + if (dm != null && !(dm instanceof InitializeMethod)) + { + //TODO: if not defined, then ctors = all valid superctors + DefNode def = ((IRMethod)((AbstractIRMethod)dm).getIRScope()).desugar(); + FlatExtractor flat = new FlatExtractor(runtime, def); + Node body = def.getBodyNode().accept(flat); + if (flat.foundsuper && flat.superline > -1) + return flat.superline + 1; // convert from 0-based to 1-based + } + return start; + } + // used by reified classes - public RubyArray splitInitialized(IRubyObject[] args) + public RubyArray splitInitialized(IRubyObject[] args, Block blk) { DynamicMethod dm = this.getMetaClass().searchMethod("j_initialize"); DynamicMethod dma = this.getMetaClass().searchMethod("j_initialize"); - if (true || !(dm instanceof AbstractIRMethod)) + if (!(dm instanceof AbstractIRMethod)) { dm = getMetaClass().searchMethod("initialize"); + if (dm != null && (dm instanceof StaticJCreateMethod)) + dm = ((StaticJCreateMethod)dm).getOriginal(); DynamicMethod dm1 = getMetaClass().retrieveMethod("initialize"); // only on ourself - if (true)//if (dm1 != null && !(dm instanceof InitializeMethod)) + if (dm1 != null && !(dm instanceof InitializeMethod)) { //TODO: if not defined, then ctors = all valid superctors DefNode def = ((IRMethod)((AbstractIRMethod)dm).getIRScope()).desugar(); @@ -280,9 +408,8 @@ public RubyArray splitInitialized(IRubyObject[] args) } } - /// TODO: move gen here - return callMethod("j_initialize", args).convertToArray(); + return callMethod(getRuntime().getCurrentContext(), "j_initialize", args, blk).convertToArray(); } // used by reified classes diff --git a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java index b7042869920..7eae1da1405 100644 --- a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java +++ b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java @@ -10,11 +10,12 @@ class FlatExtractor extends AbstractNodeVisitor { private final DefNode def; - boolean found = false; BlockNode bn = null; int level = 0; boolean error = false; + private boolean found = false; boolean foundsuper = false; + int superline = -1; private Ruby runtime; FlatExtractor(Ruby runtime, DefNode def) @@ -86,6 +87,7 @@ public Node visitSuperNode(SuperNode node) error = true; return null; } + superline = node.getLine(); Node sarg = node.getArgsNode(); if (sarg == null) sarg = new ArrayNode(node.getLine()); @@ -101,6 +103,7 @@ public Node visitZSuperNode(ZSuperNode node) error = true; return null; } + superline = node.getLine(); Node sarg = new NilNode(node.getLine()); return buildRewrite(node.getLine(), sarg, bn = new BlockNode(node.getLine())); } diff --git a/core/src/main/java/org/jruby/javasupport/Java.java b/core/src/main/java/org/jruby/javasupport/Java.java index 900d103a8d1..115ee5b3311 100644 --- a/core/src/main/java/org/jruby/javasupport/Java.java +++ b/core/src/main/java/org/jruby/javasupport/Java.java @@ -703,7 +703,7 @@ public final IRubyObject call(final ThreadContext context, IRubyObject self, Rub if (self instanceof JavaProxy) { - System.err.println("Failed nil Init for JavaProxy: " + self.asJavaString()); + System.err.println("Failed nil Init for JavaProxy: " + self.anyToString()); return context.nil; } JavaObject newObject = matching.newInstance(self, args); @@ -717,8 +717,7 @@ public final IRubyObject call(final ThreadContext context, IRubyObject self, Rub // assumes only 1 *Ruby* constructor exists! (Filters out nonruby) private JavaProxyConstructor matchConstructor0ArityOne(final ThreadContext context, final JavaProxyConstructor[] constructors, final IRubyObject arg0) { - int index = constructors[0].isExportable() ? 1 : 0; - JavaProxyConstructor forArity = checkCallableForArity(1, constructors, index); + JavaProxyConstructor forArity = checkCallableForArity(1, constructors, 0); if ( forArity == null ) { throw context.runtime.newArgumentError("wrong number of arguments for constructor"); diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java index 2c75fc15232..6665f28347e 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java @@ -63,7 +63,7 @@ import org.jruby.exceptions.RaiseException; import org.jruby.internal.runtime.methods.DynamicMethod; import org.jruby.java.codegen.Reified; -import org.jruby.java.proxies.ConcreteJavaProxy.NewMethodReified; +import org.jruby.java.proxies.ConcreteJavaProxy.*; import org.jruby.javasupport.*; import org.jruby.runtime.ObjectAllocator; import org.jruby.runtime.builtin.IRubyObject; @@ -621,14 +621,16 @@ public static JavaProxyClass setProxyClassReified(final Ruby runtime, final Ruby //TODO: //JavaSupportImpl.saveJavaProxyClass(runtime, classKey, proxyClass); clazz.setInstanceVariable("@java_proxy_class", proxyClass); - + + StaticJCreateMethod.tryInstall(runtime, clazz, proxyClass, reified); RubyClass singleton = clazz.getSingletonClass(); System.err.println("Setting on " + clazz.toString() + " + single " + singleton.toString()); singleton.setInstanceVariable("@java_proxy_class", proxyClass); singleton.setInstanceVariable("@java_class", Java.wrapJavaObject(runtime, reified)); - if (allocator) singleton.addMethod("new", new NewMethodReified(clazz)); + + if (allocator) singleton.addMethod("new", new NewMethodReified(clazz, reified)); return proxyClass; } diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java index 954375bbb20..3f956fcb4e7 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java @@ -260,9 +260,7 @@ public RubyObject new_instance(IRubyObject[] args, Block block) { } catch (Exception e) { e.printStackTrace(); - RaiseException ex = runtime.newArgumentError("Constructor invocation failed: " + e.getMessage()); - ex.initCause(e); - throw ex; + throw mapInstantiationException(runtime, e); } } diff --git a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java index a3f164d4eea..c6534c1f2fc 100644 --- a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java +++ b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java @@ -2,7 +2,7 @@ public class JavaClassConfiguration { - public boolean callInitialize = false; + public boolean callInitialize = true; public boolean allMethods = true; public boolean allClassMethods = false; // TODO: ensure defaults are sane public boolean javaConstructable = true; @@ -11,7 +11,6 @@ public class JavaClassConfiguration // for java proxies public boolean allCtors = false; public boolean rubyConstructable = true; // - public boolean splitSuper = true; public boolean IroCtors = true; //TODO: init method name? diff --git a/core/src/main/java/org/jruby/runtime/PositionAware.java b/core/src/main/java/org/jruby/runtime/PositionAware.java index 3180b9ef7a1..eff7397201e 100644 --- a/core/src/main/java/org/jruby/runtime/PositionAware.java +++ b/core/src/main/java/org/jruby/runtime/PositionAware.java @@ -14,7 +14,7 @@ public interface PositionAware { public String getFile(); /** - * Get the line number for the method. + * Get the line number for the method. 0-based (ie. line 1 returns a 0) * * @return the line number for the method */ From ef4375752a94954d7770e51424af3edcac469f66 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Tue, 17 Nov 2020 04:12:05 -0500 Subject: [PATCH 13/49] Working scoping for most code. Missing some elements still --- core/src/main/java/org/jruby/RubyClass.java | 5 +- .../main/java/org/jruby/ast/ComplexNode.java | 4 +- .../main/java/org/jruby/ast/PostExeNode.java | 4 +- .../main/java/org/jruby/ast/PreExeNode.java | 4 +- .../main/java/org/jruby/ast/RestArgNode.java | 1 + .../main/java/org/jruby/ast/SClassNode.java | 6 +- .../ast/visitor/AbstractNodeVisitor.java | 5 + .../org/jruby/ast/visitor/NodeVisitor.java | 1 + .../jruby/ast/visitor/OperatorCallNode.java | 8 + .../ir/interpreter/InterpreterContext.java | 2 +- .../jruby/java/proxies/ConcreteJavaProxy.java | 7 +- .../org/jruby/java/proxies/FlatExtractor.java | 515 ++++++++++++++++-- 12 files changed, 506 insertions(+), 56 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index 2c8e9937e14..20c86850768 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -1380,7 +1380,7 @@ else if (((ConcreteJavaReifier)reifier).jcc.IroCtors) else setRubyClassAllocator(result); reifiedClass = result; - + return; // success } catch (LinkageError error) { // fall through to failure path @@ -2041,6 +2041,9 @@ protected void reifyConstructors() //TODO: copy jpcf logic for alloc throw new RuntimeException("Class doesn't have no-arg ctor"); }*/ + + //TODO: remove rubyCtors if IRO is enabled (by default) + //TODO: warn if no ruby ctors (arity check?) if (jcc.allCtors) { for (Constructor constructor : candidates) diff --git a/core/src/main/java/org/jruby/ast/ComplexNode.java b/core/src/main/java/org/jruby/ast/ComplexNode.java index 4da67c5d065..cdca692c379 100644 --- a/core/src/main/java/org/jruby/ast/ComplexNode.java +++ b/core/src/main/java/org/jruby/ast/ComplexNode.java @@ -16,10 +16,10 @@ public class ComplexNode extends NumericNode implements SideEffectFree { private NumericNode y; - public ComplexNode(int line, NumericNode y) { + public ComplexNode(int line, NumericNode number) { super(line); - this.y = y; + this.y = number; } @Override diff --git a/core/src/main/java/org/jruby/ast/PostExeNode.java b/core/src/main/java/org/jruby/ast/PostExeNode.java index 3c7bf2042e0..c4f676a3065 100644 --- a/core/src/main/java/org/jruby/ast/PostExeNode.java +++ b/core/src/main/java/org/jruby/ast/PostExeNode.java @@ -38,8 +38,8 @@ * Captures END statements (END {...}) */ public class PostExeNode extends IterNode { - public PostExeNode(int line, Node body, int endLine) { - super(line, new ArgsNode(line, null, null, null, null, null, null, null), body, null, endLine); + public PostExeNode(int line, Node bodyNode, int endLine) { + super(line, new ArgsNode(line, null, null, null, null, null, null, null), bodyNode, null, endLine); } public NodeType getNodeType() { diff --git a/core/src/main/java/org/jruby/ast/PreExeNode.java b/core/src/main/java/org/jruby/ast/PreExeNode.java index 52084c88518..5fc94bbb84e 100644 --- a/core/src/main/java/org/jruby/ast/PreExeNode.java +++ b/core/src/main/java/org/jruby/ast/PreExeNode.java @@ -36,8 +36,8 @@ * A pre-execution construction (BEGIN { ... }). */ public class PreExeNode extends IterNode { - public PreExeNode(int line, StaticScope scope, Node body, int endLine) { - super(line, new ArgsNode(line, null, null, null, null, null, null, null), body, scope, endLine); + public PreExeNode(int line, StaticScope scope, Node bodyNode, int endLine) { + super(line, new ArgsNode(line, null, null, null, null, null, null, null), bodyNode, scope, endLine); } @Override diff --git a/core/src/main/java/org/jruby/ast/RestArgNode.java b/core/src/main/java/org/jruby/ast/RestArgNode.java index d61926c4921..2a5422999e2 100644 --- a/core/src/main/java/org/jruby/ast/RestArgNode.java +++ b/core/src/main/java/org/jruby/ast/RestArgNode.java @@ -41,6 +41,7 @@ public RestArgNode(int line, RubySymbol name, int index) { super(line, name, index); } + // TODO: ??? // 1.9 only - lvar assign logic returns an Argument node public RestArgNode(ArgumentNode argNode) { this(argNode.getLine(), argNode.getName(), argNode.getIndex()); diff --git a/core/src/main/java/org/jruby/ast/SClassNode.java b/core/src/main/java/org/jruby/ast/SClassNode.java index 3f95b798c9c..7b44f7b5d92 100644 --- a/core/src/main/java/org/jruby/ast/SClassNode.java +++ b/core/src/main/java/org/jruby/ast/SClassNode.java @@ -52,12 +52,12 @@ public class SClassNode extends Node { private final Node bodyNode; private final int endLine; - public SClassNode(int line, Node recvNode, StaticScope scope, Node bodyNode, int endLine) { - super(line, recvNode.containsVariableAssignment() || bodyNode.containsVariableAssignment()); + public SClassNode(int line, Node receiverNode, StaticScope scope, Node bodyNode, int endLine) { + super(line, receiverNode.containsVariableAssignment() || bodyNode.containsVariableAssignment()); assert scope != null : "scope is not null"; - this.receiverNode = recvNode; + this.receiverNode = receiverNode; this.scope = scope; this.bodyNode = bodyNode; this.endLine = endLine; diff --git a/core/src/main/java/org/jruby/ast/visitor/AbstractNodeVisitor.java b/core/src/main/java/org/jruby/ast/visitor/AbstractNodeVisitor.java index f1454a14360..bfee77af747 100644 --- a/core/src/main/java/org/jruby/ast/visitor/AbstractNodeVisitor.java +++ b/core/src/main/java/org/jruby/ast/visitor/AbstractNodeVisitor.java @@ -393,6 +393,11 @@ public T visitNilNode(NilNode node) { public T visitNthRefNode(NthRefNode node) { return defaultVisit(node); } + + @Override + public T visitOperatorCallNode(OperatorCallNode node) { + return defaultVisit(node); + } @Override public T visitOpElementAsgnNode(OpElementAsgnNode node) { diff --git a/core/src/main/java/org/jruby/ast/visitor/NodeVisitor.java b/core/src/main/java/org/jruby/ast/visitor/NodeVisitor.java index 321f48a72ac..f71cf25dd7d 100644 --- a/core/src/main/java/org/jruby/ast/visitor/NodeVisitor.java +++ b/core/src/main/java/org/jruby/ast/visitor/NodeVisitor.java @@ -109,6 +109,7 @@ public interface NodeVisitor { T visitNextNode(NextNode iVisited); T visitNilNode(NilNode iVisited); T visitNthRefNode(NthRefNode iVisited); + T visitOperatorCallNode(OperatorCallNode iVisited); T visitOpElementAsgnNode(OpElementAsgnNode iVisited); T visitOpAsgnNode(OpAsgnNode iVisited); T visitOpAsgnAndNode(OpAsgnAndNode iVisited); diff --git a/core/src/main/java/org/jruby/ast/visitor/OperatorCallNode.java b/core/src/main/java/org/jruby/ast/visitor/OperatorCallNode.java index a18c0fe2733..a94d2e58e88 100644 --- a/core/src/main/java/org/jruby/ast/visitor/OperatorCallNode.java +++ b/core/src/main/java/org/jruby/ast/visitor/OperatorCallNode.java @@ -8,4 +8,12 @@ public class OperatorCallNode extends CallNode { public OperatorCallNode(int line, Node receiverNode, RubySymbol name, Node argsNode, Node iterNode, boolean isLazy) { super(line, receiverNode, name, argsNode, iterNode, isLazy); } + + /** + * Accept for the visitor pattern. + * @param iVisitor the visitor + **/ + public T accept(NodeVisitor iVisitor) { + return iVisitor.visitOperatorCallNode(this); + } } diff --git a/core/src/main/java/org/jruby/ir/interpreter/InterpreterContext.java b/core/src/main/java/org/jruby/ir/interpreter/InterpreterContext.java index 3e4feccbe3a..4774a8e4a57 100644 --- a/core/src/main/java/org/jruby/ir/interpreter/InterpreterContext.java +++ b/core/src/main/java/org/jruby/ir/interpreter/InterpreterContext.java @@ -259,7 +259,7 @@ public String toStringInstrs() { for (int i = 0; i < length; i++) { if (i > 0) b.append("\n"); - b.append(" ").append(i).append('\t').append(instructions[i]); + b.append(String.format("%6d",i)).append('\t').append(instructions[i]); } /* ENEBO: I this this is too much output espectially for ic and not fic diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index c4c54efbd6a..e86668bda89 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -286,6 +286,7 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz | InvocationTargetException e) { //TODO: ??? + e.printStackTrace(); //TODO: throw if the ctor did this. Copy JPConstructor code here throw context.runtime.newStandardError("Implementation did something wrong. Please file a bug. " + e.getMessage()); } @@ -364,7 +365,7 @@ public RubyArray splitInitialized(IRubyObject[] args, Block blk) if (dm != null && (dm instanceof StaticJCreateMethod)) dm = ((StaticJCreateMethod)dm).getOriginal(); DynamicMethod dm1 = getMetaClass().retrieveMethod("initialize"); // only on ourself - if (dm1 != null && !(dm instanceof InitializeMethod)) + if ((dm1 != null && !(dm instanceof InitializeMethod))) { //TODO: if not defined, then ctors = all valid superctors DefNode def = ((IRMethod)((AbstractIRMethod)dm).getIRScope()).desugar(); @@ -396,6 +397,10 @@ public RubyArray splitInitialized(IRubyObject[] args, Block blk) dm1 = dm; dm = new MixedModeIRMethod(irm, Visibility.PUBLIC, this.getMetaClass()); + //irm.builtInterpreterContext(); +// /System.out.println(irm.getLexicalScopes().get(0).getInterpreterContext()); +// ((IRMethod)((AbstractIRMethod)dma).getIRScope()).builtInterpreterContext(); + this.getMetaClass().addMethod("j_initialize", dm); } else diff --git a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java index 7eae1da1405..379a403bc0b 100644 --- a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java +++ b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java @@ -2,7 +2,8 @@ import org.jruby.*; import org.jruby.ast.*; -import org.jruby.ast.visitor.AbstractNodeVisitor; +import org.jruby.ast.types.INameNode; +import org.jruby.ast.visitor.*; import org.jruby.ir.IRScopeType; import org.jruby.parser.*; import org.jruby.runtime.Signature; @@ -36,7 +37,7 @@ protected Node defaultVisit(Node node) level++; System.out.println("Node " + node.getLine() + "/" + node.getClass().getSimpleName()); - node.childNodes().forEach(this::defaultVisit); + node.childNodes().forEach(x -> x.accept(this)); level--; return node; @@ -116,56 +117,482 @@ public ReturnNode buildRewrite(int line, Node sarg, Node blk) scope.setScopeType(IRScopeType.CLOSURE); scope.setSignature(Signature.from(an)); // def.getArgsNode(). - def.getArgsNode().accept(new ScopeKeeper(scope)); // TODO: capture args? - ret.add(new LambdaNode(line, an, blk, scope, line) - /* - * new FCallNode(line, RubySymbol.newSymbol(runtime, "lambda"), null, new - * IterNode(line, an, blk, scope, 1)) - */); + ret.add(//new LambdaNode(line, an, blk, scope, line) + new FCallNode(line, RubySymbol.newSymbol(runtime, "lambda"), null, new + IterNode(line, an, blk, scope, 1)) + ); found = true; return new ReturnNode(line, ret); } - - public static class ScopeKeeper extends AbstractNodeVisitor + + @Override + public Node visitLocalVarNode(LocalVarNode node) { - private StaticScope scope; - - ScopeKeeper(StaticScope ss) - { - this.scope = ss; - } - - @Override - protected StaticScope defaultVisit(Node node) - { - if (node == null) - return null; + if (!found) + return node; + + return new LocalVarNode(node.getLine(), deepen(node), node.getName()); + + } - node.childNodes().forEach(n -> n.accept(this)); - return scope; - } + private int deepen(IScopedNode node) + { + return ((node.getDepth()+1) << 16) | node.getIndex(); + } + - @Override - public StaticScope visitArgumentNode(ArgumentNode node) - { - scope.addVariableThisScope(node.getName().toString()); - return scope; - } + @Override + public Node visitLocalAsgnNode(LocalAsgnNode node) { + //TODO: is this true for all situations? + if (!found) + return node; - @Override - public StaticScope visitRestArgNode(RestArgNode node) - { - scope.addVariableThisScope(node.getName().toString()); - return scope; - } - - @Override - public StaticScope visitKeywordRestArgNode(KeywordRestArgNode node) - { - scope.addVariableThisScope(node.getName().toString()); - return scope; - } + return new LocalAsgnNode(node.getLine(), node.getName(), deepen(node), v(node.getValueNode())); + } + + private Node v(Node node) + { + if (node == null) + return null; + return node.accept(this); + } + + @Override + public Node visitFlipNode(FlipNode node) { + throw new IllegalStateException("No flip flops in the constructor!"); + } + + /// boring + + + + + @Override + public Node visitArgumentNode(ArgumentNode node) { + return new ArgumentNode(node.getLine(), node.getName(), ((node.getDepth()+0) << 16) | node.getIndex());// TODO: is this 0? + } + + @Override + public Node visitVCallNode(VCallNode node) { + return new VCallNode(node.getLine(), node.getName()); + } + + @Override + public Node visitGlobalAsgnNode(GlobalAsgnNode node) { + return new GlobalAsgnNode(node.getLine(), node.getName(), v(node.getValueNode())); + } + + @Override + public Node visitGlobalVarNode(GlobalVarNode node) { + return new GlobalVarNode(node.getLine(), node.getName()); + } + + + @Override + public Node visitCallNode(CallNode node) { + return new CallNode(node.getLine(), v(node.getReceiverNode()), node.getName(), v(node.getArgsNode()), v(node.getIterNode()), node.isLazy()); + } + + @Override + public Node visitDVarNode(DVarNode node) { + return new DVarNode(node.getLine(), deepen(node), node.getName()); + } + + @Override + public Node visitDAsgnNode(DAsgnNode node) { + return new DAsgnNode(node.getLine(), node.getName(), deepen(node), v(node.getValueNode())); + } + + @Override + public Node visitBeginNode(BeginNode node) { + return new BeginNode(node.getLine(), v(node.getBodyNode())); + } + + @Override + public Node visitBignumNode(BignumNode node) { + return new BignumNode(node.getLine(), node.getValue()); + } + + @Override + public Node visitBlockPassNode(BlockPassNode node) { + return new BlockPassNode(node.getLine(), v(node.getBodyNode())); + } + + @Override + public Node visitBreakNode(BreakNode node) { + return new BreakNode(node.getLine(), v(node.getValueNode())); + } + + @Override + public Node visitConstDeclNode(ConstDeclNode node) { + return new ConstDeclNode(node.getLine(), node.getName(), (INameNode)v(node.getConstNode()), v(node.getValueNode())); + } + + @Override + public Node visitClassVarAsgnNode(ClassVarAsgnNode node) { + return new ClassVarAsgnNode(node.getLine(), node.getName(), v(node.getValueNode())); + } + + @Override + public Node visitClassVarNode(ClassVarNode node) { + return new ClassVarNode(node.getLine(), node.getName()); + } + + @Override + public Node visitCaseNode(CaseNode node) { + return new CaseNode(node.getLine(), v(node.getCaseNode()), node.getCases()); + } + + @Override + public Node visitClassNode(ClassNode node) { + return new ClassNode(node.getLine(), node.getCPath(), node.getScope(), v(node.getBodyNode()), v(node.getSuperNode()), node.getEndLine()); + } + + @Override + public Node visitColon2Node(Colon2Node node) { + if (node instanceof Colon2ConstNode) + return new Colon2ConstNode(node.getLine(), v(node.getLeftNode()), node.getName()); + else if (node instanceof Colon2ImplicitNode) + return new Colon2ImplicitNode(node.getLine(), node.getName()); + throw new IllegalStateException("Ack");// TODO:??? + } + + @Override + public Node visitColon3Node(Colon3Node node) { + return new Colon3Node(node.getLine(), node.getName()); + } + + @Override + public Node visitComplexNode(ComplexNode node) { + return new ComplexNode(node.getLine(), node.getNumber()); + } + + @Override + public Node visitConstNode(ConstNode node) { + return new ConstNode(node.getLine(), node.getName()); + } + + @Override + public Node visitDRegxNode(DRegexpNode node) { + ListNode an = new DRegexpNode(node.getLine(), node.getOptions(), node.getEncoding()); + for (Node child : node.children()) + an.add(v(child)); + return an; + } + + @Override + public Node visitDStrNode(DStrNode node) { + ListNode an = new DStrNode(node.getLine(), node.getEncoding()); + for (Node child : node.children()) + an.add(v(child)); + return an; + } + + @Override + public Node visitDefinedNode(DefinedNode node) { + return new DefinedNode(node.getLine(), v(node.getExpressionNode())); + } + + @Override + public Node visitDefnNode(DefnNode node) { + return new DefnNode(node.getLine(), node.getName(), node.getArgsNode(), node.getScope(), v(node.getBodyNode()), node.getEndLine()); + } + + @Override + public Node visitDefsNode(DefsNode node) { + return new DefsNode(node.getLine(), v(node.getReceiverNode()), node.getName(), node.getArgsNode(), node.getScope(), v(node.getBodyNode()), node.getEndLine()); + } + + @Override + public Node visitDotNode(DotNode node) { + return new DotNode(node.getLine(), v(node.getBeginNode()), v(node.getEndNode()), node.isExclusive(), node.isLiteral()); + } + + @Override + public Node visitEncodingNode(EncodingNode node) { + return new EncodingNode(node.getLine(), node.getEncoding()); + } + + @Override + public Node visitEnsureNode(EnsureNode node) { + return new EnsureNode(node.getLine(), v(node.getBodyNode()), v(node.getEnsureNode())); + } + + @Override + public Node visitEvStrNode(EvStrNode node) { + return new EvStrNode(node.getLine(), v(node.getBody())); + } + + @Override + public Node visitFalseNode(FalseNode node) { + return new FalseNode(node.getLine()); + } + + @Override + public Node visitFixnumNode(FixnumNode node) { + return new FixnumNode(node.getLine(), node.getValue()); + } + + @Override + public Node visitFloatNode(FloatNode node) { + return new FloatNode(node.getLine(), node.getValue()); + } + + @Override + public Node visitForNode(ForNode node) { + return new ForNode(node.getLine(), v(node.getVarNode()), v(node.getBodyNode()), v(node.getIterNode()), node.getScope(), node.getEndLine()); + } + + @Override + public Node visitInstAsgnNode(InstAsgnNode node) { + return new InstAsgnNode(node.getLine(), node.getName(), v(node.getValueNode())); } + + @Override + public Node visitInstVarNode(InstVarNode node) { + return new InstVarNode(node.getLine(), node.getName()); + } + + @Override + public Node visitIfNode(IfNode node) { + return new IfNode(node.getLine(), v(node.getCondition()), v(node.getThenBody()), v(node.getElseBody())); + } + + @Override + public Node visitKeywordArgNode(KeywordArgNode node) { + return new KeywordArgNode(node.getLine(), node.getAssignable()); + } + + @Override + public Node visitKeywordRestArgNode(KeywordRestArgNode node) { + return new KeywordRestArgNode(node.getLine(), node.getName(), node.getIndex()); + } + + @Override + public Node visitLambdaNode(LambdaNode node) { + return new LambdaNode(node.getLine(), node.getArgs(), v(node.getBody()), node.getScope(), node.getEndLine()); + } + + @Override + public Node visitLiteralNode(LiteralNode node) { + return new LiteralNode(node.getLine(), node.getSymbolName()); + } + + @Override + public Node visitMultipleAsgnNode(MultipleAsgnNode node) { + return new MultipleAsgnNode(node.getLine(), node.getPre(), v(node.getRest()), node.getPost()); + } + + @Override + public Node visitMatch2Node(Match2Node node) { + return new Match2Node(node.getLine(), v(node.getReceiverNode()), v(node.getValueNode())); + } + + @Override + public Node visitMatch3Node(Match3Node node) { + return new Match3Node(node.getLine(), v(node.getReceiverNode()), v(node.getValueNode())); + } + + @Override + public Node visitMatchNode(MatchNode node) { + return new MatchNode(node.getLine(), v(node.getRegexpNode())); + } + + @Override + public Node visitModuleNode(ModuleNode node) { + return new ModuleNode(node.getLine(), node.getCPath(), node.getScope(), v(node.getBodyNode()), node.getEndLine()); + } + + @Override + public Node visitNewlineNode(NewlineNode node) { + return new NewlineNode(node.getLine(), v(node.getNextNode())); + } + + @Override + public Node visitNextNode(NextNode node) { + return new NextNode(node.getLine(), v(node.getValueNode())); + } + + @Override + public Node visitNilNode(NilNode node) { + return new NilNode(node.getLine()); + } + + @Override + public Node visitNthRefNode(NthRefNode node) { + return new NthRefNode(node.getLine(), node.getMatchNumber()); + } + + @Override + public Node visitOpElementAsgnNode(OpElementAsgnNode node) { + return new OpElementAsgnNode(node.getLine(), v(node.getReceiverNode()), node.getOperatorSymbolName(), v(node.getArgsNode()), v(node.getValueNode()), v(node.getBlockNode())); + } + + @Override + public Node visitOpAsgnNode(OpAsgnNode node) { + return new OpAsgnNode(node.getLine(), v(node.getReceiverNode()), v(node.getValueNode()), node.getVariableSymbolName(), node.getOperatorSymbolName(), node.isLazy()); + } + + @Override + public Node visitOptArgNode(OptArgNode node) { + return new OptArgNode(node.getLine(), v(node.getValue())); + } + + @Override + public Node visitOrNode(OrNode node) { + return new OrNode(node.getLine(), v(node.getFirstNode()), v(node.getSecondNode())); + } + + @Override + public Node visitPreExeNode(PreExeNode node) { + return new PreExeNode(node.getLine(), node.getScope(), v(node.getBodyNode()), node.getEndLine()); + } + + @Override + public Node visitPostExeNode(PostExeNode node) { + return new PostExeNode(node.getLine(), v(node.getBodyNode()), node.getEndLine()); + } + + @Override + public Node visitRationalNode(RationalNode node) { + return new RationalNode(node.getLine(), node.getNumerator(), node.getDenominator()); + } + + @Override + public Node visitRedoNode(RedoNode node) { + return new RedoNode(node.getLine()); + } + + @Override + public Node visitRegexpNode(RegexpNode node) { + return new RegexpNode(node.getLine(), node.getValue(), node.getOptions()); + } + + @Override + public Node visitRequiredKeywordArgumentValueNode(RequiredKeywordArgumentValueNode node) { + return new RequiredKeywordArgumentValueNode(); + } + + @Override + public Node visitRescueBodyNode(RescueBodyNode node) { + return new RescueBodyNode(node.getLine(), v(node.getExceptionNodes()), v(node.getBodyNode()), node.getOptRescueNode()); + } + + @Override + public Node visitRescueNode(RescueNode node) { + return new RescueNode(node.getLine(), v(node.getBodyNode()), node.getRescueNode(), v(node.getElseNode())); + } + + @Override + public Node visitRetryNode(RetryNode node) { + return new RetryNode(node.getLine()); + } + + @Override + public Node visitReturnNode(ReturnNode node) { + return new ReturnNode(node.getLine(), v(node.getValueNode())); + } + + @Override + public Node visitSClassNode(SClassNode node) { + return new SClassNode(node.getLine(), v(node.getReceiverNode()), node.getScope(), v(node.getBodyNode()), node.getEndLine()); + } + + @Override + public Node visitSelfNode(SelfNode node) { + return new SelfNode(node.getLine()); + } + + @Override + public Node visitSplatNode(SplatNode node) { + return new SplatNode(node.getLine(), v(node.getValue())); + } + + @Override + public Node visitStarNode(StarNode node) { + return new StarNode(node.getLine()); + } + + @Override + public Node visitSValueNode(SValueNode node) { + return new SValueNode(node.getLine(), v(node.getValue())); + } + + @Override + public Node visitSymbolNode(SymbolNode node) { + return new SymbolNode(node.getLine(), node.getName()); + } + + @Override + public Node visitAliasNode(AliasNode node) { + return new AliasNode(node.getLine(), v(node.getNewName()), v(node.getOldName())); + } + + @Override + public Node visitAndNode(AndNode node) { + return new AndNode(node.getLine(), v(node.getFirstNode()), v(node.getSecondNode())); + } + + @Override + public Node visitArgsCatNode(ArgsCatNode node) { + return new ArgsCatNode(node.getLine(), v(node.getFirstNode()), v(node.getSecondNode())); + } + + @Override + public Node visitArgsPushNode(ArgsPushNode node) { + return new ArgsPushNode(node.getLine(), v(node.getFirstNode()), v(node.getSecondNode())); + } + + @Override + public Node visitAttrAssignNode(AttrAssignNode node) { + return new AttrAssignNode(node.getLine(), v(node.getReceiverNode()), node.getName(), v(node.getArgsNode()), v(node.getBlockNode()), node.isLazy()); + } + + @Override + public Node visitBackRefNode(BackRefNode node) { + return new BackRefNode(node.getLine(), node.getType()); + } + + @Override + public Node visitTrueNode(TrueNode node) { + return new TrueNode(node.getLine()); + } + + @Override + public Node visitUndefNode(UndefNode node) { + return new UndefNode(node.getLine(), v(node.getName())); + } + + @Override + public Node visitVAliasNode(VAliasNode node) { + return new VAliasNode(node.getLine(), node.getNewName(), node.getOldName()); + } + + @Override + public Node visitWhenNode(WhenNode node) { + return new WhenNode(node.getLine(), v(node.getExpressionNodes()), v(node.getBodyNode()), v(node.getNextCase())); + } + + @Override + public Node visitYieldNode(YieldNode node) { + return new YieldNode(node.getLine(), v(node.getArgsNode())); + } + + @Override + public Node visitZArrayNode(ZArrayNode node) { + ListNode an = new ZArrayNode(node.getLine()); + for (Node child : node.children()) + an.add(v(child)); + return an; + } + + + @Override + public Node visitClassVarDeclNode(ClassVarDeclNode node) { + return new ClassVarDeclNode(node.getLine(), node.getName(), v(node.getValueNode())); + } + + } \ No newline at end of file From eb1ecdbc5ab237b726a3f131a3dc75a24aa58b19 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Wed, 18 Nov 2020 01:29:55 -0500 Subject: [PATCH 14/49] Start fixing some easy test failures --- core/src/main/java/org/jruby/RubyClass.java | 276 ++++++++++-------- .../java/codegen/RealClassGenerator.java | 8 +- .../jruby/java/proxies/ConcreteJavaProxy.java | 12 +- .../org/jruby/java/proxies/FlatExtractor.java | 126 ++++++++ .../main/java/org/jruby/javasupport/Java.java | 10 +- .../proxy/JavaProxyConstructor.java | 3 +- spec/java_integration/types/coercion_spec.rb | 2 +- 7 files changed, 306 insertions(+), 131 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index 20c86850768..cb0b5590d49 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -1325,7 +1325,13 @@ public synchronized void reify(String classDumpDir, boolean useChildLoader) { Reificator reifier; if (concreteExt) - reifier = new ConcreteJavaReifier(reifiedParent, javaName, javaPath); + { + if (parentReified != reifiedParent) + { + System.out.println("parents differ! "); + } + reifier = new ConcreteJavaReifier(parentReified, javaName, javaPath); + } else reifier = new MethodReificator(reifiedParent, javaName, javaPath, null, javaPath); @@ -1687,129 +1693,149 @@ private void defineClassMethods(Set instanceMethods) { } //TODO: only generate that are overrideable (javaproxyclass) - private void defineInstanceMethods(Set instanceMethods) { - SkinnyMethodAdapter m; + protected void defineInstanceMethods(Set instanceMethods) { for (Map.Entry methodEntry : getMethods().entrySet()) { final String id = methodEntry.getKey(); final Arity arity = methodEntry.getValue().getArity(); - String javaMethodName = JavaNameMangler.mangleMethodName(id); + PositionAware position = getPositionOrDefault(methodEntry.getValue()); if (position.getLine() > 1) cw.visitSource(position.getFile(), null); - Map> methodAnnos = getMethodAnnotations().get(id); - List>> parameterAnnos = getParameterAnnotations().get(id); Class[] methodSignature = getMethodSignatures().get(id); // for concrete extension, see if the method is one we are overriding, // even if we didn't specify it manually if (methodSignature == null) - methodSignature = searchInheritedSignatures(id, arity); - - final String signature; - if (methodSignature == null) { // non-signature signature with just IRubyObject - if (!jcc.allMethods) continue; - switch (arity.getValue()) { - case 0: - signature = sig(IRubyObject.class); // return IRubyObject foo() - m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); - m.line(position.getLine()); - generateMethodAnnotations(methodAnnos, m, parameterAnnos); - generateObjectBarrier(m); - - loadRubyObject(m); // self/rubyObject - m.ldc(id); - rubycall(m, sig(IRubyObject.class, String.class)); - break; - case 1: - signature = sig(IRubyObject.class, IRubyObject.class); // return IRubyObject foo(IRubyObject arg1) - m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); - m.line(position.getLine()); - generateMethodAnnotations(methodAnnos, m, parameterAnnos); - generateObjectBarrier(m); - - loadRubyObject(m); // self/rubyObject - m.ldc(id); - m.aload(1); // IRubyObject arg1 - rubycall(m, sig(IRubyObject.class, String.class, IRubyObject.class)); - break; - // currently we only have : - // callMethod(context, name) - // callMethod(context, name, arg1) - // so for other arities use generic: - // callMethod(context, name, args...) - default: - if ( arity.isFixed() ) { - final int paramCount = arity.getValue(); - Class[] params = new Class[paramCount]; Arrays.fill(params, IRubyObject.class); - signature = sig(IRubyObject.class, params); - m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); - m.line(position.getLine()); - generateMethodAnnotations(methodAnnos, m, parameterAnnos); - generateObjectBarrier(m); - - loadRubyObject(m); // self/rubyObject - m.ldc(id); - - // generate an IRubyObject[] for the method arguments : - m.pushInt(paramCount); - m.anewarray(p(IRubyObject.class)); // new IRubyObject[size] - for ( int i = 1; i <= paramCount; i++ ) { - m.dup(); - m.pushInt(i - 1); // array index e.g. iconst_0 - m.aload(i); // IRubyObject arg1, arg2 e.g. aload_1 - m.aastore(); // arr[ i - 1 ] = arg_i - } - } - else { // (generic) variable arity e.g. method(*args) - // NOTE: maybe improve to match fixed part for < -1 e.g. (IRubObject, IRubyObject, IRubyObject...) - signature = sig(IRubyObject.class, IRubyObject[].class); - m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS, javaMethodName, signature, null, null); - m.line(position.getLine()); - generateMethodAnnotations(methodAnnos, m, parameterAnnos); - generateObjectBarrier(m); - - loadRubyObject(m); // self/rubyObject - m.ldc(id); - m.aload(1); // IRubyObject[] arg1 - } - rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class)); - } - m.areturn(); + { + for (Class[] sig : searchInheritedSignatures(id, arity)) + { + String signature = defineInstanceMethod(id, arity, position, sig); + if (signature != null) + instanceMethods.add(signature); + } } - else { // generate a real method signature for the method, with to/from coercions - - // indices for temp values - Class[] params = new Class[methodSignature.length - 1]; - ArraySupport.copy(methodSignature, 1, params, 0, params.length); - final int baseIndex = RealClassGenerator.calcBaseIndex(params, 1); - final int rubyIndex = baseIndex; - - signature = sig(methodSignature[0], params); - int mod = ACC_PUBLIC; - if ( isVarArgsSignature(id, methodSignature) ) mod |= ACC_VARARGS; - m = new SkinnyMethodAdapter(cw, mod, javaMethodName, signature, null, null); - m.line(position.getLine()); - generateMethodAnnotations(methodAnnos, m, parameterAnnos); - generateObjectBarrier(m); - - m.getstatic(javaPath, "ruby", ci(Ruby.class)); // runtime - m.astore(rubyIndex); - - loadRubyObject(m); // self/rubyObject - m.ldc(id); // method name - - RealClassGenerator.coerceArgumentsToRuby(m, params, rubyIndex); - rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class)); - RealClassGenerator.coerceResultAndReturn(m, methodSignature[0]); + else + { + String signature = defineInstanceMethod(id, arity, position, methodSignature); + if (signature != null) + instanceMethods.add(signature); } + } + } + + protected String defineInstanceMethod(final String id, final Arity arity, + PositionAware position, Class[] methodSignature) + { + String javaMethodName = JavaNameMangler.mangleMethodName(id); + + Map> methodAnnos = getMethodAnnotations().get(id); + List>> parameterAnnos = getParameterAnnotations().get(id); + + final String signature; + SkinnyMethodAdapter m; + if (methodSignature == null) { // non-signature signature with just IRubyObject + if (!jcc.allMethods) + return null; + switch (arity.getValue()) { + case 0: + signature = sig(IRubyObject.class); // return IRubyObject foo() + m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); + m.line(position.getLine()); + generateMethodAnnotations(methodAnnos, m, parameterAnnos); + generateObjectBarrier(m); + + loadRubyObject(m); // self/rubyObject + m.ldc(id); + rubycall(m, sig(IRubyObject.class, String.class)); + break; + case 1: + signature = sig(IRubyObject.class, IRubyObject.class); // return IRubyObject foo(IRubyObject arg1) + m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); + m.line(position.getLine()); + generateMethodAnnotations(methodAnnos, m, parameterAnnos); + generateObjectBarrier(m); + + loadRubyObject(m); // self/rubyObject + m.ldc(id); + m.aload(1); // IRubyObject arg1 + rubycall(m, sig(IRubyObject.class, String.class, IRubyObject.class)); + break; + // currently we only have : + // callMethod(context, name) + // callMethod(context, name, arg1) + // so for other arities use generic: + // callMethod(context, name, args...) + default: + if ( arity.isFixed() ) { + final int paramCount = arity.getValue(); + Class[] params = new Class[paramCount]; Arrays.fill(params, IRubyObject.class); + signature = sig(IRubyObject.class, params); + m = new SkinnyMethodAdapter(cw, ACC_PUBLIC, javaMethodName, signature, null, null); + m.line(position.getLine()); + generateMethodAnnotations(methodAnnos, m, parameterAnnos); + generateObjectBarrier(m); + + loadRubyObject(m); // self/rubyObject + m.ldc(id); + + // generate an IRubyObject[] for the method arguments : + m.pushInt(paramCount); + m.anewarray(p(IRubyObject.class)); // new IRubyObject[size] + for ( int i = 1; i <= paramCount; i++ ) { + m.dup(); + m.pushInt(i - 1); // array index e.g. iconst_0 + m.aload(i); // IRubyObject arg1, arg2 e.g. aload_1 + m.aastore(); // arr[ i - 1 ] = arg_i + } + } + else { // (generic) variable arity e.g. method(*args) + // NOTE: maybe improve to match fixed part for < -1 e.g. (IRubObject, IRubyObject, IRubyObject...) + signature = sig(IRubyObject.class, IRubyObject[].class); + m = new SkinnyMethodAdapter(cw, ACC_PUBLIC | ACC_VARARGS, javaMethodName, signature, null, null); + m.line(position.getLine()); + generateMethodAnnotations(methodAnnos, m, parameterAnnos); + generateObjectBarrier(m); + + loadRubyObject(m); // self/rubyObject + m.ldc(id); + m.aload(1); // IRubyObject[] arg1 + } + rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class)); + } + m.areturn(); + } + else { // generate a real method signature for the method, with to/from coercions + + // indices for temp values + Class[] params = new Class[methodSignature.length - 1]; + ArraySupport.copy(methodSignature, 1, params, 0, params.length); + final int baseIndex = RealClassGenerator.calcBaseIndex(params, 1); + final int rubyIndex = baseIndex; + + signature = sig(methodSignature[0], params); + int mod = ACC_PUBLIC; + if ( isVarArgsSignature(id, methodSignature) ) mod |= ACC_VARARGS; + m = new SkinnyMethodAdapter(cw, mod, javaMethodName, signature, null, null); + m.line(position.getLine()); + generateMethodAnnotations(methodAnnos, m, parameterAnnos); + generateObjectBarrier(m); + + m.getstatic(javaPath, "ruby", ci(Ruby.class)); // runtime + m.astore(rubyIndex); + + loadRubyObject(m); // self/rubyObject + m.ldc(id); // method name + + RealClassGenerator.coerceArgumentsToRuby(m, params, rubyIndex); + rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class)); + RealClassGenerator.coerceResultAndReturn(m, methodSignature[0]); + } + m.end(); - if (DEBUG_REIFY) LOG.debug("defining {}#{} as {}#{}", getName(), id, javaName, javaMethodName + signature); - - instanceMethods.add(javaMethodName + signature); + if (DEBUG_REIFY) LOG.debug("defining {}#{} as {}#{}", getName(), id, javaName, javaMethodName + signature); - m.end(); - } + return javaMethodName + signature; } /** @@ -1852,9 +1878,9 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) m.pop(); } - protected Class[] searchInheritedSignatures(String id, Arity arity) + protected Collection searchInheritedSignatures(String id, Arity arity) { - return null; + return Collections.EMPTY_LIST; } protected void generateObjectBarrier(SkinnyMethodAdapter m) @@ -1954,29 +1980,43 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) m.putstatic(javaPath, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class)); // Note: no end, that's in the parent call } + @Override - protected Class[] searchInheritedSignatures(String id, Arity arity) + protected Collection searchInheritedSignatures(String id, Arity arity) { - // Note: not stable. May flicker between different arities. TODO: sort? - for (Method method : reifiedParent.getDeclaredMethods()) + return searchClassMethods(reifiedParent, id, new HashMap<>()); + } + + private Collection searchClassMethods(Class clz, String id, HashMap options) + { + if (clz.getSuperclass() != null) + searchClassMethods(clz.getSuperclass(), id, options); + for (Class intf : clz.getInterfaces()) + searchClassMethods(intf, id, options); + for (Method method : clz.getDeclaredMethods()) { //TODO: java <-> ruby conversion? if (!method.getName().equals(id)) continue; final int mod = method.getModifiers(); if ( !Modifier.isPublic(mod) && !Modifier.isProtected(mod) ) continue; + //TODO: no arity checks? + /* // ensure arity is reasonable (ignores java varargs) if (arity.isFixed()) { if (arity.required() != method.getParameterCount()) continue; } else if (arity.required() > method.getParameterCount()) continue; + */ // found! built a signature to return - return join(new Class[]{method.getReturnType()}, method.getParameterTypes()); + Class[] types = join(new Class[]{method.getReturnType()}, method.getParameterTypes()); + options.put(sig(types), types); } - return null; + // Note: not stable. May flicker between different arities. TODO: sort? + return options.values(); } @Override @@ -2014,9 +2054,9 @@ protected void reifyConstructors() } else { - //TODO: no ctors = error? - //TODO ???? - throw new RuntimeException("Class doesn't have any invokeable ctors"); + //TODO: copy validateArgs + //TODO: no ctors = error? + throw runtime.newTypeError("class " +reifiedParent.getName()+ " doesn't have a public or protected constructor"); } if (zeroArg.isPresent()) diff --git a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java index 760f4acf70b..c9d70361c08 100644 --- a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java +++ b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java @@ -952,7 +952,11 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware i m.dup(); m.getstatic(cjr.javaPath, cjr.RUBY_CTOR_CACHE_FIELD, ci(JCtorCache.class));// ... arglist, arglist, ctors, m.aload(rubyIndex); // ruby - m.invokestatic(p(JCreateMethod.class), "forTypes", sig(int.class, IRubyObject.class, JCtorCache.class, Ruby.class)); + if (flag == CtorFlags.Normal) + m.iconst_1(); + else + m.iconst_0(); + m.invokestatic(p(JCreateMethod.class), "forTypes", sig(int.class, IRubyObject.class, JCtorCache.class, Ruby.class, boolean.class)); // ..., arglist, index Label defaultLabel = new Label(); Label[] cases = new Label[constructors.length+1]; //note: offset by one from index @@ -973,7 +977,7 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware i m.athrow(); // -1: super(*args), from `super` (no args) in ruby - if (flag == CtorFlags.Normal) + if (flag == CtorFlags.Normal)// || ctorTypes.length == 0) { m.label(cases[0]); m.aload(0); diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index e86668bda89..9115aa82ad6 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -285,10 +285,8 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - //TODO: ??? e.printStackTrace(); - //TODO: throw if the ctor did this. Copy JPConstructor code here - throw context.runtime.newStandardError("Implementation did something wrong. Please file a bug. " + e.getMessage()); + JavaProxyConstructor.mapInstantiationException(context.runtime, e); } return object; } @@ -306,7 +304,7 @@ public static RubyArray freshMethodArray(DynamicMethod initialize, Ruby runtime, { return runtime.newArray( - runtime.getNil(), + runtime.newArray(args), //TODO:? runtime.newProc(Block.Type.LAMBDA, new Block(new JavaInternalBlockBody(runtime, Signature.from(initialize.getArity())) { @@ -320,11 +318,11 @@ public IRubyObject yield(ThreadContext _context, IRubyObject[] _args) ); } - public static RubyArray freshNopArray(Ruby runtime) + public static RubyArray freshNopArray(Ruby runtime, IRubyObject[] args) { return runtime.newArray( - runtime.getNil(), + runtime.newArray(args), runtime.newProc(Block.Type.LAMBDA, new Block(new JavaInternalBlockBody(runtime, Signature.OPTIONAL) { @Override @@ -407,7 +405,7 @@ public RubyArray splitInitialized(IRubyObject[] args, Block blk) { //TODO: pass ruby into this if (dm instanceof InitializeMethod) - return SimpleJavaInitializes.freshNopArray(this.getRuntime()); + return SimpleJavaInitializes.freshNopArray(this.getRuntime(), args); else return SimpleJavaInitializes.freshMethodArray(dm, this.getRuntime(), this, getMetaClass(), "initialize", args); } diff --git a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java index 379a403bc0b..e51aa8b9ec2 100644 --- a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java +++ b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java @@ -7,6 +7,7 @@ import org.jruby.ir.IRScopeType; import org.jruby.parser.*; import org.jruby.runtime.Signature; +import org.jruby.util.KeyValuePair; class FlatExtractor extends AbstractNodeVisitor { @@ -594,5 +595,130 @@ public Node visitClassVarDeclNode(ClassVarDeclNode node) { return new ClassVarDeclNode(node.getLine(), node.getName(), v(node.getValueNode())); } + + + ///// manual + + @Override + public Node visitRestArgNode(RestArgNode node) + { + return new RestArgNode(node.getLine(), node.getName(), node.getIndex()); + } + + @Override + public Node visitArgsNode(ArgsNode node) + { + return new ArgsNode(node.getLine(), + (ListNode)v(node.getPre()), + (ListNode)v(node.getOptArgs()), + (RestArgNode)v(node.getRestArgNode()), + (ListNode)v(node.getPost()), + (ListNode)v(node.getKeywords()), + (KeywordRestArgNode)v(node.getKeyRest()), + (BlockArgNode)v(node.getBlock())); + } + + @Override + public Node visitArrayNode(ArrayNode node) + { + ListNode an = new ArrayNode(node.getLine()); + for (Node child : node.children()) + an.add(v(child)); + return an; + } + @Override + public Node visitListNode(ListNode node) + { + ListNode an = new ListNode(node.getLine()); + for (Node child : node.children()) + an.add(v(child)); + return an; + } + + @Override + public Node visitBlockArgNode(BlockArgNode node) + { + return new BlockArgNode(node.getLine(), node.getCount(), node.getName()); + } + + @Override + public Node visitDSymbolNode(DSymbolNode node) + { + ListNode an = new DSymbolNode(node.getLine()); + for (Node child : node.children()) + an.add(v(child)); + return an; + } + + @Override + public Node visitDXStrNode(DXStrNode node) + { + ListNode an = new DXStrNode(node.getLine(), node.getEncoding()); + for (Node child : node.children()) + an.add(v(child)); + return an; + } + + @Override + public Node visitFCallNode(FCallNode node) + { + return new FCallNode(node.getLine(), node.getName(), v(node.getArgsNode()), v(node.getIterNode())); + } + + @Override + public Node visitHashNode(HashNode node) + { + HashNode hn = new HashNode(node.getLine()); + for (KeyValuePair kvp : node.getPairs()) + { + hn.add(new KeyValuePair(v(kvp.getKey()), v(kvp.getValue()))); + } + return hn; + } + + @Override + public Node visitIterNode(IterNode node) + { + return new IterNode(node.getLine(), (ArgsNode)v(node.getArgsNode()), v(node.getBodyNode()), node.getScope(), node.getEndLine()); + } + + @Override + public Node visitOpAsgnAndNode(OpAsgnAndNode node) + { + return new OpAsgnAndNode(node.getLine(), v(node.getFirstNode()), v(node.getSecondNode())); + }@Override + public Node visitOpAsgnConstDeclNode(OpAsgnConstDeclNode node) + { + return new OpAsgnConstDeclNode(node.getLine(), v(node.getFirstNode()), node.getSymbolOperator(), v(node.getSecondNode())); + }@Override + public Node visitOpAsgnOrNode(OpAsgnOrNode node) + { + return new OpAsgnOrNode(node.getLine(), v(node.getFirstNode()), v(node.getSecondNode())); + }@Override + public Node visitOperatorCallNode(OperatorCallNode node) + { + return new OperatorCallNode(node.getLine(), v(node.getReceiverNode()), node.getName(), v(node.getArgsNode()), v(node.getIterNode()), node.isLazy()); + }@Override + public Node visitRootNode(RootNode node) + { + return new RootNode(node.getLine(), node.getScope(), v(node.getBodyNode()), node.getFile(), node.coverageMode()); + }@Override + public Node visitStrNode(StrNode node) + { + return new StrNode(node.getLine(), node.getValue(), node.getCodeRange()); + }@Override + public Node visitUntilNode(UntilNode node) + { + return new UntilNode(node.getLine(), v(node.getConditionNode()), v(node.getBodyNode()), node.evaluateAtStart()); + }@Override + public Node visitWhileNode(WhileNode node) + { + return new WhileNode(node.getLine(), v(node.getConditionNode()), v(node.getBodyNode()), node.evaluateAtStart()); + + }@Override + public Node visitXStrNode(XStrNode node) + { + return new XStrNode(node.getLine(), node.getValue(), node.getCodeRange()); + } } \ No newline at end of file diff --git a/core/src/main/java/org/jruby/javasupport/Java.java b/core/src/main/java/org/jruby/javasupport/Java.java index 115ee5b3311..f0eb597da4a 100644 --- a/core/src/main/java/org/jruby/javasupport/Java.java +++ b/core/src/main/java/org/jruby/javasupport/Java.java @@ -672,7 +672,7 @@ public final IRubyObject call(ThreadContext context, IRubyObject self, RubyModul return newObject; } - public static int forTypes(IRubyObject argarray, JCtorCache cache, Ruby runtime) + public static int forTypes(IRubyObject argarray, JCtorCache cache, Ruby runtime, boolean allowNil) { if (argarray.isNil()) // super (no args) { @@ -685,7 +685,13 @@ public static int forTypes(IRubyObject argarray, JCtorCache cache, Ruby runtime) JavaConstructor ctor = matchConstructorIndex(runtime.getCurrentContext(), cache.constructors, cache, args.length, args); int index = cache.indexOf(ctor); - if (index < 0) throw runtime.newArgumentError("index error finding superconstructor"); + if (index < 0) + { + if (allowNil) + return -1; // use class error + // use our error otherwise + throw runtime.newArgumentError("index error finding superconstructor"); + } System.out.println("Will be retuing icx + " + index); return index; } diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java index 3f956fcb4e7..4e2316a9db2 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyConstructor.java @@ -227,7 +227,7 @@ public final JavaObject newInstance(final IRubyObject self, IRubyObject arg0) th catch (Throwable ex) { throw mapInstantiationException(runtime, ex); } } - private static RaiseException mapInstantiationException(final Ruby runtime, final Throwable e) { + public static RaiseException mapInstantiationException(final Ruby runtime, final Throwable e) { Throwable cause = e; e.printStackTrace(); while ( cause.getCause() != null ) cause = cause.getCause(); @@ -236,6 +236,7 @@ private static RaiseException mapInstantiationException(final Ruby runtime, fina msg = msg == null ? ( MSG + e.getClass().getName() ) : ( MSG + msg ); RaiseException ex = runtime.newArgumentError(msg); ex.initCause(e); + ex.addSuppressed(e); throw ex; } diff --git a/spec/java_integration/types/coercion_spec.rb b/spec/java_integration/types/coercion_spec.rb index bef0d4050ed..c15c0df903d 100644 --- a/spec/java_integration/types/coercion_spec.rb +++ b/spec/java_integration/types/coercion_spec.rb @@ -744,7 +744,7 @@ class UserKlass < Object; end it "returns reified class for reified used classes" do rubycls = Class.new; require 'jruby/core_ext' rubycls.become_java! - expect(rubycls.to_java(cls)).to be JRuby.reference(rubycls).getReifiedClass + expect(rubycls.to_java(cls)).to be JRuby.reference(rubycls).getReifiedRubyClass end it "converts Java proxy classes to their JavaClass/java.lang.Class equivalent" do From cfcc2fdf029f0d468ee405febfc41dd1b757f408 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Wed, 18 Nov 2020 02:21:53 -0500 Subject: [PATCH 15/49] Several test fixes --- core/src/main/java/org/jruby/RubyClass.java | 23 ++++++++++++++----- .../org/jruby/java/proxies/FlatExtractor.java | 8 +++++-- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index cb0b5590d49..b1becdc6b5f 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -1985,15 +1985,25 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) @Override protected Collection searchInheritedSignatures(String id, Arity arity) { - return searchClassMethods(reifiedParent, id, new HashMap<>()); + HashMap types = new HashMap<>(); + Collection best = searchClassMethods(reifiedParent, arity, id, types); + for (Class intf : Java.getInterfacesFromRubyClass(RubyClass.this)) + searchClassMethods(intf, arity, id, types); + if (best.size() == 0) + { + best = searchClassMethods(reifiedParent, null, id, types); + for (Class intf : Java.getInterfacesFromRubyClass(RubyClass.this)) + searchClassMethods(intf, null, id, types); + } + return best; } - private Collection searchClassMethods(Class clz, String id, HashMap options) + private Collection searchClassMethods(Class clz, Arity arity, String id, HashMap options) { if (clz.getSuperclass() != null) - searchClassMethods(clz.getSuperclass(), id, options); + searchClassMethods(clz.getSuperclass(),arity, id, options); for (Class intf : clz.getInterfaces()) - searchClassMethods(intf, id, options); + searchClassMethods(intf, arity, id, options); for (Method method : clz.getDeclaredMethods()) { //TODO: java <-> ruby conversion? @@ -2002,14 +2012,15 @@ private Collection searchClassMethods(Class clz, String id, HashMap method.getParameterCount()) continue; - */ + } // found! built a signature to return Class[] types = join(new Class[]{method.getReturnType()}, method.getParameterTypes()); diff --git a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java index e51aa8b9ec2..b58f566f91e 100644 --- a/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java +++ b/core/src/main/java/org/jruby/java/proxies/FlatExtractor.java @@ -84,7 +84,7 @@ public Node visitBlockNode(BlockNode node) public Node visitSuperNode(SuperNode node) { foundsuper = true; - if (level != 1) + if (level > 1) { error = true; return null; @@ -93,6 +93,8 @@ public Node visitSuperNode(SuperNode node) Node sarg = node.getArgsNode(); if (sarg == null) sarg = new ArrayNode(node.getLine()); + if (level == 0) + return buildRewrite(node.getLine(), sarg, new NilImplicitNode()); return buildRewrite(node.getLine(), sarg, bn = new BlockNode(node.getLine())); } @@ -100,13 +102,15 @@ public Node visitSuperNode(SuperNode node) public Node visitZSuperNode(ZSuperNode node) { foundsuper = true; - if (level != 1) + if (level > 1) { error = true; return null; } superline = node.getLine(); Node sarg = new NilNode(node.getLine()); + if (level == 0) + return buildRewrite(node.getLine(), sarg, new NilImplicitNode()); return buildRewrite(node.getLine(), sarg, bn = new BlockNode(node.getLine())); } From 3b181d853cfb4e87db5a607bcc1097de8b527b04 Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Wed, 25 Nov 2020 13:43:27 -0600 Subject: [PATCH 16/49] Update for import/style consistency --- core/src/main/java/org/jruby/RubyClass.java | 281 +++++++++--------- .../jruby/java/proxies/ConcreteJavaProxy.java | 6 +- 2 files changed, 149 insertions(+), 138 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index cb0b5590d49..5db1637bb69 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -31,38 +31,83 @@ package org.jruby; -import static org.jruby.runtime.Visibility.*; -import static org.jruby.util.CodegenUtils.*; -import static org.jruby.util.RubyStringBuilder.*; -import static org.objectweb.asm.Opcodes.*; import java.io.IOException; -import java.lang.reflect.*; -import java.util.*; - -import org.jruby.anno.*; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +import org.jruby.anno.JRubyClass; +import org.jruby.anno.JRubyMethod; import org.jruby.compiler.impl.SkinnyMethodAdapter; import org.jruby.exceptions.RaiseException; import org.jruby.internal.runtime.methods.DynamicMethod; -import org.jruby.java.codegen.*; +import org.jruby.java.codegen.RealClassGenerator; import org.jruby.java.codegen.RealClassGenerator.CtorFlags; +import org.jruby.java.codegen.Reified; import org.jruby.java.proxies.ConcreteJavaProxy; -import org.jruby.javasupport.*; -import org.jruby.javasupport.Java.JCtorCache; -import org.jruby.javasupport.proxy.*; +import org.jruby.javasupport.Java; +import org.jruby.javasupport.JavaClass; +import org.jruby.javasupport.JavaConstructor; +import org.jruby.javasupport.proxy.JavaProxyClass; +import org.jruby.javasupport.proxy.ReifiedJavaProxy; import org.jruby.javasupport.util.JavaClassConfiguration; import org.jruby.lexer.yacc.SimpleSourcePosition; import org.jruby.parser.StaticScope; -import org.jruby.runtime.*; +import org.jruby.runtime.Arity; +import org.jruby.runtime.Block; +import org.jruby.runtime.CallSite; +import org.jruby.runtime.CallType; +import org.jruby.runtime.ClassIndex; +import org.jruby.runtime.Helpers; +import org.jruby.runtime.JavaSites; +import org.jruby.runtime.MethodIndex; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ObjectMarshal; +import org.jruby.runtime.PositionAware; +import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; -import org.jruby.runtime.callsite.*; -import org.jruby.runtime.ivars.*; -import org.jruby.runtime.marshal.*; +import org.jruby.runtime.callsite.CacheEntry; +import org.jruby.runtime.callsite.CachingCallSite; +import org.jruby.runtime.callsite.RespondToCallSite; +import org.jruby.runtime.ivars.VariableAccessor; +import org.jruby.runtime.ivars.VariableAccessorField; +import org.jruby.runtime.ivars.VariableTableManager; +import org.jruby.runtime.marshal.MarshalStream; +import org.jruby.runtime.marshal.UnmarshalStream; import org.jruby.runtime.opto.Invalidator; -import org.jruby.util.*; +import org.jruby.util.ArraySupport; +import org.jruby.util.ClassDefiningClassLoader; +import org.jruby.util.CodegenUtils; +import org.jruby.util.JavaNameMangler; +import org.jruby.util.OneShotClassLoader; +import org.jruby.util.StringSupport; import org.jruby.util.collections.ConcurrentWeakHashMap; -import org.jruby.util.log.*; -import org.objectweb.asm.*; +import org.jruby.util.log.Logger; +import org.jruby.util.log.LoggerFactory; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; + +import static org.jruby.runtime.Visibility.PRIVATE; +import static org.jruby.runtime.Visibility.PUBLIC; +import static org.jruby.util.CodegenUtils.*; +import static org.jruby.util.RubyStringBuilder.str; +import static org.jruby.util.RubyStringBuilder.types; +import static org.objectweb.asm.Opcodes.*; /** @@ -1240,13 +1285,9 @@ public boolean isReifiable(boolean[] java) { reifiedSuper == RubyBasicObject.class || Reified.class.isAssignableFrom(reifiedSuper); // TODO: check for nested java classes - if (result) - return true; - else - { - java[0] = true; - return true; - } + if (!result) java[0] = true; + + return true; } else { // non-native, non-reified super; recurse return realSuper.isReifiable(java); @@ -1419,15 +1460,13 @@ interface Reificator { private final static PositionAware defaultSimplePosition = new SimpleSourcePosition("", 0); - public PositionAware getPositionOrDefault(DynamicMethod method) - { - if (method instanceof PositionAware) - { + public PositionAware getPositionOrDefault(DynamicMethod method) { + if (method instanceof PositionAware) { PositionAware pos = (PositionAware) method; - return new SimpleSourcePosition(pos.getFile(), pos.getLine()+1); // convert from 0-based to 1-based that the JVM requires - } - else - return defaultSimplePosition; + return new SimpleSourcePosition(pos.getFile(), pos.getLine() + 1); // convert from 0-based to 1-based that the JVM requires + } else { + return defaultSimplePosition; + } } @@ -1846,8 +1885,7 @@ protected String defineInstanceMethod(final String id, final Arity arity, * and {@link JavaProxyClass#getStaticInitLookup(int)} */ @Override - public void reifyClinit(SkinnyMethodAdapter m) - { + public void reifyClinit(SkinnyMethodAdapter m) { // top stack layout: ..., i0, o[], i1, o[] m.pushInt(1); // rubyclass index @@ -1867,7 +1905,7 @@ public void reifyClinit(SkinnyMethodAdapter m) protected Object[] getExtraClinitInfo() { - return new Object[]{runtime, RubyClass.this}; + return new Object[] {runtime, RubyClass.this}; } /** @@ -1883,16 +1921,13 @@ protected Collection searchInheritedSignatures(String id, Arity arity) return Collections.EMPTY_LIST; } - protected void generateObjectBarrier(SkinnyMethodAdapter m) - { + protected void generateObjectBarrier(SkinnyMethodAdapter m) { // For non-concrete things, we ignore, as this is a RubyObject } - } // class MethodReificator //public or private? - public class ConcreteJavaReifier extends MethodReificator - { + public class ConcreteJavaReifier extends MethodReificator { // names follow pattern of `this$0` from javac nested classes to hopefully be ignored by // sane reflection tools. Also similarly marked as synthetic public static final String RUBY_OBJECT_FIELD = "this$rubyObject"; @@ -1903,16 +1938,14 @@ public class ConcreteJavaReifier extends MethodReificator boolean simpleAlloc = false; JavaConstructor[] savedSuperCtors = null; - ConcreteJavaReifier(Class reifiedParent, String javaName, String javaPath) - { + ConcreteJavaReifier(Class reifiedParent, String javaName, String javaPath) { // In theory, we should operate on IRubyObject, but everything // that we need is a ConcreteJavaProxy, and it (via RubyBasicObject) has a nicer interface to boot super(reifiedParent, javaName, javaPath, ci(ConcreteJavaProxy.class), p(ConcreteJavaProxy.class)); } @Override - public void customReify() - { + public void customReify() { super.customReify(); if (simpleAlloc && rubyctor) @@ -1922,18 +1955,16 @@ public void customReify() } @Override - protected void loadRubyObject(SkinnyMethodAdapter m) - { + protected void loadRubyObject(SkinnyMethodAdapter m) { m.aload(0); // self m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); // rubyObject } @Override - public byte[] reify() - { + public byte[] reify() { cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_PRIVATE, RUBY_OBJECT_FIELD, rubyName, null, null); cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class), null, null); - cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_CTOR_CACHE_FIELD, ci(JCtorCache.class), null, null); + cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_CTOR_CACHE_FIELD, ci(Java.JCtorCache.class), null, null); return super.reify(); } @@ -1957,14 +1988,14 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) // extract cached ctors for lookup ordering // note: consume top of stack, lookuparray - m.newobj(p(JCtorCache.class)); + m.newobj(p(Java.JCtorCache.class)); m.dup_x1(); // jccache, lookuparray, jccache m.swap();// jccache, jccache, lookuparray m.pushInt(2); // ctor fields = index 2 m.aaload(); // extract ctors, -> jccache, jccache, ctor[] m.checkcast(p(JavaConstructor[].class)); - m.invokespecial(p(JCtorCache.class), "", sig(void.class, JavaConstructor[].class)); - m.putstatic(javaPath, RUBY_CTOR_CACHE_FIELD, ci(JCtorCache.class)); + m.invokespecial(p(Java.JCtorCache.class), "", sig(void.class, JavaConstructor[].class)); + m.putstatic(javaPath, RUBY_CTOR_CACHE_FIELD, ci(Java.JCtorCache.class)); // now create proxy class m.getstatic(javaPath, "ruby", ci(Ruby.class)); @@ -1983,23 +2014,22 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) @Override - protected Collection searchInheritedSignatures(String id, Arity arity) - { + protected Collection searchInheritedSignatures(String id, Arity arity) { return searchClassMethods(reifiedParent, id, new HashMap<>()); } - private Collection searchClassMethods(Class clz, String id, HashMap options) - { - if (clz.getSuperclass() != null) - searchClassMethods(clz.getSuperclass(), id, options); - for (Class intf : clz.getInterfaces()) - searchClassMethods(intf, id, options); - for (Method method : clz.getDeclaredMethods()) - { + private Collection searchClassMethods(Class clz, String id, HashMap options) { + if (clz.getSuperclass() != null) searchClassMethods(clz.getSuperclass(), id, options); + + for (Class intf : clz.getInterfaces()) { + searchClassMethods(intf, id, options); + } + + for (Method method : clz.getDeclaredMethods()) { //TODO: java <-> ruby conversion? if (!method.getName().equals(id)) continue; final int mod = method.getModifiers(); - if ( !Modifier.isPublic(mod) && !Modifier.isProtected(mod) ) continue; + if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) continue; //TODO: no arity checks? /* @@ -2015,24 +2045,23 @@ private Collection searchClassMethods(Class clz, String id, HashMap> zeroArg = Optional.empty(); List> candidates = new ArrayList<>(); - for (Constructor constructor : reifiedParent.getDeclaredConstructors()) - { + for (Constructor constructor : reifiedParent.getDeclaredConstructors()) { final int mod = constructor.getModifiers(); - if ( !Modifier.isPublic(mod) && !Modifier.isProtected(mod) ) continue; + + if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) continue; + candidates.add(constructor); - if (constructor.getParameterCount() == 0) // TODO: varargs? - { - zeroArg = Optional.of(constructor); - } + // TODO: varargs? + if (constructor.getParameterCount() == 0) zeroArg = Optional.of(constructor); } // TODO: guess from arity? @@ -2042,83 +2071,73 @@ protected void reifyConstructors() cw.visitSource(position.getFile(), null); int superpos = ConcreteJavaProxy.findSuperLine(runtime, methodEntry, position.getLine()); - - if (candidates.size() > 0 ) //TODO: doc: implies javaConstructable? - { + //TODO: doc: implies javaConstructable? + if (candidates.size() > 0 ) { List savedCtorsList = new ArrayList<>(candidates.size()); - for (Constructor constructor : candidates) - { + + for (Constructor constructor : candidates) { savedCtorsList.add(new JavaConstructor(runtime, constructor)); } savedSuperCtors = savedCtorsList.toArray(new JavaConstructor[savedCtorsList.size()]); - } - else - { + } else { //TODO: copy validateArgs //TODO: no ctors = error? - throw runtime.newTypeError("class " +reifiedParent.getName()+ " doesn't have a public or protected constructor"); + throw runtime.newTypeError("class " + reifiedParent.getName() + " doesn't have a public or protected constructor"); } - if (zeroArg.isPresent()) - { + if (zeroArg.isPresent()) { rubyctor = true; //TODO: simpleAlloc = true; // standard constructor that accepts Ruby, RubyClass. For use by JRuby (internally) -if (!jcc.allCtors) //TODO: fix logic -{ + if (!jcc.allCtors) { //TODO: fix logic //if (jcc.rubyConstructable) RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); - - - if (jcc.javaConstructable) - { - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); - } -} - } - /*else - { + + if (jcc.javaConstructable) { + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); + } + } + } + /* else { //TODO: copy jpcf logic for alloc throw new RuntimeException("Class doesn't have no-arg ctor"); }*/ //TODO: remove rubyCtors if IRO is enabled (by default) //TODO: warn if no ruby ctors (arity check?) - if (jcc.allCtors) - { - for (Constructor constructor : candidates) - { + if (jcc.allCtors) { + for (Constructor constructor : candidates) { //TODO: do matching for zero arg? // if (zeroArg.isPresent() && constructor == zeroArg.get()) continue; - if (jcc.rubyConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); + if (jcc.rubyConstructable) { + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); + } - if (jcc.javaConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); - + if (jcc.javaConstructable) { + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); + } } } - if (jcc.extraCtors != null && jcc.extraCtors.length > 0) - { - for (Class[] constructor : jcc.extraCtors) - { + if (jcc.extraCtors != null && jcc.extraCtors.length > 0) { + for (Class[] constructor : jcc.extraCtors) { //TODO: do matching for zero arg? if (constructor.length == 0 && zeroArg.isPresent()) continue; // TODO: ensure not existing already // TODO: support annotations - if (jcc.rubyConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); + if (jcc.rubyConstructable) { + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); + } - if (jcc.javaConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); - + if (jcc.javaConstructable) { + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); + } } } - if (jcc.IroCtors) - { + + if (jcc.IroCtors) { RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, new Class[] {ConcreteJavaProxy.class, IRubyObject[].class, Block.class}, savedSuperCtors, CtorFlags.RubyArgs); } } @@ -2129,8 +2148,7 @@ protected void reifyConstructors() * method that is re-implemented by ruby */ @Override - protected void generateObjectBarrier(SkinnyMethodAdapter m) - { + protected void generateObjectBarrier(SkinnyMethodAdapter m) { // For non-concrete things, we check, as this is not a RubyObject m.aload(0); m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); @@ -2138,8 +2156,7 @@ protected void generateObjectBarrier(SkinnyMethodAdapter m) m.invokevirtual(rubyPath, "ensureThis", sig(void.class, Object.class)); } - private void defineAllocator() - { + private void defineAllocator() { SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_SYNTHETIC | ACC_PUBLIC | ACC_STATIC, "__allocate__", sig(IRubyObject.class, Ruby.class, RubyClass.class), null, null); m.newobj(javaPath); m.dup(); @@ -2151,8 +2168,7 @@ private void defineAllocator() m.end(); } - private void defineInterfaceMethods() - { + private void defineInterfaceMethods() { SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_SYNTHETIC | ACC_PUBLIC, "___jruby$rubyObject", sig(IRubyObject.class), null, null); m.aload(0); // this m.getfield(javaPath, RUBY_OBJECT_FIELD, rubyName); @@ -2165,7 +2181,6 @@ private void defineInterfaceMethods() m.areturn(); m.end(); } - } // class ConcreteJavaReifier private boolean isVarArgsSignature(final String method, final Class[] methodSignature) { @@ -2198,22 +2213,20 @@ public Class getReifiedAnyClass() { * Gets a reified Ruby class. Throws if this is a Java class */ public Class getReifiedRubyClass() { - if (reifiedClassJava == Boolean.TRUE) - // TODO: error type - throw runtime.newTypeError("Attempted to get a Ruby class for a Java class"); - else - return reifiedClass; + // TODO: error type + if (reifiedClassJava == Boolean.TRUE) throw runtime.newTypeError("Attempted to get a Ruby class for a Java class"); + + return reifiedClass; } /** * Gets a reified Java class. Throws if this is a Ruby class */ public Class getReifiedJavaClass() { - if (reifiedClassJava == Boolean.FALSE) - // TODO: error type - throw runtime.newTypeError("Attempted to get a Java class for a Ruby class"); - else - return reifiedClass; + // TODO: error type + if (reifiedClassJava == Boolean.FALSE) throw runtime.newTypeError("Attempted to get a Java class for a Ruby class"); + + return reifiedClass; } public static Class nearestReifiedClass(final RubyClass klass) { diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index 9115aa82ad6..dc7b2341daa 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -337,10 +337,8 @@ public IRubyObject yield(ThreadContext _context, IRubyObject[] _args) } - public static int findSuperLine(Ruby runtime, DynamicMethod dm, int start) - { - if (dm != null && !(dm instanceof InitializeMethod)) - { + public static int findSuperLine(Ruby runtime, DynamicMethod dm, int start) { + if (dm != null && !(dm instanceof InitializeMethod)) { //TODO: if not defined, then ctors = all valid superctors DefNode def = ((IRMethod)((AbstractIRMethod)dm).getIRScope()).desugar(); FlatExtractor flat = new FlatExtractor(runtime, def); From 913736cde59570ace690af76c0248b322c5a279f Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Wed, 25 Nov 2020 15:41:53 -0500 Subject: [PATCH 17/49] Super bridges, partial work on ruby instanceMethods) { // even if we didn't specify it manually if (methodSignature == null) { + //TODO: should inherited search for java mangledName? for (Class[] sig : searchInheritedSignatures(id, arity)) { String signature = defineInstanceMethod(id, arity, position, sig); @@ -1830,6 +1838,11 @@ protected String defineInstanceMethod(final String id, final Arity arity, RealClassGenerator.coerceArgumentsToRuby(m, params, rubyIndex); rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class)); RealClassGenerator.coerceResultAndReturn(m, methodSignature[0]); + + // generate any bridge methods needed as we overrode a defined one + //TODO: push down to all cases + if (!isRubyObject()) + generateSuperBridges(javaMethodName, methodSignature); } m.end(); @@ -1838,7 +1851,12 @@ protected String defineInstanceMethod(final String id, final Arity arity, return javaMethodName + signature; } - /** + protected void generateSuperBridges(String javaMethodName, Class[] methodSignature) + { + // Only for concrete java + } + + /** * This method generates <clinit> by marshaling the Ruby, RubyClass, etc variables * through a static map identified by integer in JavaProxyClass. Integers are serializable * through bytecode generation so we can share arbitrary objects with the generated class @@ -1902,6 +1920,7 @@ public class ConcreteJavaReifier extends MethodReificator boolean rubyctor = false; boolean simpleAlloc = false; JavaConstructor[] savedSuperCtors = null; + Map> supers = new HashMap<>(); ConcreteJavaReifier(Class reifiedParent, String javaName, String javaPath) { @@ -1977,12 +1996,74 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) //TODO: eww, where should this call go? here? there? I dislike the leaking of the abstraction across a larger part of the code than necessary m.invokestatic(p(JavaProxyClass.class), "setProxyClassReified", sig(JavaProxyClass.class, Ruby.class, RubyClass.class, Class.class, boolean.class)); + m.dup(); m.putstatic(javaPath, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class)); + + supers.forEach((name, sigs) -> { + + for (String sig : sigs) + { + m.dup(); + m.ldc(name); + m.ldc(sig); + m.iconst_1(); + m.invokevirtual(p(JavaProxyClass.class), "initMethod", sig(void.class, String.class, String.class, boolean.class)); + } + }); + m.pop(); // Note: no end, that's in the parent call } + + @Override + protected void generateSuperBridges(String javaMethodName, Class[] methodSignature) + { + //TODO: cache, don't look up this+ sIS repetedly + + // don't look on interfaces, just the parent + Class[] args = new Class[methodSignature.length - 1]; + ArraySupport.copy(methodSignature, 1, args, 0, methodSignature.length - 1); + Method supr = findTarget(reifiedParent, javaMethodName, methodSignature[0], args); + if (supr == null) return; + - @Override + SkinnyMethodAdapter m = new SkinnyMethodAdapter(cw, ACC_SYNTHETIC | ACC_BRIDGE | ACC_PUBLIC, "__super$"+javaMethodName, sig(methodSignature), null, null); + GeneratorAdapter ga = RealClassGenerator.makeGenerator(m); + ga.loadThis(); //TODO: which style? + //m.aload(0); // this //TODO: which style? + ga.loadArgs(); + m.invokespecial(p(reifiedParent), javaMethodName, sig(methodSignature)); + ga.returnValue(); + ga.endMethod(); + + if (!supers.containsKey(javaMethodName)) + supers.put(javaMethodName, new ArrayList<>()); + + supers.get(javaMethodName).add(sig(methodSignature)); + } + + private Method findTarget(Class clz, String javaMethodName, Class returns, Class[] params) //TODO: variable name pass + { + for (Method method : clz.getDeclaredMethods()) + { + if (!method.getName().equals(javaMethodName)) continue; + final int mod = method.getModifiers(); + if ( !Modifier.isPublic(mod) && !Modifier.isProtected(mod) ) continue; + if (Modifier.isAbstract(mod)) continue; + + //TODO: is args necessary? + if (!method.getReturnType().equals(returns)) continue; + if (!Arrays.equals(method.getParameterTypes(), params)) continue; + + //TODO: final? + return method; + } + if (clz.getSuperclass() != null) + return findTarget(clz.getSuperclass(), javaMethodName, returns, params); + return null; + } + + @Override //TODO: check for final? protected Collection searchInheritedSignatures(String id, Arity arity) { HashMap types = new HashMap<>(); diff --git a/core/src/main/java/org/jruby/ir/IRMethod.java b/core/src/main/java/org/jruby/ir/IRMethod.java index ad7d2beecc7..84a5433f356 100644 --- a/core/src/main/java/org/jruby/ir/IRMethod.java +++ b/core/src/main/java/org/jruby/ir/IRMethod.java @@ -108,7 +108,7 @@ private synchronized void buildMethodImpl() { IRBuilder.topIRBuilder(getManager(), this). defineMethodInner(defNode, getLexicalParent(), getCoverageMode()); // sets interpreterContext - this.defNode = null; + //this.defNode = null; } public BasicBlock[] prepareForCompilation() { diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index 9115aa82ad6..77c66be3ad3 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -339,15 +339,24 @@ public IRubyObject yield(ThreadContext _context, IRubyObject[] _args) public static int findSuperLine(Ruby runtime, DynamicMethod dm, int start) { + try + { if (dm != null && !(dm instanceof InitializeMethod)) { //TODO: if not defined, then ctors = all valid superctors DefNode def = ((IRMethod)((AbstractIRMethod)dm).getIRScope()).desugar(); + System.out.println("def is " + (def == null)); FlatExtractor flat = new FlatExtractor(runtime, def); + System.out.println("defb is " + (def.getBodyNode() == null)); Node body = def.getBodyNode().accept(flat); if (flat.foundsuper && flat.superline > -1) return flat.superline + 1; // convert from 0-based to 1-based } + } + catch(Exception e) + { + e.printStackTrace(); + } return start; } @@ -363,7 +372,7 @@ public RubyArray splitInitialized(IRubyObject[] args, Block blk) if (dm != null && (dm instanceof StaticJCreateMethod)) dm = ((StaticJCreateMethod)dm).getOriginal(); DynamicMethod dm1 = getMetaClass().retrieveMethod("initialize"); // only on ourself - if ((dm1 != null && !(dm instanceof InitializeMethod))) + if ((dm1 != null && !(dm instanceof InitializeMethod)&& !(dm instanceof StaticJCreateMethod))) //jcreate is for nested ruby classes from a java class { //TODO: if not defined, then ctors = all valid superctors DefNode def = ((IRMethod)((AbstractIRMethod)dm).getIRScope()).desugar(); diff --git a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java index 6665f28347e..79ea14fb548 100644 --- a/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java +++ b/core/src/main/java/org/jruby/javasupport/proxy/JavaProxyClass.java @@ -458,8 +458,9 @@ public final int getArity() { } + //called from reified java concrete-extended classes @SuppressWarnings("unchecked") - JavaProxyMethod initMethod(final String name, final String desc, final boolean hasSuper) { + public void initMethod(final String name, final String desc, final boolean hasSuper) { final Class proxy = this.proxyClass; try { Class[] paramTypes = parse(proxy.getClassLoader(), desc); @@ -479,7 +480,7 @@ JavaProxyMethod initMethod(final String name, final String desc, final boolean h } methodsWithName.add(proxyMethod); - return proxyMethod; + //return proxyMethod; } catch (ClassNotFoundException e) { throw new InternalError(e.getMessage()); diff --git a/spec/java_integration/reify/become_java_spec.rb b/spec/java_integration/reify/become_java_spec.rb index 8aae71ff3df..6217c8ea5ae 100644 --- a/spec/java_integration/reify/become_java_spec.rb +++ b/spec/java_integration/reify/become_java_spec.rb @@ -81,7 +81,7 @@ class TopRightOfTheStack < MiddleOfTheStack ; end java_class = TopRightOfTheStack.become_java! expect(java_class).not_to be_nil - java_class = TopRightOfTheStack.to_java.getReifiedClass + java_class = TopRightOfTheStack.to_java.getReifiedRubyClass expect(java_class).not_to be_nil end From cf4adfe2ed41c95b01c7679f902b9d4669603b50 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Wed, 25 Nov 2020 16:05:54 -0500 Subject: [PATCH 18/49] Move some gen code into split/finish Init pair --- .../java/codegen/RealClassGenerator.java | 28 ++++++++++--------- .../jruby/java/proxies/ConcreteJavaProxy.java | 25 +++++++++++------ 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java index c9d70361c08..4d0ee32643d 100644 --- a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java +++ b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java @@ -933,7 +933,7 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware i { m.aload(3); // load block from arg 3 } - m.invokevirtual(cjr.rubyPath, "splitInitialized", sig(RubyArray.class, IRubyObject[].class, Block.class) ); //pushes rubyarray + m.invokevirtual(cjr.rubyPath, "splitInitialized", sig(Object[].class, IRubyObject[].class, Block.class) ); //pushes rubyarray m.dup(); // rubyarray (results of splitInitialized) m.line(superpos); // mark this line as the super call, so the stack trace is slightly accurate. @@ -941,11 +941,8 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware i //// c = ra.entry(0); ////IRubyObject continuation = ra.entry(1); m.iconst_0(); // ..., rubyarray, rubyarray, 0 - m.swap(); // ..., rubyarray, 0, rubyarray - m.iconst_1(); // ..., rubyarray, 0, rubyarray, 1 - m.invokevirtual(p(RubyArray.class), "entry", sig(IRubyObject.class, int.class)); - m.astore(rubyContinuation); // ..., rubyarray, 0 - m.invokevirtual(p(RubyArray.class), "entry", sig(IRubyObject.class, int.class)); + m.aaload(); // ..., rubyarray, rubyobject(Object) + m.checkcast(p(IRubyObject.class)); // top of stack is now the arg list ruby array //// switch(Java.JCreateMethod.forTypes(constructors, c)) @@ -1040,20 +1037,25 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware i m.aload(0); // initialized this m.dup(); m.getfield(cjr.javaPath, ConcreteJavaReifier.RUBY_OBJECT_FIELD, cjr.rubyName);// ..., this, rubyobj - m.swap(); // ..., rubyobject, this + if (callsInit) m.dup_x1(); // rubyobject, this, rubyobject + m.swap(); // ..., rubyobject, rubyobject, this m.invokevirtual(p(ConcreteJavaProxy.class), "setObject", sig(void.class, Object.class)); // finish init if started if (callsInit) { ////continuation.callMethod(ruby.getTheadContext(), "call") - m.aload(rubyContinuation); - m.aload(rubyIndex); - m.invokevirtual(p(Ruby.class), "getCurrentContext", sig(ThreadContext.class)); - m.ldc("call"); + //m.aload(rubyContinuation); + + //m.aload(rubyIndex); + //m.invokevirtual(p(Ruby.class), "getCurrentContext", sig(ThreadContext.class)); + //m.ldc("call"); // not rubycall as using IRubyObject vs RubyBasicObject - m.invokeinterface(p(IRubyObject.class), "callMethod", sig(IRubyObject.class, ThreadContext.class, String.class)); - m.pop(); + //m.invokeinterface(p(IRubyObject.class), "callMethod", sig(IRubyObject.class, ThreadContext.class, String.class)); + //m.pop(); + m.swap(); + m.invokevirtual(p(ConcreteJavaProxy.class), "finishInitialize", sig(void.class, Object[].class)); + m.pop(); //TODO: track down this extra arg } m.voidreturn(); diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index 77c66be3ad3..bc637ec7329 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -299,11 +299,11 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz //TODO: cleanup public static final class SimpleJavaInitializes { - public static RubyArray freshMethodArray(DynamicMethod initialize, Ruby runtime, IRubyObject self, RubyModule clazz, String name, + public static Object[] freshMethodArray(DynamicMethod initialize, Ruby runtime, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) { - return runtime.newArray( + return new Object[] { runtime.newArray(args), //TODO:? runtime.newProc(Block.Type.LAMBDA, new Block(new JavaInternalBlockBody(runtime, Signature.from(initialize.getArity())) { @@ -315,13 +315,13 @@ public IRubyObject yield(ThreadContext _context, IRubyObject[] _args) } })) - ); + }; } - public static RubyArray freshNopArray(Ruby runtime, IRubyObject[] args) + public static Object[] freshNopArray(Ruby runtime, IRubyObject[] args) { - return runtime.newArray( + return new Object[] { runtime.newArray(args), runtime.newProc(Block.Type.LAMBDA, new Block(new JavaInternalBlockBody(runtime, Signature.OPTIONAL) { @@ -332,7 +332,7 @@ public IRubyObject yield(ThreadContext _context, IRubyObject[] _args) } })) - ); + }; } } @@ -361,7 +361,7 @@ public static int findSuperLine(Ruby runtime, DynamicMethod dm, int start) } // used by reified classes - public RubyArray splitInitialized(IRubyObject[] args, Block blk) + public Object[] splitInitialized(IRubyObject[] args, Block blk) { DynamicMethod dm = this.getMetaClass().searchMethod("j_initialize"); @@ -421,7 +421,16 @@ public RubyArray splitInitialized(IRubyObject[] args, Block blk) } /// TODO: move gen here - return callMethod(getRuntime().getCurrentContext(), "j_initialize", args, blk).convertToArray(); + RubyArray ra = callMethod(getRuntime().getCurrentContext(), "j_initialize", args, blk).convertToArray(); + + return new Object[] {ra.entry(0), ra.entry(1) }; + } + + // called from concrete reified code + public void finishInitialize(Object[] returned) + { + // returned = splitInitialize return value + ((IRubyObject)returned[1]).callMethod(getRuntime().getCurrentContext(), "call"); } // used by reified classes From e0d2ddbec40e10b064e061922191a3e7683a5bd1 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Fri, 27 Nov 2020 00:24:06 -0500 Subject: [PATCH 19/49] Down to 1+4+ant failures --- core/src/main/java/org/jruby/RubyClass.java | 103 +++++++++--------- .../jruby/java/proxies/ConcreteJavaProxy.java | 4 +- .../util/JavaClassConfiguration.java | 2 +- 3 files changed, 57 insertions(+), 52 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index caf146a424b..db570eb383a 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -156,7 +156,7 @@ public IRubyObject allocate(Ruby runtime, RubyClass klazz) { } }; - this.reifiedClass = clazz; + this.reifiedClass = (Class) clazz; } catch (NoSuchMethodException nsme) { throw new RuntimeException(nsme); } @@ -189,7 +189,7 @@ public IRubyObject allocate(Ruby runtime, RubyClass klazz) { } }; - this.reifiedClass = clazz; + this.reifiedClass = (Class) clazz; } catch (NoSuchMethodException nsme) { throw new RuntimeException(nsme); } @@ -1225,10 +1225,6 @@ public Object unmarshalFrom(Ruby runtime, RubyClass type, * @return true if the class can be reified, false otherwise */ public boolean isReifiable(boolean[] java) { - if (this.getBaseName().equals("SimpleFXApplication")) - { - System.out.println("brewk"); - } // already reified is not reifiable if (reifiedClass != null) return false; @@ -1245,7 +1241,7 @@ public boolean isReifiable(boolean[] java) { boolean result = reifiedSuper == RubyObject.class || reifiedSuper == RubyBasicObject.class || Reified.class.isAssignableFrom(reifiedSuper); - // TODO: check for nested java classes + // TODO: check & test for nested java classes //TODO: RJP is Reified. is that reasonable? if (result && !ReifiedJavaProxy.class.isAssignableFrom(reifiedSuper)) return true; @@ -1895,10 +1891,47 @@ protected void extraClinitLookup(SkinnyMethodAdapter m) { m.pop(); } - +//TODO: test interfaces on ruby, and not defining a method on intf, abstract class protected Collection searchInheritedSignatures(String id, Arity arity) { - return Collections.EMPTY_LIST; + HashMap types = new HashMap<>(); + for (Class intf : Java.getInterfacesFromRubyClass(RubyClass.this)) + searchClassMethods(intf, arity, id, types); + if (types.size() == 0) + types.put("", null); + return types.values(); + } + + protected Collection searchClassMethods(Class clz, Arity arity, String id, HashMap options) + { + if (clz.getSuperclass() != null) + searchClassMethods(clz.getSuperclass(),arity, id, options); + for (Class intf : clz.getInterfaces()) + searchClassMethods(intf, arity, id, options); + for (Method method : clz.getDeclaredMethods()) + { + //TODO: java <-> ruby conversion? + if (!method.getName().equals(id)) continue; + final int mod = method.getModifiers(); + if ( !Modifier.isPublic(mod) && !Modifier.isProtected(mod) ) continue; + + //TODO: no arity checks? + if (arity != null) + { + // ensure arity is reasonable (ignores java varargs) + if (arity.isFixed()) + { + if (arity.required() != method.getParameterCount()) continue; + } + else if (arity.required() > method.getParameterCount()) continue; + } + + // found! built a signature to return + Class[] types = join(new Class[]{method.getReturnType()}, method.getParameterTypes()); + options.put(sig(types), types); + } + // Note: not stable. May flicker between different arities. TODO: sort? + return options.values(); } protected void generateObjectBarrier(SkinnyMethodAdapter m) @@ -2067,48 +2100,20 @@ private Method findTarget(Class clz, String javaMethodName, Class returns, protected Collection searchInheritedSignatures(String id, Arity arity) { HashMap types = new HashMap<>(); - Collection best = searchClassMethods(reifiedParent, arity, id, types); - for (Class intf : Java.getInterfacesFromRubyClass(RubyClass.this)) + searchClassMethods(reifiedParent, arity, id, types); + for (Class intf : Java.getInterfacesFromRubyClass(RubyClass.this))// TODO: cleanup duplication in 3 places (super) searchClassMethods(intf, arity, id, types); - if (best.size() == 0) + if (types.size() == 0) { - best = searchClassMethods(reifiedParent, null, id, types); + searchClassMethods(reifiedParent, null, id, types); for (Class intf : Java.getInterfacesFromRubyClass(RubyClass.this)) searchClassMethods(intf, null, id, types); } - return best; - } - - private Collection searchClassMethods(Class clz, Arity arity, String id, HashMap options) - { - if (clz.getSuperclass() != null) - searchClassMethods(clz.getSuperclass(),arity, id, options); - for (Class intf : clz.getInterfaces()) - searchClassMethods(intf, arity, id, options); - for (Method method : clz.getDeclaredMethods()) + if (types.size() == 0) { - //TODO: java <-> ruby conversion? - if (!method.getName().equals(id)) continue; - final int mod = method.getModifiers(); - if ( !Modifier.isPublic(mod) && !Modifier.isProtected(mod) ) continue; - - //TODO: no arity checks? - if (arity != null) - { - // ensure arity is reasonable (ignores java varargs) - if (arity.isFixed()) - { - if (arity.required() != method.getParameterCount()) continue; - } - else if (arity.required() > method.getParameterCount()) continue; - } - - // found! built a signature to return - Class[] types = join(new Class[]{method.getReturnType()}, method.getParameterTypes()); - options.put(sig(types), types); + types.put("", null); } - // Note: not stable. May flicker between different arities. TODO: sort? - return options.values(); + return types.values(); } @Override @@ -2275,14 +2280,14 @@ private void logReifyException(final Throwable failure, final boolean error) { } public void setReifiedClass(Class reifiedClass) { - this.reifiedClass = reifiedClass; + this.reifiedClass = (Class) reifiedClass; //Not always true } /** * Gets a reified Ruby or Java class. * To ensure a specific type, see {@link #getReifiedRubyClass()} or {@link #getReifiedJavaClass()} */ - public Class getReifiedAnyClass() { + public Class getReifiedAnyClass() { return reifiedClass; } @@ -2294,7 +2299,7 @@ public Class getReifiedRubyClass() { // TODO: error type throw runtime.newTypeError("Attempted to get a Ruby class for a Java class"); else - return reifiedClass; + return (Class) reifiedClass; } /** @@ -2305,7 +2310,7 @@ public Class getReifiedJavaClass() { // TODO: error type throw runtime.newTypeError("Attempted to get a Java class for a Ruby class"); else - return reifiedClass; + return (Class) reifiedClass; } public static Class nearestReifiedClass(final RubyClass klass) { @@ -3061,7 +3066,7 @@ public static CS_NAMES fromOrdinal(int ordinal) { private CallSite[] extraCallSites; - private Class reifiedClass; + private Class reifiedClass; private Boolean reifiedClassJava; private Map>>> parameterAnnotations; diff --git a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java index bc637ec7329..5a9430095f8 100644 --- a/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java +++ b/core/src/main/java/org/jruby/java/proxies/ConcreteJavaProxy.java @@ -271,8 +271,8 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz JavaObject jo = (JavaObject)initialize.call(context, self, clazz, "new", args); return ((ReifiedJavaProxy)jo.getValue()).___jruby$rubyObject(); - } - catch (ArgumentError ae) + }//TODO: the latter two shouldn't be caught here + catch (ArgumentError | AssertionError | ClassCastException ae) { System.out.println("AE"); // assume no easy conversions, use ruby fallback. diff --git a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java index c6534c1f2fc..905d6909f63 100644 --- a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java +++ b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java @@ -4,7 +4,7 @@ public class JavaClassConfiguration { public boolean callInitialize = true; public boolean allMethods = true; - public boolean allClassMethods = false; // TODO: ensure defaults are sane + public boolean allClassMethods = true; // TODO: ensure defaults are sane public boolean javaConstructable = true; public Class[][] extraCtors = null; From 52dd70f67caef06277f2603c765bb15b42f02896 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Fri, 27 Nov 2020 01:32:01 -0500 Subject: [PATCH 20/49] Very questionable implementation of ruby < ruby < java hierarchy --- core/src/main/java/org/jruby/RubyClass.java | 26 +++++----- .../java/codegen/RealClassGenerator.java | 51 ++++++++++++++++++- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index db570eb383a..436d7bc17a6 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -1983,7 +1983,7 @@ protected void loadRubyObject(SkinnyMethodAdapter m) @Override public byte[] reify() { - cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_PRIVATE, RUBY_OBJECT_FIELD, rubyName, null, null); + cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_PROTECTED, RUBY_OBJECT_FIELD, rubyName, null, null); cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_PROXY_CLASS_FIELD, ci(JavaProxyClass.class), null, null); cw.visitField(ACC_SYNTHETIC | ACC_FINAL | ACC_STATIC | ACC_PRIVATE, RUBY_CTOR_CACHE_FIELD, ci(JCtorCache.class), null, null); return super.reify(); @@ -2131,6 +2131,7 @@ protected void reifyConstructors() zeroArg = Optional.of(constructor); } } + boolean isNestedRuby = ReifiedJavaProxy.class.isAssignableFrom(reifiedParent); // TODO: guess from arity? // update the source location @@ -2158,18 +2159,19 @@ protected void reifyConstructors() if (zeroArg.isPresent()) { - rubyctor = true; + rubyctor = !isNestedRuby; //TODO: simpleAlloc = true; // standard constructor that accepts Ruby, RubyClass. For use by JRuby (internally) if (!jcc.allCtors) //TODO: fix logic { //if (jcc.rubyConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); + if (!isNestedRuby) + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, isNestedRuby, this, new Class[0], savedSuperCtors, CtorFlags.Normal); if (jcc.javaConstructable) { - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, new Class[0], savedSuperCtors, CtorFlags.Normal); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, isNestedRuby, this, new Class[0], savedSuperCtors, CtorFlags.Normal); } } } @@ -2181,7 +2183,7 @@ protected void reifyConstructors() //TODO: remove rubyCtors if IRO is enabled (by default) //TODO: warn if no ruby ctors (arity check?) - if (jcc.allCtors) + if (jcc.allCtors && !isNestedRuby) { for (Constructor constructor : candidates) { @@ -2189,15 +2191,15 @@ protected void reifyConstructors() // if (zeroArg.isPresent() && constructor == zeroArg.get()) continue; if (jcc.rubyConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, isNestedRuby, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); if (jcc.javaConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, isNestedRuby, this, constructor.getParameterTypes(), savedSuperCtors, CtorFlags.Normal); } } - if (jcc.extraCtors != null && jcc.extraCtors.length > 0) + if (jcc.extraCtors != null && jcc.extraCtors.length > 0 && !isNestedRuby) { for (Class[] constructor : jcc.extraCtors) { @@ -2207,16 +2209,16 @@ protected void reifyConstructors() // TODO: support annotations if (jcc.rubyConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, isNestedRuby, this, constructor, savedSuperCtors, CtorFlags.NoSuper); if (jcc.javaConstructable) - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, true, this, constructor, savedSuperCtors, CtorFlags.NoSuper); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, false, isNestedRuby, this, constructor, savedSuperCtors, CtorFlags.NoSuper); } } - if (jcc.IroCtors) + if (jcc.IroCtors || isNestedRuby) // TODO: ensure parent is iro-enabled { - RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, true, this, new Class[] {ConcreteJavaProxy.class, IRubyObject[].class, Block.class}, savedSuperCtors, CtorFlags.RubyArgs); + RealClassGenerator.makeConcreteConstructorSwitch(cw, position, superpos, true, isNestedRuby, this, new Class[] {ConcreteJavaProxy.class, IRubyObject[].class, Block.class}, savedSuperCtors, CtorFlags.RubyArgs); } } diff --git a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java index 4d0ee32643d..51a724a1180 100644 --- a/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java +++ b/core/src/main/java/org/jruby/java/codegen/RealClassGenerator.java @@ -838,9 +838,13 @@ public static enum CtorFlags // TODO: add CJP ctor for alloc sep? //:TODO: Add IRubyobject ctor? // shouls this be in in this spot? - public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware initPosition, int superpos, boolean hasRuby, boolean callsInit, ConcreteJavaReifier cjr, Class[] ctorTypes, JavaConstructor[] constructors, CtorFlags flag) + public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware initPosition, int superpos, boolean hasRuby, boolean isNestedRuby, ConcreteJavaReifier cjr, Class[] ctorTypes, JavaConstructor[] constructors, CtorFlags flag) { + final boolean callsInit = true; // TODO: ???? + + + // TODO: add source position of super call /* This generates this code template. Note that lines of //// show what code is being generated @@ -882,6 +886,7 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware i //// Ruby varRubyIndex = ruby; final int baseIndex = RealClassGenerator.calcBaseIndex(ctorTypes, 1); final int rubyIndex = baseIndex; + final int rubyClassIndex = baseIndex+1; final int rubyArrayIndex = baseIndex+2; final int rubyContinuation = baseIndex+3; final int rubyInitArgs = baseIndex+4; @@ -891,6 +896,48 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware i m.getstatic(cjr.javaPath, "ruby", ci(Ruby.class)); m.astore(rubyIndex); // save ruby in local } + + if (isNestedRuby) //TODO: this skips the parent's initialize method + { + // TODO: support more than ruby or ()/no-arg ctors + if (flag == CtorFlags.RubyArgs) //IRO ctor means passthrough + { + //m.aload(0); // uninitialized this + m.aload(1); // cjp + m.aload(2); // args + m.aload(3); // block + m.aload(4); // ruby + m.aload(5); // rubyclass + m.invokespecial(p(cjr.reifiedParent), "", sig); + + } + else + { + + // m.aload(0); // uninitialized this + m.newobj(p(ConcreteJavaProxy.class)); + m.dup(); // rubyobject + m.aload(rubyIndex); // ruby + m.getstatic(cjr.javaPath, "rubyClass", ci(RubyClass.class)); // rubyclass + m.invokespecial(p(ConcreteJavaProxy.class), "", sig(void.class, Ruby.class, RubyClass.class)); + + m.getstatic(p(IRubyObject.class), "NULL_ARRAY", ci(IRubyObject[].class)); // args + m.getstatic(p(Block.class), "NULL_BLOCK", ci(Block.class)); + m.aload(rubyIndex); // ruby + m.getstatic(cjr.javaPath, "rubyClass", ci(RubyClass.class)); + m.invokespecial(p(cjr.reifiedParent), "", sig(void.class, ConcreteJavaProxy.class, IRubyObject[].class, Block.class, Ruby.class, RubyClass.class)); + } + // this.rubyobj = super.rubyobj + m.aload(0); // initialized this + m.dup(); + m.getfield(p(cjr.reifiedParent), ConcreteJavaReifier.RUBY_OBJECT_FIELD, cjr.rubyName); + m.putfield(cjr.javaPath, ConcreteJavaReifier.RUBY_OBJECT_FIELD, cjr.rubyName); + + m.voidreturn(); + m.end(); + return; + } + //// this.$rubyInitArgs = new IRubyObject[]{JavaUtil.convertJavaToRuby(var3, var1), JavaUtil.convertJavaToUsableRubyObject(var3, var2)}; if (flag == CtorFlags.Normal || flag == CtorFlags.NoSuper) RealClassGenerator.coerceArgumentsToRuby(m, ctorTypes, rubyIndex); @@ -907,7 +954,7 @@ public static void makeConcreteConstructorSwitch(ClassWriter cw, PositionAware i m.dup(); // rubyobject m.aload(rubyIndex); // ruby if (hasRuby) - m.aload(rubyIndex+1); // rubyclass + m.aload(rubyClassIndex); // rubyclass else m.getstatic(cjr.javaPath, "rubyClass", ci(RubyClass.class)); // rubyclass m.invokespecial(p(ConcreteJavaProxy.class), "", sig(void.class, Ruby.class, RubyClass.class)); From 7859cd5c226dec2799ab0bdb22a1e4c0d454e420 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Fri, 27 Nov 2020 02:54:59 -0500 Subject: [PATCH 21/49] Method overrides on the java side --- core/src/main/java/org/jruby/RubyClass.java | 42 ++++++++++++------- .../util/JavaClassConfiguration.java | 4 ++ lib/ruby/stdlib/jruby/core_ext/class.rb | 2 + 3 files changed, 33 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index 436d7bc17a6..73cba12b0e9 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -1697,44 +1697,56 @@ private void defineClassMethods(Set instanceMethods) { //TODO: only generate that are overrideable (javaproxyclass) protected void defineInstanceMethods(Set instanceMethods) { + Set defined = new HashSet<>(); for (Map.Entry methodEntry : getMethods().entrySet()) { final String id = methodEntry.getKey(); - final Arity arity = methodEntry.getValue().getArity(); + final String callid = jcc.renamedMethods.getOrDefault(id, id); // TODO: use set to avoid the map + if (defined.contains(id)) + continue; + defined.add(callid); //TODO: is this true? // id we won't see again, and are only defining java methods named id + + + DynamicMethod method = methodEntry.getValue(); + if (id != callid) // identity is fine as it's the default + { + method = searchMethod(callid); + } + final Arity arity = method.getArity(); PositionAware position = getPositionOrDefault(methodEntry.getValue()); if (position.getLine() > 1) cw.visitSource(position.getFile(), null); - Class[] methodSignature = getMethodSignatures().get(id); + Class[] methodSignature = getMethodSignatures().get(callid); // ruby side, use callid // for concrete extension, see if the method is one we are overriding, // even if we didn't specify it manually if (methodSignature == null) { //TODO: should inherited search for java mangledName? - for (Class[] sig : searchInheritedSignatures(id, arity)) + for (Class[] sig : searchInheritedSignatures(id, arity)) // id (vs callid) here as this is searching in java { - String signature = defineInstanceMethod(id, arity, position, sig); + String signature = defineInstanceMethod(id, callid, arity, position, sig); if (signature != null) instanceMethods.add(signature); } } else { - String signature = defineInstanceMethod(id, arity, position, methodSignature); + String signature = defineInstanceMethod(id, callid, arity, position, methodSignature); if (signature != null) instanceMethods.add(signature); } } } - protected String defineInstanceMethod(final String id, final Arity arity, + protected String defineInstanceMethod(final String id, final String callid, final Arity arity, PositionAware position, Class[] methodSignature) { String javaMethodName = JavaNameMangler.mangleMethodName(id); - Map> methodAnnos = getMethodAnnotations().get(id); - List>> parameterAnnos = getParameterAnnotations().get(id); + Map> methodAnnos = getMethodAnnotations().get(callid); // ruby side, use callid + List>> parameterAnnos = getParameterAnnotations().get(callid); // ruby side, use callid final String signature; SkinnyMethodAdapter m; @@ -1750,7 +1762,7 @@ protected String defineInstanceMethod(final String id, final Arity arity, generateObjectBarrier(m); loadRubyObject(m); // self/rubyObject - m.ldc(id); + m.ldc(callid); rubycall(m, sig(IRubyObject.class, String.class)); break; case 1: @@ -1761,7 +1773,7 @@ protected String defineInstanceMethod(final String id, final Arity arity, generateObjectBarrier(m); loadRubyObject(m); // self/rubyObject - m.ldc(id); + m.ldc(callid); m.aload(1); // IRubyObject arg1 rubycall(m, sig(IRubyObject.class, String.class, IRubyObject.class)); break; @@ -1781,7 +1793,7 @@ protected String defineInstanceMethod(final String id, final Arity arity, generateObjectBarrier(m); loadRubyObject(m); // self/rubyObject - m.ldc(id); + m.ldc(callid); // generate an IRubyObject[] for the method arguments : m.pushInt(paramCount); @@ -1802,7 +1814,7 @@ protected String defineInstanceMethod(final String id, final Arity arity, generateObjectBarrier(m); loadRubyObject(m); // self/rubyObject - m.ldc(id); + m.ldc(callid); m.aload(1); // IRubyObject[] arg1 } rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class)); @@ -1819,7 +1831,7 @@ protected String defineInstanceMethod(final String id, final Arity arity, signature = sig(methodSignature[0], params); int mod = ACC_PUBLIC; - if ( isVarArgsSignature(id, methodSignature) ) mod |= ACC_VARARGS; + if ( isVarArgsSignature(callid, methodSignature) ) mod |= ACC_VARARGS; m = new SkinnyMethodAdapter(cw, mod, javaMethodName, signature, null, null); m.line(position.getLine()); generateMethodAnnotations(methodAnnos, m, parameterAnnos); @@ -1829,7 +1841,7 @@ protected String defineInstanceMethod(final String id, final Arity arity, m.astore(rubyIndex); loadRubyObject(m); // self/rubyObject - m.ldc(id); // method name + m.ldc(callid); // method name RealClassGenerator.coerceArgumentsToRuby(m, params, rubyIndex); rubycall(m, sig(IRubyObject.class, String.class, IRubyObject[].class)); @@ -1842,7 +1854,7 @@ protected String defineInstanceMethod(final String id, final Arity arity, } m.end(); - if (DEBUG_REIFY) LOG.debug("defining {}#{} as {}#{}", getName(), id, javaName, javaMethodName + signature); + if (DEBUG_REIFY) LOG.debug("defining {}#{} (calling #{}) as {}#{}", getName(), id, callid, javaName, javaMethodName + signature); return javaMethodName + signature; } diff --git a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java index 905d6909f63..bc86ec6632b 100644 --- a/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java +++ b/core/src/main/java/org/jruby/javasupport/util/JavaClassConfiguration.java @@ -1,5 +1,7 @@ package org.jruby.javasupport.util; +import java.util.*; + public class JavaClassConfiguration { public boolean callInitialize = true; @@ -13,6 +15,8 @@ public class JavaClassConfiguration public boolean rubyConstructable = true; // public boolean IroCtors = true; + public Map renamedMethods = new HashMap<>(); + //TODO: init method name? //TODO: renames? } diff --git a/lib/ruby/stdlib/jruby/core_ext/class.rb b/lib/ruby/stdlib/jruby/core_ext/class.rb index 977f1f85a05..91799e4540a 100644 --- a/lib/ruby/stdlib/jruby/core_ext/class.rb +++ b/lib/ruby/stdlib/jruby/core_ext/class.rb @@ -183,7 +183,9 @@ def configure_java_class(**kwargs) config.allCtors = kwargs[:ctors] == :all if kwargs.has_key? :ctors config.rubyConstructable = kwargs[:ruby_constructable] if kwargs.has_key? :ruby_constructable config.splitSuper = kwargs[:split_super] if kwargs.has_key? :split_super + config.renamedMethods = kwargs[:mapped_methods] if kwargs.has_key? :mapped_methods self_r.class_config = config + # TODO: errors kwargs end From 7f2058cd2cd4888cfd50766a241d5f852f9b8ed7 Mon Sep 17 00:00:00 2001 From: Patrick Plenefisch Date: Fri, 27 Nov 2020 17:16:07 -0500 Subject: [PATCH 22/49] Class#clone should copy local reify state --- core/src/main/java/org/jruby/RubyClass.java | 23 +++++++++++++++++++- core/src/main/java/org/jruby/RubyModule.java | 2 ++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/jruby/RubyClass.java b/core/src/main/java/org/jruby/RubyClass.java index 73cba12b0e9..325a9d43a39 100644 --- a/core/src/main/java/org/jruby/RubyClass.java +++ b/core/src/main/java/org/jruby/RubyClass.java @@ -961,7 +961,27 @@ public IRubyObject initialize_copy(IRubyObject original){ if (original instanceof MetaClass) throw runtime.newTypeError("can't copy singleton class"); super.initialize_copy(original); - allocator = ((RubyClass)original).allocator; + RubyClass originalClazz = ((RubyClass)original); + allocator = originalClazz.allocator; + + + // copy over reify options + javaClassConfiguration = originalClazz.javaClassConfiguration;//TODO: Dup + classAnnotations = originalClazz.classAnnotations; // TODO: dup + fieldSignatures = originalClazz.fieldSignatures; // TODO: dup + methodSignatures = originalClazz.methodSignatures; // TODO: dup + fieldAnnotations = originalClazz.fieldAnnotations; // TODO: dup + methodAnnotations = originalClazz.methodAnnotations; // TODO: dup + parameterAnnotations = originalClazz.parameterAnnotations; // TODO: dup + + // copy over reified class if applicable + + if (originalClazz.getJavaProxy() && originalClazz.reifiedClass != null && !Reified.class.isAssignableFrom(originalClazz.reifiedClass)) // TODO: ensure this line is correct + { + reifiedClass = originalClazz.reifiedClass; + reifiedClassJava = originalClazz.reifiedClassJava; + } + return this; } @@ -2293,6 +2313,7 @@ private void logReifyException(final Throwable failure, final boolean error) { } } + //TODO: check for java? public void setReifiedClass(Class reifiedClass) { this.reifiedClass = (Class) reifiedClass; //Not always true } diff --git a/core/src/main/java/org/jruby/RubyModule.java b/core/src/main/java/org/jruby/RubyModule.java index 6e0c609fefe..5f95b94f5a6 100644 --- a/core/src/main/java/org/jruby/RubyModule.java +++ b/core/src/main/java/org/jruby/RubyModule.java @@ -2458,6 +2458,8 @@ public IRubyObject initialize_copy(IRubyObject original) { syncConstants(originalModule); originalModule.cloneMethods(this); + + this.javaProxy = originalModule.javaProxy; return this; } From 08ae7dd71b3b144493ea304736a6b9505653d9d6 Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Mon, 30 Nov 2020 09:44:50 -0600 Subject: [PATCH 23/49] Add WIP unconnected interpreter which will allow stopping and continuing execution. The goal will be to split execution before and after a super instruction. The arguments to the super call will be retrieve'able which from Java can end up calling a java ctor. This also adds a new flag uses_super which just makes it simple for us to know the initialize method contains a super. The intent is initialize methods in Java proxy classes without super will just not use this special interpreter. --- core/src/main/java/org/jruby/ir/IRScope.java | 9 ++ .../ir/instructions/ClassSuperInstr.java | 1 + .../ir/instructions/InstanceSuperInstr.java | 10 ++ .../ir/instructions/UnresolvedSuperInstr.java | 1 + .../jruby/ir/instructions/ZSuperInstr.java | 1 + .../ExitableInterpreterEngine.java | 147 ++++++++++++++++++ .../ExitableInterpreterEngineContext.java | 48 ++++++ 7 files changed, 217 insertions(+) create mode 100644 core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngine.java create mode 100644 core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineContext.java diff --git a/core/src/main/java/org/jruby/ir/IRScope.java b/core/src/main/java/org/jruby/ir/IRScope.java index 33185c994ec..e84c6dcb301 100644 --- a/core/src/main/java/org/jruby/ir/IRScope.java +++ b/core/src/main/java/org/jruby/ir/IRScope.java @@ -132,6 +132,7 @@ public abstract class IRScope implements ParseResult { private boolean canCaptureCallersBinding; private boolean canReceiveBreaks; // may receive a break during execution (from itself of child scope). private boolean canReceiveNonLocalReturns; + private boolean usesSuper; private boolean usesZSuper; private boolean needsCodeCoverage; private boolean usesEval; @@ -493,6 +494,14 @@ public void setUsesZSuper() { usesZSuper = true; } + public void setUsesSuper() { + usesSuper = true; + } + + public boolean usesSuper() { + return usesSuper; + } + public boolean usesZSuper() { return usesZSuper; } diff --git a/core/src/main/java/org/jruby/ir/instructions/ClassSuperInstr.java b/core/src/main/java/org/jruby/ir/instructions/ClassSuperInstr.java index b318f1bf13e..47cd33fd44f 100644 --- a/core/src/main/java/org/jruby/ir/instructions/ClassSuperInstr.java +++ b/core/src/main/java/org/jruby/ir/instructions/ClassSuperInstr.java @@ -44,6 +44,7 @@ public Operand getDefiningModule() { @Override public boolean computeScopeFlags(IRScope scope, EnumSet flags) { super.computeScopeFlags(scope, flags); + scope.setUsesSuper(); flags.add(IRFlags.REQUIRES_CLASS); // for current class and method name flags.add(IRFlags.REQUIRES_METHODNAME); // for current class and method name return true; diff --git a/core/src/main/java/org/jruby/ir/instructions/InstanceSuperInstr.java b/core/src/main/java/org/jruby/ir/instructions/InstanceSuperInstr.java index 0cbf8632a9c..f607005b7df 100644 --- a/core/src/main/java/org/jruby/ir/instructions/InstanceSuperInstr.java +++ b/core/src/main/java/org/jruby/ir/instructions/InstanceSuperInstr.java @@ -3,6 +3,7 @@ import org.jruby.RubyInstanceConfig; import org.jruby.RubyModule; import org.jruby.RubySymbol; +import org.jruby.ir.IRFlags; import org.jruby.ir.IRScope; import org.jruby.ir.IRVisitor; import org.jruby.ir.Operation; @@ -20,6 +21,8 @@ import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; +import java.util.EnumSet; + public class InstanceSuperInstr extends CallInstr { private final boolean isLiteralBlock; @@ -44,6 +47,13 @@ public Operand getDefiningModule() { return getReceiver(); } + @Override + public boolean computeScopeFlags(IRScope scope, EnumSet flags) { + scope.setUsesSuper(); + + return true; + } + @Override public Instr clone(CloneInfo ii) { return new InstanceSuperInstr(ii.getScope(), ii.getRenamedVariable(getResult()), diff --git a/core/src/main/java/org/jruby/ir/instructions/UnresolvedSuperInstr.java b/core/src/main/java/org/jruby/ir/instructions/UnresolvedSuperInstr.java index c008cbf277b..bf3fe120ba1 100644 --- a/core/src/main/java/org/jruby/ir/instructions/UnresolvedSuperInstr.java +++ b/core/src/main/java/org/jruby/ir/instructions/UnresolvedSuperInstr.java @@ -59,6 +59,7 @@ public UnresolvedSuperInstr(IRScope scope, Variable result, Operand receiver, Op @Override public boolean computeScopeFlags(IRScope scope, EnumSet flags) { super.computeScopeFlags(scope, flags); + scope.setUsesSuper(); flags.add(IRFlags.REQUIRES_CLASS); // for current class and method name flags.add(IRFlags.REQUIRES_METHODNAME); // for current class and method name return true; diff --git a/core/src/main/java/org/jruby/ir/instructions/ZSuperInstr.java b/core/src/main/java/org/jruby/ir/instructions/ZSuperInstr.java index 579aeee624c..1a8870fe62d 100644 --- a/core/src/main/java/org/jruby/ir/instructions/ZSuperInstr.java +++ b/core/src/main/java/org/jruby/ir/instructions/ZSuperInstr.java @@ -34,6 +34,7 @@ public ZSuperInstr(IRScope scope, Variable result, Operand receiver, Operand[] a @Override public boolean computeScopeFlags(IRScope scope, EnumSet flags) { super.computeScopeFlags(scope, flags); + scope.setUsesSuper(); scope.setUsesZSuper(); return true; } diff --git a/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngine.java b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngine.java new file mode 100644 index 00000000000..0e34a61c68f --- /dev/null +++ b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngine.java @@ -0,0 +1,147 @@ +package org.jruby.ir.interpreter; + +import org.jruby.RubyModule; +import org.jruby.ir.Operation; +import org.jruby.ir.instructions.Instr; +import org.jruby.ir.instructions.JumpInstr; +import org.jruby.ir.instructions.boxing.AluInstr; +import org.jruby.ir.runtime.IRRuntimeHelpers; +import org.jruby.parser.StaticScope; +import org.jruby.runtime.Block; +import org.jruby.runtime.DynamicScope; +import org.jruby.runtime.Helpers; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +/** + * InterpreterEngine capable of exiting part way through execution up to a particular instruction. Continued + * execution will assign the result of that instruction which we pass in and continue executing from that point. + * The mechanism for this interpreter to work is a context object which contains the IPC, the result, and the + * temporary variables. + * + * Extra Notes: + * - We may need to make all natural split arities here if a JIT calls this directly. + * - I dislike replicating this code but we do not want our default interpreter to have an extra stack frame. + */ +public class ExitableInterpreterEngine extends InterpreterEngine { + public IRubyObject interpret(ThreadContext context, Block block, IRubyObject self, + InterpreterContext interpreterContext, RubyModule implClass, String name, + IRubyObject[] args, Block blockArg, ExitableInterpreterEngineContext executionContext) { + Instr[] instrs = interpreterContext.getInstructions(); + Object[] temp = executionContext.getTemporaryVariables(interpreterContext); + double[] floats = executionContext.getTemporaryFloatVariables(interpreterContext); + long[] fixnums = executionContext.getTemporaryFixnumVariables(interpreterContext); + boolean[] booleans = executionContext.getTemporaryBooleanVariables(interpreterContext); + int n = instrs.length; + int ipc = executionContext.getIPC(); + Object exception = null; + boolean acceptsKeywordArgument = interpreterContext.receivesKeywordArguments(); + + if (acceptsKeywordArgument) { + args = IRRuntimeHelpers.frobnicateKwargsArgument(context, args, interpreterContext.getRequiredArgsCount()); + } + + StaticScope currScope = interpreterContext.getStaticScope(); + DynamicScope currDynScope = context.getCurrentScope(); + + // Init profiling this scope + boolean debug = IRRuntimeHelpers.isDebug(); + boolean profile = IRRuntimeHelpers.inProfileMode(); + Integer scopeVersion = profile ? Profiler.initProfiling(interpreterContext.getScope()) : 0; + + // Enter the looooop! + while (ipc < n) { + Instr instr = instrs[ipc]; + + Operation operation = instr.getOperation(); + if (debug) { + Interpreter.LOG.info("I: {" + ipc + "} " + instr); + Interpreter.interpInstrsCount++; + } else if (profile) { + Profiler.instrTick(operation); + Interpreter.interpInstrsCount++; + } + + ipc++; + + try { + switch (operation.opClass) { + case INT_OP: + interpretIntOp((AluInstr) instr, operation, fixnums, booleans); + break; + case FLOAT_OP: + interpretFloatOp((AluInstr) instr, operation, floats, booleans); + break; + case ARG_OP: + receiveArg(context, instr, operation, args, acceptsKeywordArgument, currDynScope, temp, exception, blockArg); + break; + case CALL_OP: + if (profile) Profiler.updateCallSite(instr, interpreterContext.getScope(), scopeVersion); + processCall(context, instr, operation, currDynScope, currScope, temp, self); + break; + case RET_OP: + return processReturnOp(context, block, instr, operation, currDynScope, temp, self, currScope); + case BRANCH_OP: + switch (operation) { + case JUMP: ipc = ((JumpInstr)instr).getJumpTarget().getTargetPC(); break; + default: ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc); break; + } + break; + case BOOK_KEEPING_OP: + // IMPORTANT: Preserve these update to currDynScope, self, and args. + // They affect execution of all following instructions in this scope. + switch (operation) { + case PUSH_METHOD_BINDING: + currDynScope = interpreterContext.newDynamicScope(context); + context.pushScope(currDynScope); + break; + case PUSH_BLOCK_BINDING: + currDynScope = IRRuntimeHelpers.pushBlockDynamicScopeIfNeeded(context, block, interpreterContext.pushNewDynScope(), interpreterContext.reuseParentDynScope()); + break; + case UPDATE_BLOCK_STATE: + self = IRRuntimeHelpers.updateBlockState(block, self); + break; + case PREPARE_NO_BLOCK_ARGS: + args = IRRuntimeHelpers.prepareNoBlockArgs(context, block, args); + break; + case PREPARE_SINGLE_BLOCK_ARG: + args = IRRuntimeHelpers.prepareSingleBlockArgs(context, block, args); + break; + case PREPARE_FIXED_BLOCK_ARGS: + args = IRRuntimeHelpers.prepareFixedBlockArgs(context, block, args); + break; + case PREPARE_BLOCK_ARGS: + args = IRRuntimeHelpers.prepareBlockArgs(context, block, args, acceptsKeywordArgument); + break; + default: + processBookKeepingOp(context, block, instr, operation, name, args, self, blockArg, implClass, currDynScope, temp, currScope); + break; + } + break; + case OTHER_OP: + processOtherOp(context, block, instr, operation, currDynScope, currScope, temp, self, floats, fixnums, booleans); + break; + } + } catch (Throwable t) { + if (debug) extractToMethodToAvoidC2Crash(instr, t); + + // StartupInterpreterEngine never calls this method so we know it is a full build. + ipc = ((FullInterpreterContext) interpreterContext).determineRPC(ipc); + + if (debug) { + Interpreter.LOG.info("in : " + interpreterContext.getScope() + ", caught Java throwable: " + t + "; excepting instr: " + instr); + Interpreter.LOG.info("ipc for rescuer: " + ipc); + } + + if (ipc == -1) { + Helpers.throwException(t); + } else { + exception = t; + } + } + } + + // Control should never get here! + throw context.runtime.newRuntimeError("BUG: interpreter fell through to end unexpectedly"); + } +} diff --git a/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineContext.java b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineContext.java new file mode 100644 index 00000000000..c316d21cdf0 --- /dev/null +++ b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineContext.java @@ -0,0 +1,48 @@ +package org.jruby.ir.interpreter; + +public class ExitableInterpreterEngineContext { + private int ipc = 0; + private Object[] temporaryVariables = null; + + // Normally we do not use specialized temp variables and when acquiring them they return null. + boolean specializedTemporaryVariablesInitialized = false; + double[] floats = null; + long[] fixnums = null; + boolean[] booleans = null; + + public Object[] getTemporaryVariables(InterpreterContext interpreterContext) { + if (temporaryVariables == null) { + temporaryVariables = interpreterContext.allocateTemporaryVariables(); + } + + return temporaryVariables; + } + + public double[] getTemporaryFloatVariables(InterpreterContext interpreterContext) { + if (specializedTemporaryVariablesInitialized) { + floats = interpreterContext.allocateTemporaryFloatVariables(); + } + + return floats; + } + + public long[] getTemporaryFixnumVariables(InterpreterContext interpreterContext) { + if (specializedTemporaryVariablesInitialized) { + fixnums = interpreterContext.allocateTemporaryFixnumVariables(); + } + + return fixnums; + } + + public boolean[] getTemporaryBooleanVariables(InterpreterContext interpreterContext) { + if (specializedTemporaryVariablesInitialized) { + booleans = interpreterContext.allocateTemporaryBooleanVariables(); + } + + return booleans; + } + + public int getIPC() { + return ipc; + } +} From 13229777b18150608af9af144a2f6d6bea989713 Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Mon, 30 Nov 2020 16:28:49 -0600 Subject: [PATCH 24/49] Filled out to the point I believe RubyClass can ask for the interpreter context via IRMethod.builtInterperterContextForJavaConstructor(). If this is and ExitableInterpreterEngine then we can split execute the method. At that point we need to ask for ic.getEngineState() and then call ExitableInterpreterEngine.interpret(). That method will execute up to the super instr and return a RubyArray of the arguments to the super call. At this point we should convert those values for Java CTOR and execute it. Once finished we will then just call interpret() again. The interpreter will then continue executing at one instr past the original super instr. So the above description still needs to exist in RubyClass vs the AST stuff it is doing now. There is another small problem that this is a startup interpreter of sorts and this method will still try and promote to being a FULL/JIT method. We need to either disable that or possibly automatically convert this to be a full interpreter (but still disable JIT compilation). --- core/src/main/java/org/jruby/ir/IRMethod.java | 46 ++++++ .../ExitableInterpreterContext.java | 46 ++++++ .../ExitableInterpreterEngine.java | 139 ++++++++++++------ .../ExitableInterpreterEngineContext.java | 48 ------ .../ExitableInterpreterEngineState.java | 30 ++++ 5 files changed, 213 insertions(+), 96 deletions(-) create mode 100644 core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterContext.java delete mode 100644 core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineContext.java create mode 100644 core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineState.java diff --git a/core/src/main/java/org/jruby/ir/IRMethod.java b/core/src/main/java/org/jruby/ir/IRMethod.java index ad7d2beecc7..85aa03008ec 100644 --- a/core/src/main/java/org/jruby/ir/IRMethod.java +++ b/core/src/main/java/org/jruby/ir/IRMethod.java @@ -6,14 +6,17 @@ import org.jruby.ast.InstVarNode; import org.jruby.ast.Node; import org.jruby.ast.visitor.AbstractNodeVisitor; +import org.jruby.ir.instructions.CallBase; import org.jruby.ir.instructions.GetFieldInstr; import org.jruby.ir.instructions.Instr; import org.jruby.ir.instructions.PutFieldInstr; +import org.jruby.ir.interpreter.ExitableInterpreterContext; import org.jruby.ir.interpreter.InterpreterContext; import org.jruby.ir.operands.LocalVariable; import org.jruby.ir.representations.BasicBlock; import org.jruby.parser.StaticScope; import org.jruby.runtime.ArgumentDescriptor; +import org.jruby.runtime.CallType; import org.jruby.runtime.ivars.MethodData; import org.jruby.util.ByteList; @@ -97,6 +100,49 @@ public InterpreterContext builtInterpreterContext() { return lazilyAcquireInterpreterContext(); } + /** + * initialize methods in reified Java types will try and dispatch to the Java base classes + * constructor when the Ruby in the initialize: + * + * a) The super call is still valid in terms of Java (e.g. you cannot access self/this before the super call + * b) We can detect the validity of 'a'. Limitations like super in all paths of branches is not supported (for now). + * + * In cases where no super exists or it is unsupported we will return a normal interpreter (and a warning when + * unsupported): + * + * @return appropriate interpretercontext + */ + public synchronized InterpreterContext builtInterperterContextForJavaConstructor() { + InterpreterContext interpreterContext = builtInterpreterContext(); + + if (usesSuper()) { // We know at least one super is in here somewhere + int ipc = 0; + int superIPC = -1; + CallBase superCall = null; + boolean badJump = false; + boolean badBranch = false; + + for(Instr instr: interpreterContext.getInstructions()) { + if (instr instanceof CallBase && ((CallBase) instr).getCallType() == CallType.SUPER) { + // We have already found one super call already. No analysis yet to figure out if this is + // still ok or not so we will error. + if (superCall != null) throw getManager().getRuntime().newRuntimeError("Found multiple supers in java ctor"); + + superIPC = ipc; + } + // FIXME: Add jump/branch error condition code. + + ipc++; + } + + if (superIPC != -1) { + return new ExitableInterpreterContext(interpreterContext, superCall, superIPC); + } + } + + return interpreterContext; + } + final InterpreterContext lazilyAcquireInterpreterContext() { if (!hasBeenBuilt()) buildMethodImpl(); diff --git a/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterContext.java b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterContext.java new file mode 100644 index 00000000000..8dda3e3108b --- /dev/null +++ b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterContext.java @@ -0,0 +1,46 @@ +package org.jruby.ir.interpreter; + +import org.jruby.ir.instructions.CallBase; +import org.jruby.ir.operands.Operand; +import org.jruby.parser.StaticScope; +import org.jruby.runtime.DynamicScope; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; + +import java.util.Arrays; + +public class ExitableInterpreterContext extends InterpreterContext { + private CallBase superCall; + private int exitIPC; + + public ExitableInterpreterContext(InterpreterContext originalIC, CallBase superCall, int exitIPC) { + super(originalIC.getScope(), Arrays.asList(originalIC.getInstructions()), + originalIC.getTemporaryVariableCount(), originalIC.getFlags()); + + this.superCall = superCall; + this.exitIPC = exitIPC; + } + + public ExitableInterpreterEngineState getEngineState() { + return new ExitableInterpreterEngineState(this); + } + + public int getExitIPC() { + return exitIPC; + } + + /** + * @returns the live ruby values for the operand to the original super call. + */ + public IRubyObject[] getArgs(ThreadContext context, IRubyObject self, StaticScope currScope, DynamicScope currDynScope, Object[] temps) { + Operand[] args = superCall.getCallArgs(); + int length = args.length; + IRubyObject[] values = new IRubyObject[length]; + + for(int i = 0; i < length; i++) { + values[i] = (IRubyObject) args[i].retrieve(context, self, currScope, currDynScope, temps); + } + + return values; + } +} diff --git a/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngine.java b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngine.java index 0e34a61c68f..52d9958b67b 100644 --- a/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngine.java +++ b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngine.java @@ -1,10 +1,14 @@ package org.jruby.ir.interpreter; import org.jruby.RubyModule; +import org.jruby.common.IRubyWarnings; import org.jruby.ir.Operation; +import org.jruby.ir.instructions.CheckForLJEInstr; +import org.jruby.ir.instructions.CopyInstr; +import org.jruby.ir.instructions.GetFieldInstr; import org.jruby.ir.instructions.Instr; import org.jruby.ir.instructions.JumpInstr; -import org.jruby.ir.instructions.boxing.AluInstr; +import org.jruby.ir.instructions.RuntimeHelperCall; import org.jruby.ir.runtime.IRRuntimeHelpers; import org.jruby.parser.StaticScope; import org.jruby.runtime.Block; @@ -12,6 +16,10 @@ import org.jruby.runtime.Helpers; import org.jruby.runtime.ThreadContext; import org.jruby.runtime.builtin.IRubyObject; +import org.jruby.runtime.ivars.VariableAccessor; + +import static org.jruby.util.RubyStringBuilder.ids; +import static org.jruby.util.RubyStringBuilder.str; /** * InterpreterEngine capable of exiting part way through execution up to a particular instruction. Continued @@ -25,25 +33,23 @@ */ public class ExitableInterpreterEngine extends InterpreterEngine { public IRubyObject interpret(ThreadContext context, Block block, IRubyObject self, - InterpreterContext interpreterContext, RubyModule implClass, String name, - IRubyObject[] args, Block blockArg, ExitableInterpreterEngineContext executionContext) { + ExitableInterpreterContext interpreterContext, ExitableInterpreterEngineState state, + RubyModule implClass, String name, IRubyObject[] args, Block blockArg) { Instr[] instrs = interpreterContext.getInstructions(); - Object[] temp = executionContext.getTemporaryVariables(interpreterContext); - double[] floats = executionContext.getTemporaryFloatVariables(interpreterContext); - long[] fixnums = executionContext.getTemporaryFixnumVariables(interpreterContext); - boolean[] booleans = executionContext.getTemporaryBooleanVariables(interpreterContext); + Object[] temp = state.getTemporaryVariables(); int n = instrs.length; - int ipc = executionContext.getIPC(); + int ipc = state.getIPC(); + int exitIPC = interpreterContext.getExitIPC(); Object exception = null; - boolean acceptsKeywordArgument = interpreterContext.receivesKeywordArguments(); - if (acceptsKeywordArgument) { - args = IRRuntimeHelpers.frobnicateKwargsArgument(context, args, interpreterContext.getRequiredArgsCount()); - } + boolean acceptsKeywordArgument = interpreterContext.receivesKeywordArguments(); + if (acceptsKeywordArgument) args = IRRuntimeHelpers.frobnicateKwargsArgument(context, args, interpreterContext.getRequiredArgsCount()); StaticScope currScope = interpreterContext.getStaticScope(); DynamicScope currDynScope = context.getCurrentScope(); + int[] rescuePCs = interpreterContext.getRescueIPCs(); + // Init profiling this scope boolean debug = IRRuntimeHelpers.isDebug(); boolean profile = IRRuntimeHelpers.inProfileMode(); @@ -51,27 +57,29 @@ public IRubyObject interpret(ThreadContext context, Block block, IRubyObject sel // Enter the looooop! while (ipc < n) { + // We want to exit at this instr and return its call arguments to consumer of this interpreter. + if (ipc == exitIPC) { + // FIXME: I assume result of super in this case will be nil which means we should not have to explicitly + // set the temp to nil but we shall see... + state.setIPC(ipc + 1); // Mark next instr to execute when we call execute again using this state. + // FIXME: We are forcing a boxing to a Ruby array we probably do not need but did it anyways so it matched the + // interface of interpreterengine (re-consider this). + return context.runtime.newArray(interpreterContext.getArgs(context, self, currScope, currDynScope, temp)); + } + Instr instr = instrs[ipc]; Operation operation = instr.getOperation(); if (debug) { - Interpreter.LOG.info("I: {" + ipc + "} " + instr); + Interpreter.LOG.info("I: " + ipc + ", R: " + rescuePCs[ipc] + " - " + instr + ">"); Interpreter.interpInstrsCount++; } else if (profile) { Profiler.instrTick(operation); Interpreter.interpInstrsCount++; } - ipc++; - try { switch (operation.opClass) { - case INT_OP: - interpretIntOp((AluInstr) instr, operation, fixnums, booleans); - break; - case FLOAT_OP: - interpretFloatOp((AluInstr) instr, operation, floats, booleans); - break; case ARG_OP: receiveArg(context, instr, operation, args, acceptsKeywordArgument, currDynScope, temp, exception, blockArg); break; @@ -83,50 +91,41 @@ public IRubyObject interpret(ThreadContext context, Block block, IRubyObject sel return processReturnOp(context, block, instr, operation, currDynScope, temp, self, currScope); case BRANCH_OP: switch (operation) { - case JUMP: ipc = ((JumpInstr)instr).getJumpTarget().getTargetPC(); break; - default: ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc); break; + case JUMP: + JumpInstr jump = ((JumpInstr)instr); + ipc = jump.getJumpTarget().getTargetPC(); + break; + default: + ipc = instr.interpretAndGetNewIPC(context, currDynScope, currScope, self, temp, ipc + 1); + break; + } - break; + continue; case BOOK_KEEPING_OP: - // IMPORTANT: Preserve these update to currDynScope, self, and args. - // They affect execution of all following instructions in this scope. switch (operation) { case PUSH_METHOD_BINDING: + // IMPORTANT: Preserve this update of currDynScope. + // This affects execution of all instructions in this scope + // which will now use the updated value of currDynScope. currDynScope = interpreterContext.newDynamicScope(context); context.pushScope(currDynScope); - break; - case PUSH_BLOCK_BINDING: - currDynScope = IRRuntimeHelpers.pushBlockDynamicScopeIfNeeded(context, block, interpreterContext.pushNewDynScope(), interpreterContext.reuseParentDynScope()); - break; - case UPDATE_BLOCK_STATE: - self = IRRuntimeHelpers.updateBlockState(block, self); - break; - case PREPARE_NO_BLOCK_ARGS: - args = IRRuntimeHelpers.prepareNoBlockArgs(context, block, args); - break; - case PREPARE_SINGLE_BLOCK_ARG: - args = IRRuntimeHelpers.prepareSingleBlockArgs(context, block, args); - break; - case PREPARE_FIXED_BLOCK_ARGS: - args = IRRuntimeHelpers.prepareFixedBlockArgs(context, block, args); - break; - case PREPARE_BLOCK_ARGS: - args = IRRuntimeHelpers.prepareBlockArgs(context, block, args, acceptsKeywordArgument); + case EXC_REGION_START: + case EXC_REGION_END: break; default: processBookKeepingOp(context, block, instr, operation, name, args, self, blockArg, implClass, currDynScope, temp, currScope); - break; } break; case OTHER_OP: - processOtherOp(context, block, instr, operation, currDynScope, currScope, temp, self, floats, fixnums, booleans); + processOtherOp(context, block, instr, operation, currDynScope, currScope, temp, self); break; } + + ipc++; } catch (Throwable t) { if (debug) extractToMethodToAvoidC2Crash(instr, t); - // StartupInterpreterEngine never calls this method so we know it is a full build. - ipc = ((FullInterpreterContext) interpreterContext).determineRPC(ipc); + ipc = rescuePCs == null ? -1 : rescuePCs[ipc]; if (debug) { Interpreter.LOG.info("in : " + interpreterContext.getScope() + ", caught Java throwable: " + t + "; excepting instr: " + instr); @@ -144,4 +143,48 @@ public IRubyObject interpret(ThreadContext context, Block block, IRubyObject sel // Control should never get here! throw context.runtime.newRuntimeError("BUG: interpreter fell through to end unexpectedly"); } + + protected static void processOtherOp(ThreadContext context, Block block, Instr instr, Operation operation, DynamicScope currDynScope, + StaticScope currScope, Object[] temp, IRubyObject self) { + switch(operation) { + case RECV_SELF: + break; + case COPY: { + CopyInstr c = (CopyInstr)instr; + setResult(temp, currDynScope, c.getResult(), retrieveOp(c.getSource(), context, self, currDynScope, currScope, temp)); + break; + } + case GET_FIELD: { + GetFieldInstr gfi = (GetFieldInstr)instr; + IRubyObject object = (IRubyObject)gfi.getSource().retrieve(context, self, currScope, currDynScope, temp); + VariableAccessor a = gfi.getAccessor(object); + Object result = a == null ? null : (IRubyObject)a.get(object); + if (result == null) { + if (context.runtime.isVerbose()) { + context.runtime.getWarnings().warning(IRubyWarnings.ID.IVAR_NOT_INITIALIZED, + str(context.runtime, "instance variable ", ids(context.runtime, gfi.getId()), " not initialized")); + } + result = context.nil; + } + setResult(temp, currDynScope, gfi.getResult(), result); + break; + } + case RUNTIME_HELPER: { + RuntimeHelperCall rhc = (RuntimeHelperCall)instr; + setResult(temp, currDynScope, rhc.getResult(), + rhc.callHelper(context, currScope, currDynScope, self, temp, block)); + break; + } + case CHECK_FOR_LJE: + ((CheckForLJEInstr) instr).check(context, currDynScope, block); + break; + case LOAD_FRAME_CLOSURE: + setResult(temp, currDynScope, instr, context.getFrameBlock()); + return; + // ---------- All the rest --------- + default: + setResult(temp, currDynScope, instr, instr.interpret(context, currScope, currDynScope, self, temp)); + break; + } + } } diff --git a/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineContext.java b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineContext.java deleted file mode 100644 index c316d21cdf0..00000000000 --- a/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineContext.java +++ /dev/null @@ -1,48 +0,0 @@ -package org.jruby.ir.interpreter; - -public class ExitableInterpreterEngineContext { - private int ipc = 0; - private Object[] temporaryVariables = null; - - // Normally we do not use specialized temp variables and when acquiring them they return null. - boolean specializedTemporaryVariablesInitialized = false; - double[] floats = null; - long[] fixnums = null; - boolean[] booleans = null; - - public Object[] getTemporaryVariables(InterpreterContext interpreterContext) { - if (temporaryVariables == null) { - temporaryVariables = interpreterContext.allocateTemporaryVariables(); - } - - return temporaryVariables; - } - - public double[] getTemporaryFloatVariables(InterpreterContext interpreterContext) { - if (specializedTemporaryVariablesInitialized) { - floats = interpreterContext.allocateTemporaryFloatVariables(); - } - - return floats; - } - - public long[] getTemporaryFixnumVariables(InterpreterContext interpreterContext) { - if (specializedTemporaryVariablesInitialized) { - fixnums = interpreterContext.allocateTemporaryFixnumVariables(); - } - - return fixnums; - } - - public boolean[] getTemporaryBooleanVariables(InterpreterContext interpreterContext) { - if (specializedTemporaryVariablesInitialized) { - booleans = interpreterContext.allocateTemporaryBooleanVariables(); - } - - return booleans; - } - - public int getIPC() { - return ipc; - } -} diff --git a/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineState.java b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineState.java new file mode 100644 index 00000000000..29bc206f63e --- /dev/null +++ b/core/src/main/java/org/jruby/ir/interpreter/ExitableInterpreterEngineState.java @@ -0,0 +1,30 @@ +package org.jruby.ir.interpreter; + +public class ExitableInterpreterEngineState { + // What IC this is executing. + private ExitableInterpreterContext interpreterContext; + + // The current index of the instruction we are executing. + private int ipc = 0; + private Object[] temporaryVariables = null; + + public ExitableInterpreterEngineState(ExitableInterpreterContext interpreterContext) { + this.interpreterContext = interpreterContext; + } + + public Object[] getTemporaryVariables() { + if (temporaryVariables == null) { + temporaryVariables = interpreterContext.allocateTemporaryVariables(); + } + + return temporaryVariables; + } + + public int getIPC() { + return ipc; + } + + public void setIPC(int ipc) { + this.ipc = ipc; + } +} From 3311fa0a0311695a60b90bde4ece65d43c1a2d01 Mon Sep 17 00:00:00 2001 From: "Thomas E. Enebo" Date: Tue, 1 Dec 2020 14:36:33 -0600 Subject: [PATCH 25/49] Add depth logic to IR super splitting. This looks for jump targets and labels. A label is where we will jump to. jumptargets are places which will move to a label. If we run into a jump target we need to just notice whether the jump will move forward past the super call or jump backwards to before the super call. All the other jumps should be fine. From a purely what could we support this is a bit restrictive. There are cases where we could support all paths of an if/else where both branches are guaranteed to call super. This should be allowed but this analysis requires flow analysis and the current version is too simple to support it. Future work can add this. --- core/src/main/java/org/jruby/ir/IRMethod.java | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/jruby/ir/IRMethod.java b/core/src/main/java/org/jruby/ir/IRMethod.java index 85aa03008ec..22fed70691f 100644 --- a/core/src/main/java/org/jruby/ir/IRMethod.java +++ b/core/src/main/java/org/jruby/ir/IRMethod.java @@ -9,9 +9,12 @@ import org.jruby.ir.instructions.CallBase; import org.jruby.ir.instructions.GetFieldInstr; import org.jruby.ir.instructions.Instr; +import org.jruby.ir.instructions.JumpTargetInstr; +import org.jruby.ir.instructions.LabelInstr; import org.jruby.ir.instructions.PutFieldInstr; import org.jruby.ir.interpreter.ExitableInterpreterContext; import org.jruby.ir.interpreter.InterpreterContext; +import org.jruby.ir.operands.Label; import org.jruby.ir.operands.LocalVariable; import org.jruby.ir.representations.BasicBlock; import org.jruby.parser.StaticScope; @@ -21,7 +24,9 @@ import org.jruby.util.ByteList; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class IRMethod extends IRScope { public final boolean isInstanceMethod; @@ -119,8 +124,8 @@ public synchronized InterpreterContext builtInterperterContextForJavaConstructor int ipc = 0; int superIPC = -1; CallBase superCall = null; - boolean badJump = false; - boolean badBranch = false; + Map labels = new HashMap<>(); + List