--- old/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/JavaAdapterBytecodeGenerator.java 2020-04-15 18:51:00.000000000 +0530
+++ /dev/null 2020-04-15 18:51:00.000000000 +0530
@@ -1,1237 +0,0 @@
-/*
- * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package jdk.nashorn.internal.runtime.linker;
-
-import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL;
-import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE;
-import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC;
-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.ALOAD;
-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.H_INVOKESTATIC;
-import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL;
-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.RETURN;
-import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup;
-import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup;
-import static jdk.nashorn.internal.lookup.Lookup.MH;
-import static jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR;
-
-import java.lang.invoke.CallSite;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles.Lookup;
-import java.lang.invoke.MethodType;
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-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;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import jdk.internal.org.objectweb.asm.ClassWriter;
-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.ScriptFunction;
-import jdk.nashorn.internal.runtime.ScriptObject;
-import jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome;
-import jdk.internal.reflect.CallerSensitive;
-
-/**
- * Generates bytecode for a Java adapter class. Used by the {@link JavaAdapterFactory}.
- *
- * For every protected or public constructor in the extended class, the adapter class will have either one or two
- * public constructors (visibility of protected constructors in the extended class is promoted to public).
- *
- * For adapter classes with instance-level overrides, a constructor taking a trailing ScriptObject argument preceded
- * by original constructor arguments is always created on the adapter class. When such a constructor is invoked, the
- * passed ScriptObject's member functions are used to implement and/or override methods on the original class,
- * dispatched by name. A single JavaScript function will act as the implementation for all overloaded methods of the
- * same name. When methods on an adapter instance are invoked, the functions are invoked having the ScriptObject passed
- * in the instance constructor as their "this". Subsequent changes to the ScriptObject (reassignment or removal of its
- * functions) will be reflected in the adapter instance as it is live dispatching to its members on every method invocation.
- * {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The
- * only restriction is that since every JavaScript object already has a {@code toString} function through the
- * {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a
- * {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be
- * implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too.
- *
- *
- * If the original types collectively have only one abstract method, or have several of them, but all share the
- * same name, an additional constructor for instance-level override adapter is provided for every original constructor;
- * this one takes a ScriptFunction as its last argument preceded by original constructor arguments. This constructor
- * will use the passed function as the implementation for all abstract methods. For consistency, any concrete methods
- * sharing the single abstract method name will also be overridden by the function. When methods on the adapter instance
- * are invoked, the ScriptFunction is invoked with UNDEFINED or Global as its "this" depending whether the function is
- * strict or not.
- *
- *
- * If the adapter being generated has class-level overrides, constructors taking same arguments as the superclass
- * constructors are created. These constructors simply delegate to the superclass constructor. They are simply used to
- * create instances of the adapter class, with no instance-level overrides, as they don't have them. If the original
- * class' constructor was variable arity, the adapter constructor will also be variable arity. Protected constructors
- * are exposed as public.
- *
- *
- *
- * For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect
- * to coerce the JavaScript function return value to the expected Java return type.
- *
- * Since we are adding a trailing argument to the generated constructors in the adapter class with instance-level overrides, they will never be
- * declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The
- * reason we are passing the additional argument at the end of the argument list instead at the front is that the
- * source-level script expression new X(a, b) { ... }
(which is a proprietary syntax extension Nashorn uses
- * 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, 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
- * every invocation, and the implementation object is bound to the class, not to any instance. All created instances
- * will share these functions. If it is required to have both class-level overrides and instance-level overrides, the
- * class-level override adapter class should be subclassed with an instance-override adapter. Since adapters delegate to
- * super class when an overriding method handle is not specified, this will behave as expected. It is not possible to
- * have both class-level and instance-level overrides in the same class for security reasons: adapter classes are
- * defined with a protection domain of their creator code, and an adapter class that has both class and instance level
- * overrides would need to have two potentially different protection domains: one for class-based behavior and one for
- * instance-based behavior; since Java classes can only belong to a single protection domain, this could not be
- * implemented securely.
- */
-final class JavaAdapterBytecodeGenerator {
- // Field names in adapters
- private static final String GLOBAL_FIELD_NAME = "global";
- private static final String DELEGATE_FIELD_NAME = "delegate";
- private static final String IS_FUNCTION_FIELD_NAME = "isFunction";
- private static final String CALL_THIS_FIELD_NAME = "callThis";
-
- // Initializer names
- private static final String INIT = "";
- private static final String CLASS_INIT = "";
-
- // Types often used in generated bytecode
- 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);
- private static final Call EXPORT_RETURN_VALUE = lookupServiceMethod("exportReturnValue", Object.class, Object.class);
- private static final Call GET_CALL_THIS = lookupServiceMethod("getCallThis", Object.class, ScriptFunction.class, Object.class);
- private static final Call GET_CLASS_OVERRIDES = lookupServiceMethod("getClassOverrides", ScriptObject.class);
- private static final Call GET_NON_NULL_GLOBAL = lookupServiceMethod("getNonNullGlobal", ScriptObject.class);
- private static final Call HAS_OWN_TO_STRING = lookupServiceMethod("hasOwnToString", boolean.class, ScriptObject.class);
- private static final Call INVOKE_NO_PERMISSIONS = lookupServiceMethod("invokeNoPermissions", void.class, MethodHandle.class, Object.class);
- private static final Call NOT_AN_OBJECT = lookupServiceMethod("notAnObject", void.class, Object.class);
- private static final Call SET_GLOBAL = lookupServiceMethod("setGlobal", Runnable.class, ScriptObject.class);
- 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);
- private static final Call CHAR_VALUE_OF = staticCallNoLookup(Character.class, "valueOf", Character.class, char.class);
- private static final Call DOUBLE_VALUE_OF = staticCallNoLookup(Double.class, "valueOf", Double.class, double.class);
- private static final Call LONG_VALUE_OF = staticCallNoLookup(Long.class, "valueOf", Long.class, long.class);
- private static final Call RUN = interfaceCallNoLookup(Runnable.class, "run", void.class);
-
- // ASM handle to the bootstrap method
- private static final Handle BOOTSTRAP_HANDLE = new Handle(H_INVOKESTATIC,
- Type.getInternalName(JavaAdapterServices.class), "bootstrap",
- MethodType.methodType(CallSite.class, Lookup.class, String.class,
- MethodType.class, int.class).toMethodDescriptorString(), false);
-
- // ASM handle to the bootstrap method for array populator
- private static final Handle CREATE_ARRAY_BOOTSTRAP_HANDLE = new Handle(H_INVOKESTATIC,
- Type.getInternalName(JavaAdapterServices.class), "createArrayBootstrap",
- MethodType.methodType(CallSite.class, Lookup.class, String.class,
- MethodType.class).toMethodDescriptorString(), false);
-
- // Field type names used in the generated bytecode
- private static final String SCRIPT_OBJECT_TYPE_DESCRIPTOR = SCRIPT_OBJECT_TYPE.getDescriptor();
- private static final String OBJECT_TYPE_DESCRIPTOR = OBJECT_TYPE.getDescriptor();
- private static final String BOOLEAN_TYPE_DESCRIPTOR = Type.BOOLEAN_TYPE.getDescriptor();
-
- // Throwable names used in the generated bytecode
- private static final String RUNTIME_EXCEPTION_TYPE_NAME = Type.getInternalName(RuntimeException.class);
- private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class);
- private static final String THROWABLE_TYPE_NAME = Type.getInternalName(Throwable.class);
-
- // Some more frequently used method descriptors
- 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);
-
- 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
- static final String SUPER_PREFIX = "super$";
-
- // Method name and type for the no-privilege finalizer delegate
- private static final String FINALIZER_DELEGATE_NAME = "$$nashornFinalizerDelegate";
- private static final String FINALIZER_DELEGATE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE);
-
- /**
- * Collection of methods we never override: Object.clone(), Object.finalize().
- */
- private static final Collection EXCLUDED = getExcludedMethods();
-
- // This is the superclass for our generated adapter.
- private final Class> superClass;
- // Interfaces implemented by our generated adapter.
- private final List> interfaces;
- // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class
- // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the
- // Nashorn classes.
- private final ClassLoader commonLoader;
- // Is this a generator for the version of the class that can have overrides on the class level?
- private final boolean classOverride;
- // Binary name of the superClass
- private final String superClassName;
- // Binary name of the generated class.
- private final String generatedClassName;
- private final Set abstractMethodNames = new HashSet<>();
- private final String samName;
- private final Set finalMethods = new HashSet<>(EXCLUDED);
- private final Set methodInfos = new HashSet<>();
- private final boolean autoConvertibleFromFunction;
- private boolean hasExplicitFinalizer = false;
-
- private final ClassWriter cw;
-
- /**
- * Creates a generator for the bytecode for the adapter for the specified superclass and interfaces.
- * @param superClass the superclass the adapter will extend.
- * @param interfaces the interfaces the adapter will implement.
- * @param commonLoader the class loader that can see all of superClass, interfaces, and Nashorn classes.
- * @param classOverride true to generate the bytecode for the adapter that has class-level overrides, false to
- * generate the bytecode for the adapter that has instance-level overrides.
- * @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 {
- assert superClass != null && !superClass.isInterface();
- assert interfaces != null;
-
- this.superClass = superClass;
- this.interfaces = interfaces;
- this.classOverride = classOverride;
- this.commonLoader = commonLoader;
- cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) {
- @Override
- protected String getCommonSuperClass(final String type1, final String type2) {
- // We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class
- // loader to find the common superclass of two types when needed.
- return JavaAdapterBytecodeGenerator.this.getCommonSuperClass(type1, type2);
- }
- };
- superClassName = Type.getInternalName(superClass);
- generatedClassName = getGeneratedClassName(superClass, interfaces);
-
- cw.visit(Opcodes.V1_8, ACC_PUBLIC | ACC_SUPER, generatedClassName, null, superClassName, getInternalTypeNames(interfaces));
- generateField(GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
- generateField(DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-
- gatherMethods(superClass);
- gatherMethods(interfaces);
- if (abstractMethodNames.size() == 1) {
- samName = abstractMethodNames.iterator().next();
- generateField(CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR);
- generateField(IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR);
- } else {
- samName = null;
- }
- if(classOverride) {
- generateClassInit();
- }
- autoConvertibleFromFunction = generateConstructors();
- generateMethods();
- generateSuperMethods();
- if (hasExplicitFinalizer) {
- generateFinalizerMethods();
- }
- // }
- cw.visitEnd();
- }
-
- private void generateField(final String name, final String fieldDesc) {
- cw.visitField(ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0), name, fieldDesc, null, null).visitEnd();
- }
-
- JavaAdapterClassLoader createAdapterClassLoader() {
- return new JavaAdapterClassLoader(generatedClassName, cw.toByteArray());
- }
-
- boolean isAutoConvertibleFromFunction() {
- return autoConvertibleFromFunction;
- }
-
- private static String getGeneratedClassName(final Class> superType, final List> interfaces) {
- // The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're
- // just implementing interfaces or extending Object), then the first implemented interface or Object.
- final Class> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType;
- final Package pkg = namingType.getPackage();
- final String namingTypeName = Type.getInternalName(namingType);
- final StringBuilder buf = new StringBuilder();
- buf.append(ADAPTER_PACKAGE_INTERNAL).append(namingTypeName.replace('/', '_'));
- final Iterator> it = interfaces.iterator();
- if(superType == Object.class && it.hasNext()) {
- it.next(); // Skip first interface, it was used to primarily name the adapter
- }
- // Append interface names to the adapter name
- while(it.hasNext()) {
- buf.append("$$").append(it.next().getSimpleName());
- }
- return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length()));
- }
-
- /**
- * Given a list of class objects, return an array with their binary names. Used to generate the array of interface
- * names to implement.
- * @param classes the classes
- * @return an array of names
- */
- private static String[] getInternalTypeNames(final List> classes) {
- final int interfaceCount = classes.size();
- final String[] interfaceNames = new String[interfaceCount];
- for(int i = 0; i < interfaceCount; ++i) {
- interfaceNames[i] = Type.getInternalName(classes.get(i));
- }
- return interfaceNames;
- }
-
- private void generateClassInit() {
- final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_STATIC, CLASS_INIT,
- VOID_METHOD_DESCRIPTOR, null, null));
-
- // Assign "global = Context.getGlobal()"
- GET_NON_NULL_GLOBAL.invoke(mv);
- mv.putstatic(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-
- GET_CLASS_OVERRIDES.invoke(mv);
- if(samName != null) {
- // If the class is a SAM, allow having ScriptFunction passed as class overrides
- mv.dup();
- mv.instanceOf(SCRIPT_FUNCTION_TYPE);
- mv.dup();
- mv.putstatic(generatedClassName, IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR);
- final Label notFunction = new Label();
- mv.ifeq(notFunction);
- mv.dup();
- mv.checkcast(SCRIPT_FUNCTION_TYPE);
- emitInitCallThis(mv);
- mv.visitLabel(notFunction);
- }
- mv.putstatic(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-
- endInitMethod(mv);
- }
-
- /**
- * Emit bytecode for initializing the "callThis" field.
- */
- private void emitInitCallThis(final InstructionAdapter mv) {
- loadField(mv, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
- GET_CALL_THIS.invoke(mv);
- if(classOverride) {
- mv.putstatic(generatedClassName, CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR);
- } else {
- // It is presumed ALOAD 0 was already executed
- mv.putfield(generatedClassName, CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR);
- }
- }
-
- private boolean generateConstructors() throws AdaptationException {
- boolean gotCtor = false;
- boolean canBeAutoConverted = false;
- for (final Constructor> ctor: superClass.getDeclaredConstructors()) {
- final int modifier = ctor.getModifiers();
- if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0 && !isCallerSensitive(ctor)) {
- canBeAutoConverted = generateConstructors(ctor) | canBeAutoConverted;
- gotCtor = true;
- }
- }
- if(!gotCtor) {
- throw new AdaptationException(ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName());
- }
- return canBeAutoConverted;
- }
-
- private boolean generateConstructors(final Constructor> ctor) {
- if(classOverride) {
- // Generate a constructor that just delegates to ctor. This is used with class-level overrides, when we want
- // to create instances without further per-instance overrides.
- generateDelegatingConstructor(ctor);
- return false;
- }
-
- // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the
- // beginning of its parameter list.
- generateOverridingConstructor(ctor, false);
-
- 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 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;
- }
-
- private void generateDelegatingConstructor(final Constructor> ctor) {
- final Type originalCtorType = Type.getType(ctor);
- final Type[] argTypes = originalCtorType.getArgumentTypes();
-
- // All constructors must be public, even if in the superclass they were protected.
- final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC |
- (ctor.isVarArgs() ? ACC_VARARGS : 0), INIT,
- Type.getMethodDescriptor(originalCtorType.getReturnType(), argTypes), null, null));
-
- mv.visitCode();
- emitSuperConstructorCall(mv, originalCtorType.getDescriptor());
-
- endInitMethod(mv);
- }
-
- /**
- * Generates a constructor for the instance adapter class. This constructor will take the same arguments as the supertype
- * 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). 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.
- * @param ctor the supertype constructor that is serving as the base for the generated constructor.
- * @param fromFunction true if we're generating a constructor that initializes SAM types from a single
- * ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a
- * ScriptObject passed to it.
- */
- private void generateOverridingConstructor(final Constructor> ctor, final boolean fromFunction) {
- final Type originalCtorType = Type.getType(ctor);
- final Type[] originalArgTypes = originalCtorType.getArgumentTypes();
- final int argLen = originalArgTypes.length;
- final Type[] newArgTypes = new Type[argLen + 1];
-
- // Insert ScriptFunction|ScriptObject as the last argument to the constructor
- final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : SCRIPT_OBJECT_TYPE;
- newArgTypes[argLen] = extraArgumentType;
- System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen);
-
- // All constructors must be public, even if in the superclass they were protected.
- // Existing super constructor (this, args...) triggers generating (this, args..., delegate).
- // Any variable arity constructors become fixed-arity with explicit array arguments.
- final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
- Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null));
-
- mv.visitCode();
- // First, invoke super constructor with original arguments.
- final int extraArgOffset = emitSuperConstructorCall(mv, originalCtorType.getDescriptor());
-
- // Assign "this.global = Context.getGlobal()"
- mv.visitVarInsn(ALOAD, 0);
- GET_NON_NULL_GLOBAL.invoke(mv);
- mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-
- // Assign "this.delegate = delegate"
- mv.visitVarInsn(ALOAD, 0);
- mv.visitVarInsn(ALOAD, extraArgOffset);
- mv.putfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
-
- if (fromFunction) {
- // 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.visitVarInsn(ALOAD, extraArgOffset);
- emitInitCallThis(mv);
- }
-
- endInitMethod(mv);
-
- if (! fromFunction) {
- newArgTypes[argLen] = OBJECT_TYPE;
- final InstructionAdapter mv2 = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT,
- Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null));
- generateOverridingConstructorWithObjectParam(mv2, originalCtorType.getDescriptor());
- }
- }
-
- // 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);
- }
-
- private static void endInitMethod(final InstructionAdapter mv) {
- mv.visitInsn(RETURN);
- endMethod(mv);
- }
-
- private static void endMethod(final InstructionAdapter mv) {
- mv.visitMaxs(0, 0);
- mv.visitEnd();
- }
-
- /**
- * Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the
- * reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the
- * method handle serving as the implementation of this method in adapter instances.
- *
- */
- private static class MethodInfo {
- private final Method method;
- private final MethodType type;
-
- private MethodInfo(final Class> clazz, final String name, final Class>... argTypes) throws NoSuchMethodException {
- this(clazz.getDeclaredMethod(name, argTypes));
- }
-
- private MethodInfo(final Method method) {
- this.method = method;
- this.type = MH.type(method.getReturnType(), method.getParameterTypes());
- }
-
- @Override
- public boolean equals(final Object obj) {
- return obj instanceof MethodInfo && equals((MethodInfo)obj);
- }
-
- private boolean equals(final MethodInfo other) {
- // Only method name and type are used for comparison; method handle field name is not.
- return getName().equals(other.getName()) && type.equals(other.type);
- }
-
- String getName() {
- return method.getName();
- }
-
- @Override
- public int hashCode() {
- return getName().hashCode() ^ type.hashCode();
- }
- }
-
- private void generateMethods() {
- for(final MethodInfo mi: methodInfos) {
- generateMethod(mi);
- }
- }
-
- /**
- * Generates a method in the adapter class that adapts a method from the
- * original class. The generated method will either invoke the delegate
- * using a CALL dynamic operation call site (if it is a SAM method and the
- * delegate is a ScriptFunction), or invoke GET_METHOD_PROPERTY dynamic
- * operation with the method name as the argument and then invoke the
- * returned ScriptFunction using the CALL dynamic operation. If
- * GET_METHOD_PROPERTY returns null or undefined (that is, the JS object
- * doesn't provide an implementation for the method) then the method will
- * either do a super invocation to base class, or if the method is abstract,
- * throw an {@link UnsupportedOperationException}. Finally, if
- * GET_METHOD_PROPERTY returns something other than a ScriptFunction, null,
- * or undefined, a TypeError is thrown. The current Global is checked before
- * the dynamic operations, and if it is different than the Global used to
- * create the adapter, the creating Global is set to be the current Global.
- * In this case, the previously current Global is restored after the
- * invocation. If CALL results in a Throwable that is not one of the
- * method's declared exceptions, and is not an unchecked throwable, then it
- * is wrapped into a {@link RuntimeException} and the runtime exception is
- * thrown.
- * @param mi the method info describing the method to be generated.
- */
- private void generateMethod(final MethodInfo mi) {
- final Method method = mi.method;
- final Class>[] exceptions = method.getExceptionTypes();
- final String[] exceptionNames = getExceptionNames(exceptions);
- final MethodType type = mi.type;
- final String methodDesc = type.toMethodDescriptorString();
- final String name = mi.getName();
-
- final Type asmType = Type.getMethodType(methodDesc);
- final Type[] asmArgTypes = asmType.getArgumentTypes();
-
- final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), name,
- methodDesc, null, exceptionNames));
- mv.visitCode();
-
- final Class> returnType = type.returnType();
- final Type asmReturnType = Type.getType(returnType);
-
- // Determine the first index for a local variable
- int nextLocalVar = 1; // "this" is at 0
- for(final Type t: asmArgTypes) {
- nextLocalVar += t.getSize();
- }
- // Set our local variable index
- final int globalRestoringRunnableVar = nextLocalVar++;
-
- // Load the creatingGlobal object
- loadField(mv, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
- // stack: [creatingGlobal]
- SET_GLOBAL.invoke(mv);
- // stack: [runnable]
- mv.visitVarInsn(ASTORE, globalRestoringRunnableVar);
- // stack: []
-
- final Label tryBlockStart = new Label();
- mv.visitLabel(tryBlockStart);
-
- final Label callCallee = new Label();
- final Label defaultBehavior = new Label();
- // If this is a SAM type...
- if (samName != null) {
- // ...every method will be checking whether we're initialized with a
- // function.
- loadField(mv, IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR);
- // stack: [isFunction]
- if (name.equals(samName)) {
- final Label notFunction = new Label();
- mv.ifeq(notFunction);
- // stack: []
- // If it's a SAM method, it'll load delegate as the "callee" and
- // "callThis" as "this" for the call if delegate is a function.
- loadField(mv, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
- // NOTE: if we added "mv.checkcast(SCRIPT_FUNCTION_TYPE);" here
- // we could emit the invokedynamic CALL instruction with signature
- // (ScriptFunction, Object, ...) instead of (Object, Object, ...).
- // We could combine this with an optimization in
- // ScriptFunction.findCallMethod where it could link a call with a
- // thinner guard when the call site statically guarantees that the
- // callee argument is a ScriptFunction. Additionally, we could use
- // a "ScriptFunction function" field in generated classes instead
- // of a "boolean isFunction" field to avoid the checkcast.
- loadField(mv, CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR);
- // stack: [callThis, delegate]
- mv.goTo(callCallee);
- mv.visitLabel(notFunction);
- } else {
- // If it's not a SAM method, and the delegate is a function,
- // it'll fall back to default behavior
- mv.ifne(defaultBehavior);
- // stack: []
- }
- }
-
- // At this point, this is either not a SAM method or the delegate is
- // not a ScriptFunction. We need to emit a GET_METHOD_PROPERTY Nashorn
- // invokedynamic.
-
- if(name.equals("toString")) {
- // Since every JS Object has a toString, we only override
- // "String toString()" it if it's explicitly specified on the object.
- loadField(mv, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
- // stack: [delegate]
- HAS_OWN_TO_STRING.invoke(mv);
- // stack: [hasOwnToString]
- mv.ifeq(defaultBehavior);
- }
-
- loadField(mv, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
- //For the cases like scripted overridden methods invoked from super constructors get adapter global/delegate fields as null, since we
- //cannot set these fields before invoking super constructor better solution is opt out of scripted overridden method if global/delegate fields
- //are null and invoke super method instead
- mv.ifnull(defaultBehavior);
- loadField(mv, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR);
- mv.dup();
- // stack: [delegate, delegate]
- final String encodedName = NameCodec.encode(name);
- mv.visitInvokeDynamicInsn(encodedName,
- GET_METHOD_PROPERTY_METHOD_DESCRIPTOR, BOOTSTRAP_HANDLE,
- NashornCallSiteDescriptor.GET_METHOD_PROPERTY);
- // stack: [callee, delegate]
- mv.visitLdcInsn(name);
- // stack: [name, callee, delegate]
- CHECK_FUNCTION.invoke(mv);
- // stack: [fnCalleeOrNull, delegate]
- final Label hasFunction = new Label();
- mv.dup();
- // stack: [fnCalleeOrNull, fnCalleeOrNull, delegate]
- mv.ifnonnull(hasFunction);
- // stack: [null, delegate]
- // If it's null or undefined, clear stack and fall back to default
- // behavior.
- mv.pop2();
- // stack: []
-
- // We can also arrive here from check for "delegate instanceof ScriptFunction"
- // in a non-SAM method as well as from a check for "hasOwnToString(delegate)"
- // for a toString delegate.
- mv.visitLabel(defaultBehavior);
- final Runnable emitFinally = ()->emitFinally(mv, globalRestoringRunnableVar);
- final Label normalFinally = new Label();
- if(Modifier.isAbstract(method.getModifiers())) {
- // If the super method is abstract, throw UnsupportedOperationException
- UNSUPPORTED.invoke(mv);
- // NOTE: no need to invoke emitFinally.run() as we're inside the
- // tryBlockStart/tryBlockEnd range, so throwing this exception will
- // transfer control to the rethrow handler and the finally block in it
- // will execute.
- mv.athrow();
- } else {
- // If the super method is not abstract, delegate to it.
- emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc);
- mv.goTo(normalFinally);
- }
-
- mv.visitLabel(hasFunction);
- // stack: [callee, delegate]
- mv.swap();
- // stack [delegate, callee]
- mv.visitLabel(callCallee);
-
-
- // Load all parameters back on stack for dynamic invocation.
-
- int varOffset = 1;
- // If the param list length is more than 253 slots, we can't invoke it
- // directly as with (callee, this) it'll exceed 255.
- final boolean isVarArgCall = getParamListLengthInSlots(asmArgTypes) > 253;
- for (final Type t : asmArgTypes) {
- mv.load(varOffset, t);
- convertParam(mv, t, isVarArgCall);
- varOffset += t.getSize();
- }
- // stack: [args..., callee, delegate]
-
- // If the resulting parameter list length is too long...
- if (isVarArgCall) {
- // ... we pack the parameters (except callee and this) into an array
- // and use Nashorn vararg invocation.
- mv.visitInvokeDynamicInsn(NameCodec.EMPTY_NAME,
- getArrayCreatorMethodType(type).toMethodDescriptorString(),
- CREATE_ARRAY_BOOTSTRAP_HANDLE);
- }
-
- // Invoke the target method handle
- mv.visitInvokeDynamicInsn(encodedName,
- getCallMethodType(isVarArgCall, type).toMethodDescriptorString(),
- BOOTSTRAP_HANDLE, NashornCallSiteDescriptor.CALL);
- // stack: [returnValue]
- convertReturnValue(mv, returnType);
- mv.visitLabel(normalFinally);
- emitFinally.run();
- mv.areturn(asmReturnType);
-
- // If Throwable is not declared, we need an adapter from Throwable to RuntimeException
- final boolean throwableDeclared = isThrowableDeclared(exceptions);
- final Label throwableHandler;
- if (!throwableDeclared) {
- // Add "throw new RuntimeException(Throwable)" handler for Throwable
- throwableHandler = new Label();
- mv.visitLabel(throwableHandler);
- WRAP_THROWABLE.invoke(mv);
- // Fall through to rethrow handler
- } else {
- throwableHandler = null;
- }
- final Label rethrowHandler = new Label();
- mv.visitLabel(rethrowHandler);
- // Rethrow handler for RuntimeException, Error, and all declared exception types
- emitFinally.run();
- mv.athrow();
-
- if(throwableDeclared) {
- mv.visitTryCatchBlock(tryBlockStart, normalFinally, rethrowHandler, THROWABLE_TYPE_NAME);
- assert throwableHandler == null;
- } else {
- mv.visitTryCatchBlock(tryBlockStart, normalFinally, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME);
- mv.visitTryCatchBlock(tryBlockStart, normalFinally, rethrowHandler, ERROR_TYPE_NAME);
- for(final String excName: exceptionNames) {
- mv.visitTryCatchBlock(tryBlockStart, normalFinally, rethrowHandler, excName);
- }
- mv.visitTryCatchBlock(tryBlockStart, normalFinally, throwableHandler, THROWABLE_TYPE_NAME);
- }
- endMethod(mv);
- }
-
- private static MethodType getCallMethodType(final boolean isVarArgCall, final MethodType type) {
- final Class>[] callParamTypes;
- if (isVarArgCall) {
- // Variable arity calls are always (Object callee, Object this, Object[] params)
- callParamTypes = new Class>[] { Object.class, Object.class, Object[].class };
- } else {
- // Adjust invocation type signature for conversions we instituted in
- // convertParam; also, byte and short get passed as ints.
- final Class>[] origParamTypes = type.parameterArray();
- callParamTypes = new Class>[origParamTypes.length + 2];
- callParamTypes[0] = Object.class; // callee; could be ScriptFunction.class ostensibly
- callParamTypes[1] = Object.class; // this
- for(int i = 0; i < origParamTypes.length; ++i) {
- callParamTypes[i + 2] = getNashornParamType(origParamTypes[i], false);
- }
- }
- return MethodType.methodType(getNashornReturnType(type.returnType()), callParamTypes);
- }
-
- private static MethodType getArrayCreatorMethodType(final MethodType type) {
- final Class>[] callParamTypes = type.parameterArray();
- for(int i = 0; i < callParamTypes.length; ++i) {
- callParamTypes[i] = getNashornParamType(callParamTypes[i], true);
- }
- return MethodType.methodType(Object[].class, callParamTypes);
- }
-
- private static Class> getNashornParamType(final Class> clazz, final boolean varArg) {
- if (clazz == byte.class || clazz == short.class) {
- return int.class;
- } else if (clazz == float.class) {
- // If using variable arity, we'll pass a Double instead of double
- // so that floats don't extend the length of the parameter list.
- // We return Object.class instead of Double.class though as the
- // array collector will anyway operate on Object.
- return varArg ? Object.class : double.class;
- } else if (!clazz.isPrimitive() || clazz == long.class || clazz == char.class) {
- return Object.class;
- }
- return clazz;
- }
-
- private static Class> getNashornReturnType(final Class> clazz) {
- if (clazz == byte.class || clazz == short.class) {
- return int.class;
- } else if (clazz == float.class) {
- return double.class;
- } else if (clazz == void.class || clazz == char.class) {
- return Object.class;
- }
- return clazz;
- }
-
-
- private void loadField(final InstructionAdapter mv, final String name, final String desc) {
- if(classOverride) {
- mv.getstatic(generatedClassName, name, desc);
- } else {
- mv.visitVarInsn(ALOAD, 0);
- mv.getfield(generatedClassName, name, desc);
- }
- }
-
- private static void convertReturnValue(final InstructionAdapter mv, final Class> origReturnType) {
- if (origReturnType == void.class) {
- mv.pop();
- } else if (origReturnType == Object.class) {
- // Must hide ConsString (and potentially other internal Nashorn types) from callers
- EXPORT_RETURN_VALUE.invoke(mv);
- } else if (origReturnType == byte.class) {
- mv.visitInsn(I2B);
- } else if (origReturnType == short.class) {
- mv.visitInsn(I2S);
- } else if (origReturnType == float.class) {
- mv.visitInsn(D2F);
- } else if (origReturnType == char.class) {
- TO_CHAR_PRIMITIVE.invoke(mv);
- }
- }
-
- /**
- * Emits instruction for converting a parameter on the top of the stack to
- * a type that is understood by Nashorn.
- * @param mv the current method visitor
- * @param t the type on the top of the stack
- * @param varArg if the invocation will be variable arity
- */
- private static void convertParam(final InstructionAdapter mv, final Type t, final boolean varArg) {
- // We perform conversions of some primitives to accommodate types that
- // Nashorn can handle.
- switch(t.getSort()) {
- case Type.CHAR:
- // Chars are boxed, as we don't know if the JS code wants to treat
- // them as an effective "unsigned short" or as a single-char string.
- CHAR_VALUE_OF.invoke(mv);
- break;
- case Type.FLOAT:
- // Floats are widened to double.
- mv.visitInsn(Opcodes.F2D);
- if (varArg) {
- // We'll be boxing everything anyway for the vararg invocation,
- // so we might as well do it proactively here and thus not cause
- // a widening in the number of slots, as that could even make
- // the array creation invocation go over 255 param slots.
- DOUBLE_VALUE_OF.invoke(mv);
- }
- break;
- case Type.LONG:
- // Longs are boxed as Nashorn can't represent them precisely as a
- // primitive number.
- LONG_VALUE_OF.invoke(mv);
- break;
- case Type.OBJECT:
- if(t.equals(OBJECT_TYPE)) {
- // Object can carry a ScriptObjectMirror and needs to be unwrapped
- // before passing into a Nashorn function.
- UNWRAP.invoke(mv);
- }
- break;
- }
- }
-
- private static int getParamListLengthInSlots(final Type[] paramTypes) {
- int len = paramTypes.length;
- for(final Type t: paramTypes) {
- final int sort = t.getSort();
- if (sort == Type.FLOAT || sort == Type.DOUBLE) {
- // Floats are widened to double, so they'll take up two slots.
- // Longs on the other hand are always boxed, so their width
- // becomes 1 and thus they don't contribute an extra slot here.
- ++len;
- }
- }
- return len;
- }
-
- /**
- * Emit code to restore the previous Nashorn Context when needed.
- * @param mv the instruction adapter
- * @param globalRestoringRunnableVar index of the local variable holding the reference to the global restoring Runnable
- */
- private static void emitFinally(final InstructionAdapter mv, final int globalRestoringRunnableVar) {
- mv.visitVarInsn(ALOAD, globalRestoringRunnableVar);
- RUN.invoke(mv);
- }
-
- private static boolean isThrowableDeclared(final Class>[] exceptions) {
- for (final Class> exception : exceptions) {
- if (exception == Throwable.class) {
- return true;
- }
- }
- return false;
- }
-
- private void generateSuperMethods() {
- for(final MethodInfo mi: methodInfos) {
- if(!Modifier.isAbstract(mi.method.getModifiers())) {
- generateSuperMethod(mi);
- }
- }
- }
-
- private void generateSuperMethod(final MethodInfo mi) {
- final Method method = mi.method;
-
- final String methodDesc = mi.type.toMethodDescriptorString();
- final String name = mi.getName();
-
- final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method),
- SUPER_PREFIX + name, methodDesc, null, getExceptionNames(method.getExceptionTypes())));
- mv.visitCode();
-
- emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc);
- mv.areturn(Type.getType(mi.type.returnType()));
- endMethod(mv);
- }
-
- // find the appropriate super type to use for invokespecial on the given interface
- private Class> findInvokespecialOwnerFor(final Class> cl) {
- assert Modifier.isInterface(cl.getModifiers()) : cl + " is not an interface";
-
- if (cl.isAssignableFrom(superClass)) {
- return superClass;
- }
-
- for (final Class> iface : interfaces) {
- if (cl.isAssignableFrom(iface)) {
- return iface;
- }
- }
-
- // we better that interface that extends the given interface!
- throw new AssertionError("can't find the class/interface that extends " + cl);
- }
-
- private int emitSuperConstructorCall(final InstructionAdapter mv, final String methodDesc) {
- return emitSuperCall(mv, null, INIT, methodDesc, true);
- }
-
- private int emitSuperCall(final InstructionAdapter mv, final Class> owner, final String name, final String methodDesc) {
- return emitSuperCall(mv, owner, name, methodDesc, false);
- }
-
- private int emitSuperCall(final InstructionAdapter mv, final Class> owner, final String name, final String methodDesc, final boolean constructor) {
- mv.visitVarInsn(ALOAD, 0);
- int nextParam = 1;
- final Type methodType = Type.getMethodType(methodDesc);
- for(final Type t: methodType.getArgumentTypes()) {
- mv.load(nextParam, t);
- nextParam += t.getSize();
- }
-
- // default method - non-abstract, interface method
- if (!constructor && Modifier.isInterface(owner.getModifiers())) {
- // we should call default method on the immediate "super" type - not on (possibly)
- // the indirectly inherited interface class!
- final Class> superType = findInvokespecialOwnerFor(owner);
- mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(superType), name, methodDesc,
- Modifier.isInterface(superType.getModifiers()));
- } else {
- mv.invokespecial(superClassName, name, methodDesc, false);
- }
- return nextParam;
- }
-
- private void generateFinalizerMethods() {
- generateFinalizerDelegate();
- generateFinalizerOverride();
- }
-
- private void generateFinalizerDelegate() {
- // Generate a delegate that will be invoked from the no-permission trampoline. Note it can be private, as we'll
- // refer to it with a MethodHandle constant pool entry in the overridden finalize() method (see
- // generateFinalizerOverride()).
- final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PRIVATE | ACC_STATIC,
- FINALIZER_DELEGATE_NAME, FINALIZER_DELEGATE_METHOD_DESCRIPTOR, null, null));
-
- // Simply invoke super.finalize()
- mv.visitVarInsn(ALOAD, 0);
- mv.checkcast(Type.getType('L' + generatedClassName + ';'));
- mv.invokespecial(superClassName, "finalize", VOID_METHOD_DESCRIPTOR, false);
-
- mv.visitInsn(RETURN);
- endMethod(mv);
- }
-
- private void generateFinalizerOverride() {
- final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, "finalize",
- VOID_METHOD_DESCRIPTOR, null, null));
- // Overridden finalizer will take a MethodHandle to the finalizer delegating method, ...
- mv.aconst(new Handle(Opcodes.H_INVOKESTATIC, generatedClassName, FINALIZER_DELEGATE_NAME,
- FINALIZER_DELEGATE_METHOD_DESCRIPTOR, false));
- mv.visitVarInsn(ALOAD, 0);
- // ...and invoke it through JavaAdapterServices.invokeNoPermissions
- INVOKE_NO_PERMISSIONS.invoke(mv);
- mv.visitInsn(RETURN);
- endMethod(mv);
- }
-
- private static String[] getExceptionNames(final Class>[] exceptions) {
- final String[] exceptionNames = new String[exceptions.length];
- for (int i = 0; i < exceptions.length; ++i) {
- exceptionNames[i] = Type.getInternalName(exceptions[i]);
- }
- return exceptionNames;
- }
-
- private static int getAccessModifiers(final Method method) {
- return ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0);
- }
-
- /**
- * Gathers methods that can be implemented or overridden from the specified type into this factory's
- * {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from
- * the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its
- * superclass and the interfaces it implements, and add further methods that were not directly declared on the
- * class.
- * @param type the type defining the methods.
- */
- private void gatherMethods(final Class> type) throws AdaptationException {
- if (Modifier.isPublic(type.getModifiers())) {
- final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods();
-
- for (final Method typeMethod: typeMethods) {
- final String name = typeMethod.getName();
- if(name.startsWith(SUPER_PREFIX)) {
- continue;
- }
- final int m = typeMethod.getModifiers();
- if (Modifier.isStatic(m)) {
- continue;
- }
- if (Modifier.isPublic(m) || Modifier.isProtected(m)) {
- // Is it a "finalize()"?
- if(name.equals("finalize") && typeMethod.getParameterCount() == 0) {
- if(type != Object.class) {
- hasExplicitFinalizer = true;
- if(Modifier.isFinal(m)) {
- // Must be able to override an explicit finalizer
- throw new AdaptationException(Outcome.ERROR_FINAL_FINALIZER, type.getCanonicalName());
- }
- }
- continue;
- }
-
- final MethodInfo mi = new MethodInfo(typeMethod);
- if (Modifier.isFinal(m) || isCallerSensitive(typeMethod)) {
- finalMethods.add(mi);
- } else if (!finalMethods.contains(mi) && methodInfos.add(mi) && Modifier.isAbstract(m)) {
- abstractMethodNames.add(mi.getName());
- }
- }
- }
- }
- // If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done.
- // Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to
- // see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a
- // superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and
- // getMethods() does provide those declared in a superinterface.
- if (!type.isInterface()) {
- final Class> superType = type.getSuperclass();
- if (superType != null) {
- gatherMethods(superType);
- }
- for (final Class> itf: type.getInterfaces()) {
- gatherMethods(itf);
- }
- }
- }
-
- private void gatherMethods(final List> classes) throws AdaptationException {
- for(final Class> c: classes) {
- gatherMethods(c);
- }
- }
-
- private static final AccessControlContext GET_DECLARED_MEMBERS_ACC_CTXT = ClassAndLoader.createPermAccCtxt("accessDeclaredMembers");
-
- /**
- * Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters,
- * as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and
- * {@code Object.clone()}.
- * @return a collection of method infos representing those methods that we never override in adapter classes.
- */
- private static Collection getExcludedMethods() {
- return AccessController.doPrivileged(new PrivilegedAction>() {
- @Override
- public Collection run() {
- try {
- return Arrays.asList(
- new MethodInfo(Object.class, "finalize"),
- new MethodInfo(Object.class, "clone"));
- } catch (final NoSuchMethodException e) {
- throw new AssertionError(e);
- }
- }
- }, GET_DECLARED_MEMBERS_ACC_CTXT);
- }
-
- private String getCommonSuperClass(final String type1, final String type2) {
- try {
- final Class> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader);
- final Class> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader);
- if (c1.isAssignableFrom(c2)) {
- return type1;
- }
- if (c2.isAssignableFrom(c1)) {
- return type2;
- }
- if (c1.isInterface() || c2.isInterface()) {
- return OBJECT_TYPE.getInternalName();
- }
- return assignableSuperClass(c1, c2).getName().replace('.', '/');
- } catch(final ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
- }
-
- private static Class> assignableSuperClass(final Class> c1, final Class> c2) {
- final Class> superClass = c1.getSuperclass();
- return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2);
- }
-
- private static boolean isCallerSensitive(final AccessibleObject e) {
- return e.isAnnotationPresent(CallerSensitive.class);
- }
-
- private static Call lookupServiceMethod(final String name, final Class> rtype, final Class>... ptypes) {
- return staticCallNoLookup(JavaAdapterServices.class, name, rtype, ptypes);
- }
-}