--- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java 2016-11-16 15:55:44.000000000 +0100 +++ new/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java 2016-11-16 15:55:43.000000000 +0100 @@ -31,24 +31,13 @@ import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS; -import static jdk.internal.org.objectweb.asm.Opcodes.AALOAD; import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; -import static jdk.internal.org.objectweb.asm.Opcodes.ARRAYLENGTH; import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE; import static jdk.internal.org.objectweb.asm.Opcodes.D2F; -import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.GOTO; import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC; -import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_0; -import static jdk.internal.org.objectweb.asm.Opcodes.IF_ICMPGE; -import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; -import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEVIRTUAL; -import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE; import static jdk.internal.org.objectweb.asm.Opcodes.I2B; import static jdk.internal.org.objectweb.asm.Opcodes.I2S; -import static jdk.internal.org.objectweb.asm.Opcodes.POP; -import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup; import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; @@ -66,6 +55,7 @@ import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; +import java.security.ProtectionDomain; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -73,20 +63,14 @@ import java.util.List; import java.util.Set; import jdk.internal.org.objectweb.asm.ClassWriter; -import jdk.internal.org.objectweb.asm.FieldVisitor; -import jdk.internal.org.objectweb.asm.Handle; -import jdk.internal.org.objectweb.asm.Label; -import jdk.internal.org.objectweb.asm.MethodVisitor; -import jdk.internal.org.objectweb.asm.Opcodes; -import jdk.internal.org.objectweb.asm.Type; import jdk.internal.org.objectweb.asm.Handle; import jdk.internal.org.objectweb.asm.Label; import jdk.internal.org.objectweb.asm.Opcodes; import jdk.internal.org.objectweb.asm.Type; import jdk.internal.org.objectweb.asm.commons.InstructionAdapter; +import jdk.nashorn.api.scripting.ScriptObjectMirror; import jdk.nashorn.api.scripting.ScriptUtils; import jdk.nashorn.internal.codegen.CompilerConstants.Call; -import jdk.nashorn.internal.runtime.Context; import jdk.nashorn.internal.runtime.ScriptFunction; import jdk.nashorn.internal.runtime.ScriptObject; import jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome; @@ -139,7 +123,8 @@ * to resemble Java anonymous classes) is actually equivalent to new X(a, b, { ... }). *

* It is possible to create two different adapter classes: those that can have class-level overrides, and those that can - * have instance-level overrides. When {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked + * have instance-level overrides. When {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject, ProtectionDomain)} + * or {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject, Lookup)} is invoked * with non-null {@code classOverrides} parameter, an adapter class is created that can have class-level overrides, and * the passed script object will be used as the implementations for its methods, just as in the above case of the * constructor taking a script object. Note that in the case of class-level overrides, a new adapter class is created on @@ -168,6 +153,7 @@ private static final Type OBJECT_TYPE = Type.getType(Object.class); private static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class); private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class); + private static final Type SCRIPT_OBJECT_MIRROR_TYPE = Type.getType(ScriptObjectMirror.class); // JavaAdapterServices methods used in generated bytecode private static final Call CHECK_FUNCTION = lookupServiceMethod("checkFunction", ScriptFunction.class, Object.class, String.class); @@ -182,6 +168,7 @@ private static final Call TO_CHAR_PRIMITIVE = lookupServiceMethod("toCharPrimitive", char.class, Object.class); private static final Call UNSUPPORTED = lookupServiceMethod("unsupported", UnsupportedOperationException.class); private static final Call WRAP_THROWABLE = lookupServiceMethod("wrapThrowable", RuntimeException.class, Throwable.class); + private static final Call UNWRAP_MIRROR = lookupServiceMethod("unwrapMirror", ScriptObject.class, Object.class, boolean.class); // Other methods invoked by the generated bytecode private static final Call UNWRAP = staticCallNoLookup(ScriptUtils.class, "unwrap", Object.class, Object.class); @@ -216,8 +203,7 @@ private static final String GET_METHOD_PROPERTY_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, SCRIPT_OBJECT_TYPE); private static final String VOID_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE); - static final String ADAPTER_PACKAGE_INTERNAL = "jdk/nashorn/javaadapters/"; - static final String ADAPTER_PACKAGE = "jdk.nashorn.javaadapters"; + private static final String ADAPTER_PACKAGE_INTERNAL = "jdk/nashorn/javaadapters/"; private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 255; // Method name prefix for invoking super-methods @@ -265,7 +251,7 @@ * @throws AdaptationException if the adapter can not be generated for some reason. */ JavaAdapterBytecodeGenerator(final Class superClass, final List> interfaces, - final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException { + final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException { assert superClass != null && !superClass.isInterface(); assert interfaces != null; @@ -369,7 +355,7 @@ // If the class is a SAM, allow having ScriptFunction passed as class overrides mv.dup(); mv.instanceOf(SCRIPT_FUNCTION_TYPE); - mv.dup(); + mv.dup(); mv.putstatic(generatedClassName, IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR); final Label notFunction = new Label(); mv.ifeq(notFunction); @@ -389,9 +375,9 @@ private void emitInitCallThis(final InstructionAdapter mv) { loadField(mv, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); GET_CALL_THIS.invoke(mv); - if(classOverride) { + if(classOverride) { mv.putstatic(generatedClassName, CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR); - } else { + } else { // It is presumed ALOAD 0 was already executed mv.putfield(generatedClassName, CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR); } @@ -427,10 +413,10 @@ if (samName == null) { return false; - } - // If all our abstract methods have a single name, generate an additional constructor, one that takes a - // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods. - generateOverridingConstructor(ctor, true); + } + // If all our abstract methods have a single name, generate an additional constructor, one that takes a + // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods. + generateOverridingConstructor(ctor, true); // If the original type only has a single abstract method name, as well as a default ctor, then it can // be automatically converted from JS function. return ctor.getParameterTypes().length == 0; @@ -456,14 +442,9 @@ * constructor passed as the argument here, and delegate to it. However, it will take an additional argument of * either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize * all the method handle fields of the adapter instance with functions from the script object (or the script - * function itself, if that's what's passed). There is one method handle field in the adapter class for every method - * that can be implemented or overridden; the name of every field is same as the name of the method, with a number - * suffix that makes it unique in case of overloaded methods. The generated constructor will invoke - * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType, - * boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity - * adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}. - * The constructor that takes a script function will only initialize the methods with the same name as the single - * abstract method. The constructor will also store the Nashorn global that was current at the constructor + * function itself, if that's what's passed). Additionally, it will create another constructor with an additional + * Object type parameter that can be used for ScriptObjectMirror objects. + * The constructor will also store the Nashorn global that was current at the constructor * invocation time in a field named "global". The generated constructor will be public, regardless of whether the * supertype constructor was public or protected. The generated constructor will not be variable arity, even if the * supertype constructor was. @@ -524,14 +505,59 @@ } } - // Object additional param accepting constructor - generated to handle null and undefined value - // for script adapters. This is effectively to throw TypeError on such script adapters. See - // JavaAdapterServices.getHandle as well. + // Object additional param accepting constructor for handling ScriptObjectMirror objects, which are + // unwrapped to work as ScriptObjects or ScriptFunctions. This also handles null and undefined values for + // script adapters by throwing TypeError on such script adapters. private void generateOverridingConstructorWithObjectParam(final InstructionAdapter mv, final String ctorDescriptor) { mv.visitCode(); final int extraArgOffset = emitSuperConstructorCall(mv, ctorDescriptor); + + // Check for ScriptObjectMirror + mv.visitVarInsn(ALOAD, extraArgOffset); + mv.instanceOf(SCRIPT_OBJECT_MIRROR_TYPE); + final Label notMirror = new Label(); + mv.ifeq(notMirror); + + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, extraArgOffset); + mv.iconst(0); + UNWRAP_MIRROR.invoke(mv); + mv.putfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); + + mv.visitVarInsn(ALOAD, 0); + mv.visitVarInsn(ALOAD, extraArgOffset); + mv.iconst(1); + UNWRAP_MIRROR.invoke(mv); + mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); + + final Label done = new Label(); + + if (samName != null) { + mv.visitVarInsn(ALOAD, 0); + mv.getfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); + mv.instanceOf(SCRIPT_FUNCTION_TYPE); + mv.ifeq(done); + + // Assign "isFunction = true" + mv.visitVarInsn(ALOAD, 0); + mv.iconst(1); + mv.putfield(generatedClassName, IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR); + + mv.visitVarInsn(ALOAD, 0); + mv.dup(); + mv.getfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); + mv.checkcast(SCRIPT_FUNCTION_TYPE); + emitInitCallThis(mv); + mv.goTo(done); + } + + mv.visitLabel(notMirror); + + // Throw error if not a ScriptObject mv.visitVarInsn(ALOAD, extraArgOffset); NOT_AN_OBJECT.invoke(mv); + + mv.visitLabel(done); endInitMethod(mv); } @@ -678,7 +704,7 @@ // stack: [callThis, delegate] mv.goTo(callCallee); mv.visitLabel(notFunction); - } else { + } else { // If it's not a SAM method, and the delegate is a function, // it'll fall back to default behavior mv.ifne(defaultBehavior); @@ -818,7 +844,7 @@ if (isVarArgCall) { // Variable arity calls are always (Object callee, Object this, Object[] params) callParamTypes = new Class[] { Object.class, Object.class, Object[].class }; - } else { + } else { // Adjust invocation type signature for conversions we instituted in // convertParam; also, byte and short get passed as ints. final Class[] origParamTypes = type.parameterArray(); @@ -868,13 +894,13 @@ private void loadField(final InstructionAdapter mv, final String name, final String desc) { - if(classOverride) { + if(classOverride) { mv.getstatic(generatedClassName, name, desc); - } else { - mv.visitVarInsn(ALOAD, 0); + } else { + mv.visitVarInsn(ALOAD, 0); mv.getfield(generatedClassName, name, desc); - } - } + } + } private static void convertReturnValue(final InstructionAdapter mv, final Class origReturnType) { if (origReturnType == void.class) { @@ -948,6 +974,7 @@ } return len; } + /** * Emit code to restore the previous Nashorn Context when needed. * @param mv the instruction adapter @@ -999,9 +1026,9 @@ } for (final Class iface : interfaces) { - if (cl.isAssignableFrom(iface)) { - return iface; - } + if (cl.isAssignableFrom(iface)) { + return iface; + } } // we better that interface that extends the given interface! @@ -1122,8 +1149,8 @@ if (Modifier.isFinal(m) || isCallerSensitive(typeMethod)) { finalMethods.add(mi); } else if (!finalMethods.contains(mi) && methodInfos.add(mi) && Modifier.isAbstract(m)) { - abstractMethodNames.add(mi.getName()); - } + abstractMethodNames.add(mi.getName()); + } } } }