JavaAdapterBytecodeGenerator.java revision 1329:1b4ad06c714e
12606Sdfr/* 22606Sdfr * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 331603Syokota * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 42606Sdfr * 52606Sdfr * This code is free software; you can redistribute it and/or modify it 62606Sdfr * under the terms of the GNU General Public License version 2 only, as 72606Sdfr * published by the Free Software Foundation. Oracle designates this 82606Sdfr * particular file as subject to the "Classpath" exception as provided 92606Sdfr * by Oracle in the LICENSE file that accompanied this code. 102606Sdfr * 112606Sdfr * This code is distributed in the hope that it will be useful, but WITHOUT 122606Sdfr * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 132606Sdfr * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 142606Sdfr * version 2 for more details (a copy is included in the LICENSE file that 152606Sdfr * accompanied this code). 162606Sdfr * 172606Sdfr * You should have received a copy of the GNU General Public License version 182606Sdfr * 2 along with this work; if not, write to the Free Software Foundation, 192606Sdfr * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 202606Sdfr * 212606Sdfr * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 222606Sdfr * or visit www.oracle.com if you need additional information or have any 2350477Speter * questions. 242606Sdfr */ 252606Sdfr 2666860Sphkpackage jdk.nashorn.internal.runtime.linker; 2766860Sphk 2819753Ssosimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 2919753Ssosimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; 3019753Ssosimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 3119753Ssosimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 3231603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; 3331603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS; 3431603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL; 3531603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; 3631603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.ASTORE; 3731603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.DUP; 3831603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL; 3931603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; 4031603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.ISTORE; 4131603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.POP; 4231603Syokotaimport static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 4319753Ssosimport static jdk.nashorn.internal.lookup.Lookup.MH; 4431603Syokotaimport static jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR; 4531603Syokota 4631603Syokotaimport java.lang.invoke.MethodHandle; 4731603Syokotaimport java.lang.invoke.MethodType; 4831603Syokotaimport java.lang.reflect.AccessibleObject; 4931603Syokotaimport java.lang.reflect.Constructor; 5019753Ssosimport java.lang.reflect.Method; 51132865Snjlimport java.lang.reflect.Modifier; 52132865Snjlimport java.security.AccessControlContext; 5320073Ssosimport java.security.AccessController; 5420073Ssosimport java.security.PrivilegedAction; 5531603Syokotaimport java.util.Arrays; 5620073Ssosimport java.util.Collection; 5720073Ssosimport java.util.HashSet; 5820073Ssosimport java.util.Iterator; 5920073Ssosimport java.util.LinkedHashMap; 6031603Syokotaimport java.util.List; 6120073Ssosimport java.util.Map; 6221327Snateimport java.util.Set; 6320073Ssosimport jdk.internal.org.objectweb.asm.ClassWriter; 6420073Ssosimport jdk.internal.org.objectweb.asm.Handle; 6520073Ssosimport jdk.internal.org.objectweb.asm.Label; 6620073Ssosimport jdk.internal.org.objectweb.asm.Opcodes; 6720073Ssosimport jdk.internal.org.objectweb.asm.Type; 6820073Ssosimport jdk.internal.org.objectweb.asm.commons.InstructionAdapter; 6920073Ssosimport jdk.nashorn.api.scripting.ScriptUtils; 7020073Ssosimport jdk.nashorn.internal.runtime.Context; 7120073Ssosimport jdk.nashorn.internal.runtime.JSType; 7231603Syokotaimport jdk.nashorn.internal.runtime.ScriptFunction; 7331603Syokotaimport jdk.nashorn.internal.runtime.ScriptObject; 7431603Syokotaimport jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome; 7531603Syokotaimport sun.reflect.CallerSensitive; 7620073Ssos 7731603Syokota/** 7831603Syokota * Generates bytecode for a Java adapter class. Used by the {@link JavaAdapterFactory}. 7931603Syokota * </p><p> 8031603Syokota * For every protected or public constructor in the extended class, the adapter class will have either one or two 8131603Syokota * public constructors (visibility of protected constructors in the extended class is promoted to public). 8231603Syokota * <li> 8319753Ssos * <li>For adapter classes with instance-level overrides, a constructor taking a trailing ScriptObject argument preceded 8431603Syokota * by original constructor arguments is always created on the adapter class. When such a constructor is invoked, the 8519753Ssos * passed ScriptObject's member functions are used to implement and/or override methods on the original class, 8619753Ssos * dispatched by name. A single JavaScript function will act as the implementation for all overloaded methods of the 8731603Syokota * same name. When methods on an adapter instance are invoked, the functions are invoked having the ScriptObject passed 8819753Ssos * in the instance constructor as their "this". Subsequent changes to the ScriptObject (reassignment or removal of its 8921327Snate * functions) are not reflected in the adapter instance; the method implementations are bound to functions at 9021327Snate * constructor invocation time. 9119753Ssos * {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The 9221327Snate * only restriction is that since every JavaScript object already has a {@code toString} function through the 93132865Snjl * {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a 94132865Snjl * {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be 95132865Snjl * implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too. 96132865Snjl * </li> 97132865Snjl * <li> 98132865Snjl * If the original types collectively have only one abstract method, or have several of them, but all share the 99132865Snjl * same name, an additional constructor for instance-level override adapter is provided for every original constructor; 100132865Snjl * this one takes a ScriptFunction as its last argument preceded by original constructor arguments. This constructor 101132865Snjl * will use the passed function as the implementation for all abstract methods. For consistency, any concrete methods 102132865Snjl * sharing the single abstract method name will also be overridden by the function. When methods on the adapter instance 103132865Snjl * are invoked, the ScriptFunction is invoked with UNDEFINED or Global as its "this" depending whether the function is 104132865Snjl * strict or not. 105132865Snjl * </li> 106132865Snjl * <li> 107132865Snjl * If the adapter being generated can have class-level overrides, constructors taking same arguments as the superclass 108132865Snjl * constructors are created. These constructors simply delegate to the superclass constructor. They are simply used to 109132865Snjl * create instances of the adapter class, with no instance-level overrides, as they don't have them. If the original 110132865Snjl * class' constructor was variable arity, the adapter constructor will also be variable arity. Protected constructors 111132865Snjl * are exposed as public. 11219753Ssos * </li> 11331603Syokota * </ul> 11419753Ssos * </p><p> 11519753Ssos * For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect 11619753Ssos * to coerce the JavaScript function return value to the expected Java return type. 11719753Ssos * </p><p> 11831603Syokota * Since we are adding a trailing argument to the generated constructors in the adapter class, they will never be 11944186Sn_hibma * declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The 12021327Snate * reason we are passing the additional argument at the end of the argument list instead at the front is that the 12119753Ssos * source-level script expression <code>new X(a, b) { ... }</code> (which is a proprietary syntax extension Nashorn uses 12219753Ssos * to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>. 12319753Ssos * </p><p> 12419753Ssos * It is possible to create two different adapter classes: those that can have class-level overrides, and those that can 12519753Ssos * have instance-level overrides. When {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked 12619753Ssos * with non-null {@code classOverrides} parameter, an adapter class is created that can have class-level overrides, and 1272606Sdfr * the passed script object will be used as the implementations for its methods, just as in the above case of the 12831603Syokota * constructor taking a script object. Note that in the case of class-level overrides, a new adapter class is created on 12931603Syokota * every invocation, and the implementation object is bound to the class, not to any instance. All created instances 13031603Syokota * will share these functions. If it is required to have both class-level overrides and instance-level overrides, the 13131603Syokota * class-level override adapter class should be subclassed with an instance-override adapter. Since adapters delegate to 13231603Syokota * super class when an overriding method handle is not specified, this will behave as expected. It is not possible to 13331603Syokota * have both class-level and instance-level overrides in the same class for security reasons: adapter classes are 13431603Syokota * defined with a protection domain of their creator code, and an adapter class that has both class and instance level 13531603Syokota * overrides would need to have two potentially different protection domains: one for class-based behavior and one for 13631603Syokota * instance-based behavior; since Java classes can only belong to a single protection domain, this could not be 13731603Syokota * implemented securely. 13841271Syokota */ 13949964Syokotafinal class JavaAdapterBytecodeGenerator { 14058230Syokota private static final Type SCRIPTUTILS_TYPE = Type.getType(ScriptUtils.class); 14158230Syokota private static final Type OBJECT_TYPE = Type.getType(Object.class); 14258230Syokota private static final Type CLASS_TYPE = Type.getType(Class.class); 143132865Snjl 14431603Syokota static final String OBJECT_TYPE_NAME = OBJECT_TYPE.getInternalName(); 14519753Ssos static final String SCRIPTUTILS_TYPE_NAME = SCRIPTUTILS_TYPE.getInternalName(); 14619753Ssos 14719753Ssos static final String INIT = "<init>"; 14831603Syokota 14919753Ssos static final String GLOBAL_FIELD_NAME = "global"; 15031603Syokota 15131603Syokota // "global" is declared as Object instead of Global - avoid static references to internal Nashorn classes when possible. 15231603Syokota static final String GLOBAL_TYPE_DESCRIPTOR = OBJECT_TYPE.getDescriptor(); 15319753Ssos 15421327Snate static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE); 15519753Ssos static final String VOID_NOARG_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE); 15658230Syokota 15758230Syokota private static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class); 15858230Syokota private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class); 15958230Syokota private static final Type STRING_TYPE = Type.getType(String.class); 16058230Syokota private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class); 16158230Syokota private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class); 16258230Syokota private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE, 16358230Syokota OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE); 16458230Syokota private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE, 16558230Syokota SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE); 16631603Syokota private static final String GET_CLASS_INITIALIZER_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE); 16719753Ssos private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class); 16819753Ssos private static final Type THROWABLE_TYPE = Type.getType(Throwable.class); 16919753Ssos private static final Type UNSUPPORTED_OPERATION_TYPE = Type.getType(UnsupportedOperationException.class); 17019753Ssos 17119753Ssos private static final String SERVICES_CLASS_TYPE_NAME = Type.getInternalName(JavaAdapterServices.class); 17219753Ssos private static final String RUNTIME_EXCEPTION_TYPE_NAME = RUNTIME_EXCEPTION_TYPE.getInternalName(); 17331603Syokota private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class); 17419753Ssos private static final String THROWABLE_TYPE_NAME = THROWABLE_TYPE.getInternalName(); 17531603Syokota private static final String UNSUPPORTED_OPERATION_TYPE_NAME = UNSUPPORTED_OPERATION_TYPE.getInternalName(); 17631603Syokota 17731603Syokota private static final String METHOD_HANDLE_TYPE_DESCRIPTOR = METHOD_HANDLE_TYPE.getDescriptor(); 17831603Syokota private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE); 17931603Syokota private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(CLASS_TYPE); 18036991Sahasty private static final String EXPORT_RETURN_VALUE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE); 18141271Syokota private static final String UNWRAP_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE); 18249964Syokota private static final String GET_CONVERTER_METHOD_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE, CLASS_TYPE); 18393071Swill private static final String TO_CHAR_PRIMITIVE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.CHAR_TYPE, OBJECT_TYPE); 184145001Smdodd private static final String TO_STRING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(STRING_TYPE, OBJECT_TYPE); 18519753Ssos 18631603Syokota // Package used when the adapter can't be defined in the adaptee's package (either because it's sealed, or because 18731603Syokota // it's a java.* package. 18831603Syokota private static final String ADAPTER_PACKAGE_PREFIX = "jdk/nashorn/javaadapters/"; 18931603Syokota // Class name suffix used to append to the adaptee class name, when it can be defined in the adaptee's package. 19031603Syokota private static final String ADAPTER_CLASS_NAME_SUFFIX = "$$NashornJavaAdapter"; 19131603Syokota private static final String JAVA_PACKAGE_PREFIX = "java/"; 19231603Syokota private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 255; 19331603Syokota 19431603Syokota private static final String CLASS_INIT = "<clinit>"; 19531603Syokota 19631603Syokota // Method name prefix for invoking super-methods 19731603Syokota static final String SUPER_PREFIX = "super$"; 19831603Syokota 19931603Syokota /** 20031603Syokota * Collection of methods we never override: Object.clone(), Object.finalize(). 20131603Syokota */ 20231603Syokota private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods(); 20331603Syokota 20431603Syokota // This is the superclass for our generated adapter. 20531603Syokota private final Class<?> superClass; 20631603Syokota // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class 20731603Syokota // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the 20831603Syokota // Nashorn classes. 20931603Syokota private final ClassLoader commonLoader; 21031603Syokota // Is this a generator for the version of the class that can have overrides on the class level? 211132865Snjl private final boolean classOverride; 212132865Snjl // Binary name of the superClass 213132865Snjl private final String superClassName; 21419753Ssos // Binary name of the generated class. 21519753Ssos private final String generatedClassName; 21619753Ssos private final Set<String> usedFieldNames = new HashSet<>(); 21719753Ssos private final Set<String> abstractMethodNames = new HashSet<>(); 21819753Ssos private final String samName; 21919753Ssos private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED); 22019753Ssos private final Set<MethodInfo> methodInfos = new HashSet<>(); 22119753Ssos private boolean autoConvertibleFromFunction = false; 22219753Ssos private boolean hasExplicitFinalizer = false; 22320073Ssos 22431603Syokota /** 22520073Ssos * Names of static fields holding type converter method handles for return value conversion. We are emitting code 22679298Sdd * for invoking these explicitly after the delegate handle is invoked, instead of doing an asType or 22731603Syokota * filterReturnValue on the delegate handle, as that would create a new converter handle wrapping the function's 22831603Syokota * handle for every instance of the adapter, causing the handle.invokeExact() call sites to become megamorphic. 22931603Syokota */ 23079298Sdd private final Map<Class<?>, String> converterFields = new LinkedHashMap<>(); 23131603Syokota 23231603Syokota /** 23331603Syokota * Subset of possible return types for all methods; namely, all possible return types of the SAM methods (we 23431603Syokota * identify SAM types by having all of their abstract methods share a single name, so there can be multiple 23531603Syokota * overloads with multiple return types. We use this set when emitting the constructor taking a ScriptFunction (the 23658230Syokota * SAM initializer) to avoid populating converter fields that will never be used by SAM methods. 23731603Syokota */ 23819753Ssos private final Set<Class<?>> samReturnTypes = new HashSet<>(); 23919753Ssos 24019753Ssos private final ClassWriter cw; 24119753Ssos 24219753Ssos /** 24319753Ssos * Creates a generator for the bytecode for the adapter for the specified superclass and interfaces. 24419753Ssos * @param superClass the superclass the adapter will extend. 24519753Ssos * @param interfaces the interfaces the adapter will implement. 24644186Sn_hibma * @param commonLoader the class loader that can see all of superClass, interfaces, and Nashorn classes. 24719753Ssos * @param classOverride true to generate the bytecode for the adapter that has class-level overrides, false to 24820073Ssos * generate the bytecode for the adapter that has instance-level overrides. 24920073Ssos * @throws AdaptationException if the adapter can not be generated for some reason. 25020073Ssos */ 25120073Ssos JavaAdapterBytecodeGenerator(final Class<?> superClass, final List<Class<?>> interfaces, 25220073Ssos final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException { 25320073Ssos assert superClass != null && !superClass.isInterface(); 25420073Ssos assert interfaces != null; 25520073Ssos 25620073Ssos this.superClass = superClass; 25720073Ssos this.classOverride = classOverride; 25820073Ssos this.commonLoader = commonLoader; 25919753Ssos cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { 26019753Ssos @Override 26131603Syokota protected String getCommonSuperClass(final String type1, final String type2) { 26231603Syokota // We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class 26319753Ssos // loader to find the common superclass of two types when needed. 26419753Ssos return JavaAdapterBytecodeGenerator.this.getCommonSuperClass(type1, type2); 26519753Ssos } 26619753Ssos }; 26731603Syokota superClassName = Type.getInternalName(superClass); 26831603Syokota generatedClassName = getGeneratedClassName(superClass, interfaces); 26931603Syokota 27031603Syokota cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER, generatedClassName, null, superClassName, getInternalTypeNames(interfaces)); 27131603Syokota generateGlobalFields(); 27219753Ssos 27319753Ssos gatherMethods(superClass); 27419753Ssos gatherMethods(interfaces); 27519753Ssos samName = abstractMethodNames.size() == 1 ? abstractMethodNames.iterator().next() : null; 27658230Syokota generateHandleFields(); 27758230Syokota generateConverterFields(); 27848778Syokota if(classOverride) { 27948778Syokota generateClassInit(); 28058230Syokota } 28158230Syokota generateConstructors(); 28258230Syokota generateMethods(); 28319753Ssos generateSuperMethods(); 28458230Syokota if (hasExplicitFinalizer) { 28558230Syokota generateFinalizerMethods(); 28658230Syokota } 28758230Syokota // } 28858230Syokota cw.visitEnd(); 28958230Syokota } 29058230Syokota 29158230Syokota private void generateGlobalFields() { 29258230Syokota cw.visitField(ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0), GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR, null, null).visitEnd(); 29358230Syokota usedFieldNames.add(GLOBAL_FIELD_NAME); 29458230Syokota } 29558230Syokota 29658230Syokota JavaAdapterClassLoader createAdapterClassLoader() { 29758230Syokota return new JavaAdapterClassLoader(generatedClassName, cw.toByteArray()); 29858230Syokota } 29958230Syokota 30049964Syokota boolean isAutoConvertibleFromFunction() { 30149964Syokota return autoConvertibleFromFunction; 30249964Syokota } 30349964Syokota 30449964Syokota private static String getGeneratedClassName(final Class<?> superType, final List<Class<?>> interfaces) { 30549964Syokota // The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're 30649964Syokota // just implementing interfaces or extending Object), then the first implemented interface or Object. 30749964Syokota final Class<?> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType; 30849964Syokota final Package pkg = namingType.getPackage(); 30949964Syokota final String namingTypeName = Type.getInternalName(namingType); 31049964Syokota final StringBuilder buf = new StringBuilder(); 31149964Syokota if (namingTypeName.startsWith(JAVA_PACKAGE_PREFIX) || pkg == null || pkg.isSealed()) { 31249964Syokota // Can't define new classes in java.* packages 31349964Syokota buf.append(ADAPTER_PACKAGE_PREFIX).append(namingTypeName); 31449964Syokota } else { 31549964Syokota buf.append(namingTypeName).append(ADAPTER_CLASS_NAME_SUFFIX); 31649964Syokota } 31749964Syokota final Iterator<Class<?>> it = interfaces.iterator(); 31849964Syokota if(superType == Object.class && it.hasNext()) { 31949964Syokota it.next(); // Skip first interface, it was used to primarily name the adapter 32049964Syokota } 32149964Syokota // Append interface names to the adapter name 32258230Syokota while(it.hasNext()) { 32358230Syokota buf.append("$$").append(it.next().getSimpleName()); 32458230Syokota } 32558230Syokota return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length())); 32658230Syokota } 32758230Syokota 32858230Syokota /** 32958230Syokota * Given a list of class objects, return an array with their binary names. Used to generate the array of interface 33058230Syokota * names to implement. 33131603Syokota * @param classes the classes 33231603Syokota * @return an array of names 33331603Syokota */ 33431603Syokota private static String[] getInternalTypeNames(final List<Class<?>> classes) { 33531603Syokota final int interfaceCount = classes.size(); 33631603Syokota final String[] interfaceNames = new String[interfaceCount]; 33731603Syokota for(int i = 0; i < interfaceCount; ++i) { 33831603Syokota interfaceNames[i] = Type.getInternalName(classes.get(i)); 33931603Syokota } 34031603Syokota return interfaceNames; 34131603Syokota } 34231603Syokota 34331603Syokota private void generateHandleFields() { 34431603Syokota final int flags = ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0); 34531603Syokota for (final MethodInfo mi: methodInfos) { 34631603Syokota cw.visitField(flags, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd(); 34731603Syokota } 34831603Syokota } 34931603Syokota 35031603Syokota private void generateConverterFields() { 35131603Syokota final int flags = ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0); 35231603Syokota for (final MethodInfo mi: methodInfos) { 35331603Syokota final Class<?> returnType = mi.type.returnType(); 35431603Syokota // Handle primitive types, Object, and String specially 35531603Syokota if(!returnType.isPrimitive() && returnType != Object.class && returnType != String.class) { 35631603Syokota if(!converterFields.containsKey(returnType)) { 35731603Syokota final String name = nextName("convert"); 35831603Syokota converterFields.put(returnType, name); 35936991Sahasty if(mi.getName().equals(samName)) { 36036991Sahasty samReturnTypes.add(returnType); 36136991Sahasty } 36266860Sphk cw.visitField(flags, name, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd(); 363 } 364 } 365 } 366 } 367 368 private void generateClassInit() { 369 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_STATIC, CLASS_INIT, 370 Type.getMethodDescriptor(Type.VOID_TYPE), null, null)); 371 372 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getClassOverrides", GET_CLASS_INITIALIZER_DESCRIPTOR, false); 373 final Label initGlobal; 374 if(samName != null) { 375 // If the class is a SAM, allow having a ScriptFunction passed as class overrides 376 final Label notAFunction = new Label(); 377 mv.dup(); 378 mv.instanceOf(SCRIPT_FUNCTION_TYPE); 379 mv.ifeq(notAFunction); 380 mv.checkcast(SCRIPT_FUNCTION_TYPE); 381 382 // Assign MethodHandle fields through invoking getHandle() for a ScriptFunction, only assigning the SAM 383 // method(s). 384 for (final MethodInfo mi : methodInfos) { 385 if(mi.getName().equals(samName)) { 386 mv.dup(); 387 loadMethodTypeAndGetHandle(mv, mi, GET_HANDLE_FUNCTION_DESCRIPTOR); 388 } else { 389 mv.visitInsn(ACONST_NULL); 390 } 391 mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); 392 } 393 initGlobal = new Label(); 394 mv.goTo(initGlobal); 395 mv.visitLabel(notAFunction); 396 } else { 397 initGlobal = null; 398 } 399 // Assign MethodHandle fields through invoking getHandle() for a ScriptObject 400 for (final MethodInfo mi : methodInfos) { 401 mv.dup(); 402 mv.aconst(mi.getName()); 403 loadMethodTypeAndGetHandle(mv, mi, GET_HANDLE_OBJECT_DESCRIPTOR); 404 mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); 405 } 406 407 if(initGlobal != null) { 408 mv.visitLabel(initGlobal); 409 } 410 // Assign "global = Context.getGlobal()" 411 invokeGetGlobalWithNullCheck(mv); 412 mv.putstatic(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR); 413 414 generateConverterInit(mv, false); 415 endInitMethod(mv); 416 } 417 418 private void generateConverterInit(final InstructionAdapter mv, final boolean samOnly) { 419 assert !samOnly || !classOverride; 420 for(final Map.Entry<Class<?>, String> converterField: converterFields.entrySet()) { 421 final Class<?> returnType = converterField.getKey(); 422 if(!classOverride) { 423 mv.visitVarInsn(ALOAD, 0); 424 } 425 426 if(samOnly && !samReturnTypes.contains(returnType)) { 427 mv.visitInsn(ACONST_NULL); 428 } else { 429 mv.aconst(Type.getType(converterField.getKey())); 430 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getObjectConverter", GET_CONVERTER_METHOD_DESCRIPTOR, false); 431 } 432 433 if(classOverride) { 434 mv.putstatic(generatedClassName, converterField.getValue(), METHOD_HANDLE_TYPE_DESCRIPTOR); 435 } else { 436 mv.putfield(generatedClassName, converterField.getValue(), METHOD_HANDLE_TYPE_DESCRIPTOR); 437 } 438 } 439 } 440 441 private static void loadMethodTypeAndGetHandle(final InstructionAdapter mv, final MethodInfo mi, final String getHandleDescriptor) { 442 // NOTE: we're using generic() here because we'll be linking to the "generic" invoker version of 443 // the functions anyway, so we cut down on megamorphism in the invokeExact() calls in adapter 444 // bodies. Once we start linking to type-specializing invokers, this should be changed. 445 mv.aconst(Type.getMethodType(mi.type.generic().toMethodDescriptorString())); 446 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor, false); 447 } 448 449 private static void invokeGetGlobalWithNullCheck(final InstructionAdapter mv) { 450 invokeGetGlobal(mv); 451 mv.dup(); 452 mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR, false); // check against null Context 453 mv.pop(); 454 } 455 456 private void generateConstructors() throws AdaptationException { 457 boolean gotCtor = false; 458 for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) { 459 final int modifier = ctor.getModifiers(); 460 if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0 && !isCallerSensitive(ctor)) { 461 generateConstructors(ctor); 462 gotCtor = true; 463 } 464 } 465 if(!gotCtor) { 466 throw new AdaptationException(ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName()); 467 } 468 } 469 470 private void generateConstructors(final Constructor<?> ctor) { 471 if(classOverride) { 472 // Generate a constructor that just delegates to ctor. This is used with class-level overrides, when we want 473 // to create instances without further per-instance overrides. 474 generateDelegatingConstructor(ctor); 475 } else { 476 // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the 477 // beginning of its parameter list. 478 generateOverridingConstructor(ctor, false); 479 480 if (samName != null) { 481 if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) { 482 // If the original type only has a single abstract method name, as well as a default ctor, then it can 483 // be automatically converted from JS function. 484 autoConvertibleFromFunction = true; 485 } 486 // If all our abstract methods have a single name, generate an additional constructor, one that takes a 487 // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods. 488 generateOverridingConstructor(ctor, true); 489 } 490 } 491 } 492 493 private void generateDelegatingConstructor(final Constructor<?> ctor) { 494 final Type originalCtorType = Type.getType(ctor); 495 final Type[] argTypes = originalCtorType.getArgumentTypes(); 496 497 // All constructors must be public, even if in the superclass they were protected. 498 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC | 499 (ctor.isVarArgs() ? ACC_VARARGS : 0), INIT, 500 Type.getMethodDescriptor(originalCtorType.getReturnType(), argTypes), null, null)); 501 502 mv.visitCode(); 503 // Invoke super constructor with the same arguments. 504 mv.visitVarInsn(ALOAD, 0); 505 int offset = 1; // First arg is at position 1, after this. 506 for (final Type argType: argTypes) { 507 mv.load(offset, argType); 508 offset += argType.getSize(); 509 } 510 mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor(), false); 511 512 endInitMethod(mv); 513 } 514 515 /** 516 * Generates a constructor for the instance adapter class. This constructor will take the same arguments as the supertype 517 * constructor passed as the argument here, and delegate to it. However, it will take an additional argument of 518 * either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize 519 * all the method handle fields of the adapter instance with functions from the script object (or the script 520 * function itself, if that's what's passed). There is one method handle field in the adapter class for every method 521 * that can be implemented or overridden; the name of every field is same as the name of the method, with a number 522 * suffix that makes it unique in case of overloaded methods. The generated constructor will invoke 523 * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType, 524 * boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity 525 * adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}. 526 * The constructor that takes a script function will only initialize the methods with the same name as the single 527 * abstract method. The constructor will also store the Nashorn global that was current at the constructor 528 * invocation time in a field named "global". The generated constructor will be public, regardless of whether the 529 * supertype constructor was public or protected. The generated constructor will not be variable arity, even if the 530 * supertype constructor was. 531 * @param ctor the supertype constructor that is serving as the base for the generated constructor. 532 * @param fromFunction true if we're generating a constructor that initializes SAM types from a single 533 * ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a 534 * ScriptObject passed to it. 535 */ 536 private void generateOverridingConstructor(final Constructor<?> ctor, final boolean fromFunction) { 537 final Type originalCtorType = Type.getType(ctor); 538 final Type[] originalArgTypes = originalCtorType.getArgumentTypes(); 539 final int argLen = originalArgTypes.length; 540 final Type[] newArgTypes = new Type[argLen + 1]; 541 542 // Insert ScriptFunction|ScriptObject as the last argument to the constructor 543 final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : SCRIPT_OBJECT_TYPE; 544 newArgTypes[argLen] = extraArgumentType; 545 System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen); 546 547 // All constructors must be public, even if in the superclass they were protected. 548 // Existing super constructor <init>(this, args...) triggers generating <init>(this, args..., scriptObj). 549 // Any variable arity constructors become fixed-arity with explicit array arguments. 550 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT, 551 Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null)); 552 553 mv.visitCode(); 554 // First, invoke super constructor with original arguments. If the form of the constructor we're generating is 555 // <init>(this, args..., scriptFn), then we're invoking super.<init>(this, args...). 556 mv.visitVarInsn(ALOAD, 0); 557 final Class<?>[] argTypes = ctor.getParameterTypes(); 558 int offset = 1; // First arg is at position 1, after this. 559 for (int i = 0; i < argLen; ++i) { 560 final Type argType = Type.getType(argTypes[i]); 561 mv.load(offset, argType); 562 offset += argType.getSize(); 563 } 564 mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor(), false); 565 566 // Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method. 567 final String getHandleDescriptor = fromFunction ? GET_HANDLE_FUNCTION_DESCRIPTOR : GET_HANDLE_OBJECT_DESCRIPTOR; 568 569 // Assign MethodHandle fields through invoking getHandle() 570 for (final MethodInfo mi : methodInfos) { 571 mv.visitVarInsn(ALOAD, 0); 572 if (fromFunction && !mi.getName().equals(samName)) { 573 // Constructors initializing from a ScriptFunction only initialize methods with the SAM name. 574 // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This 575 // is a deliberate design choice. All other method handles are initialized to null. 576 mv.visitInsn(ACONST_NULL); 577 } else { 578 mv.visitVarInsn(ALOAD, offset); 579 if(!fromFunction) { 580 mv.aconst(mi.getName()); 581 } 582 loadMethodTypeAndGetHandle(mv, mi, getHandleDescriptor); 583 } 584 mv.putfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); 585 } 586 587 // Assign "this.global = Context.getGlobal()" 588 mv.visitVarInsn(ALOAD, 0); 589 invokeGetGlobalWithNullCheck(mv); 590 mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR); 591 592 // Initialize converters 593 generateConverterInit(mv, fromFunction); 594 endInitMethod(mv); 595 596 if (! fromFunction) { 597 newArgTypes[argLen] = OBJECT_TYPE; 598 final InstructionAdapter mv2 = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT, 599 Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null)); 600 generateOverridingConstructorWithObjectParam(mv2, ctor, originalCtorType.getDescriptor()); 601 } 602 } 603 604 // Object additional param accepting constructor - generated to handle null and undefined value 605 // for script adapters. This is effectively to throw TypeError on such script adapters. See 606 // JavaAdapterServices.getHandle as well. 607 private void generateOverridingConstructorWithObjectParam(final InstructionAdapter mv, final Constructor<?> ctor, final String ctorDescriptor) { 608 mv.visitCode(); 609 mv.visitVarInsn(ALOAD, 0); 610 final Class<?>[] argTypes = ctor.getParameterTypes(); 611 int offset = 1; // First arg is at position 1, after this. 612 for (int i = 0; i < argTypes.length; ++i) { 613 final Type argType = Type.getType(argTypes[i]); 614 mv.load(offset, argType); 615 offset += argType.getSize(); 616 } 617 mv.invokespecial(superClassName, INIT, ctorDescriptor, false); 618 mv.visitVarInsn(ALOAD, offset); 619 mv.visitInsn(ACONST_NULL); 620 mv.visitInsn(ACONST_NULL); 621 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", GET_HANDLE_OBJECT_DESCRIPTOR, false); 622 endInitMethod(mv); 623 } 624 625 private static void endInitMethod(final InstructionAdapter mv) { 626 mv.visitInsn(RETURN); 627 endMethod(mv); 628 } 629 630 private static void endMethod(final InstructionAdapter mv) { 631 mv.visitMaxs(0, 0); 632 mv.visitEnd(); 633 } 634 635 private static void invokeGetGlobal(final InstructionAdapter mv) { 636 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR, false); 637 } 638 639 private static void invokeSetGlobal(final InstructionAdapter mv) { 640 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR, false); 641 } 642 643 /** 644 * Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the 645 * reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the 646 * method handle serving as the implementation of this method in adapter instances. 647 * 648 */ 649 private static class MethodInfo { 650 private final Method method; 651 private final MethodType type; 652 private String methodHandleFieldName; 653 654 private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException { 655 this(clazz.getDeclaredMethod(name, argTypes)); 656 } 657 658 private MethodInfo(final Method method) { 659 this.method = method; 660 this.type = MH.type(method.getReturnType(), method.getParameterTypes()); 661 } 662 663 @Override 664 public boolean equals(final Object obj) { 665 return obj instanceof MethodInfo && equals((MethodInfo)obj); 666 } 667 668 private boolean equals(final MethodInfo other) { 669 // Only method name and type are used for comparison; method handle field name is not. 670 return getName().equals(other.getName()) && type.equals(other.type); 671 } 672 673 String getName() { 674 return method.getName(); 675 } 676 677 @Override 678 public int hashCode() { 679 return getName().hashCode() ^ type.hashCode(); 680 } 681 682 void setIsCanonical(final JavaAdapterBytecodeGenerator self) { 683 methodHandleFieldName = self.nextName(getName()); 684 } 685 } 686 687 private String nextName(final String name) { 688 int i = 0; 689 String nextName = name; 690 while (!usedFieldNames.add(nextName)) { 691 final String ordinal = String.valueOf(i++); 692 final int maxNameLen = 255 - ordinal.length(); 693 nextName = (name.length() <= maxNameLen ? name : name.substring(0, maxNameLen)).concat(ordinal); 694 } 695 return nextName; 696 } 697 698 private void generateMethods() { 699 for(final MethodInfo mi: methodInfos) { 700 generateMethod(mi); 701 } 702 } 703 704 /** 705 * Generates a method in the adapter class that adapts a method from the original class. The generated methods will 706 * inspect the method handle field assigned to them. If it is null (the JS object doesn't provide an implementation 707 * for the method) then it will either invoke its version in the supertype, or if it is abstract, throw an 708 * {@link UnsupportedOperationException}. Otherwise, if the method handle field's value is not null, the handle is 709 * invoked using invokeExact (signature polymorphic invocation as per JLS 15.12.3). Before the invocation, the 710 * current Nashorn {@link Context} is checked, and if it is different than the global used to create the adapter 711 * instance, the creating global is set to be the current global. In this case, the previously current global is 712 * restored after the invocation. If invokeExact results in a Throwable that is not one of the method's declared 713 * exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime 714 * exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of 715 * the method; this is guaranteed by the way constructors of the adapter class obtain them using 716 * {@link #getHandle(Object, String, MethodType, boolean)}. 717 * @param mi the method info describing the method to be generated. 718 */ 719 private void generateMethod(final MethodInfo mi) { 720 final Method method = mi.method; 721 final Class<?>[] exceptions = method.getExceptionTypes(); 722 final String[] exceptionNames = getExceptionNames(exceptions); 723 final MethodType type = mi.type; 724 final String methodDesc = type.toMethodDescriptorString(); 725 final String name = mi.getName(); 726 727 final Type asmType = Type.getMethodType(methodDesc); 728 final Type[] asmArgTypes = asmType.getArgumentTypes(); 729 730 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), name, 731 methodDesc, null, exceptionNames)); 732 mv.visitCode(); 733 734 final Label handleDefined = new Label(); 735 736 final Class<?> returnType = type.returnType(); 737 final Type asmReturnType = Type.getType(returnType); 738 739 // See if we have overriding method handle defined 740 if(classOverride) { 741 mv.getstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); 742 } else { 743 mv.visitVarInsn(ALOAD, 0); 744 mv.getfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); 745 } 746 // stack: [handle] 747 mv.visitInsn(DUP); 748 mv.visitJumpInsn(IFNONNULL, handleDefined); 749 750 // No handle is available, fall back to default behavior 751 if(Modifier.isAbstract(method.getModifiers())) { 752 // If the super method is abstract, throw an exception 753 mv.anew(UNSUPPORTED_OPERATION_TYPE); 754 mv.dup(); 755 mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR, false); 756 mv.athrow(); 757 } else { 758 mv.visitInsn(POP); 759 // If the super method is not abstract, delegate to it. 760 emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc); 761 } 762 763 mv.visitLabel(handleDefined); 764 // Load the creatingGlobal object 765 if(classOverride) { 766 // If class handle is defined, load the static defining global 767 mv.getstatic(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR); 768 } else { 769 mv.visitVarInsn(ALOAD, 0); 770 mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR); 771 } 772 // stack: [creatingGlobal, handle] 773 final Label setupGlobal = new Label(); 774 mv.visitLabel(setupGlobal); 775 776 // Determine the first index for a local variable 777 int nextLocalVar = 1; // "this" is at 0 778 for(final Type t: asmArgTypes) { 779 nextLocalVar += t.getSize(); 780 } 781 // Set our local variable indices 782 final int currentGlobalVar = nextLocalVar++; 783 final int globalsDifferVar = nextLocalVar++; 784 785 mv.dup(); 786 // stack: [creatingGlobal, creatingGlobal, handle] 787 788 // Emit code for switching to the creating global 789 // Global currentGlobal = Context.getGlobal(); 790 invokeGetGlobal(mv); 791 mv.dup(); 792 793 mv.visitVarInsn(ASTORE, currentGlobalVar); 794 // stack: [currentGlobal, creatingGlobal, creatingGlobal, handle] 795 // if(definingGlobal == currentGlobal) { 796 final Label globalsDiffer = new Label(); 797 mv.ifacmpne(globalsDiffer); 798 // stack: [creatingGlobal, handle] 799 // globalsDiffer = false 800 mv.pop(); 801 // stack: [handle] 802 mv.iconst(0); // false 803 // stack: [false, handle] 804 final Label invokeHandle = new Label(); 805 mv.goTo(invokeHandle); 806 mv.visitLabel(globalsDiffer); 807 // } else { 808 // Context.setGlobal(definingGlobal); 809 // stack: [creatingGlobal, handle] 810 invokeSetGlobal(mv); 811 // stack: [handle] 812 // globalsDiffer = true 813 mv.iconst(1); 814 // stack: [true, handle] 815 816 mv.visitLabel(invokeHandle); 817 mv.visitVarInsn(ISTORE, globalsDifferVar); 818 // stack: [handle] 819 820 // Load all parameters back on stack for dynamic invocation. NOTE: since we're using a generic 821 // Object(Object, Object, ...) type signature for the method, we must box all arguments here. 822 int varOffset = 1; 823 for (final Type t : asmArgTypes) { 824 mv.load(varOffset, t); 825 boxStackTop(mv, t); 826 varOffset += t.getSize(); 827 } 828 829 // Invoke the target method handle 830 final Label tryBlockStart = new Label(); 831 mv.visitLabel(tryBlockStart); 832 emitInvokeExact(mv, type.generic()); 833 convertReturnValue(mv, returnType, asmReturnType); 834 final Label tryBlockEnd = new Label(); 835 mv.visitLabel(tryBlockEnd); 836 emitFinally(mv, currentGlobalVar, globalsDifferVar); 837 mv.areturn(asmReturnType); 838 839 // If Throwable is not declared, we need an adapter from Throwable to RuntimeException 840 final boolean throwableDeclared = isThrowableDeclared(exceptions); 841 final Label throwableHandler; 842 if (!throwableDeclared) { 843 // Add "throw new RuntimeException(Throwable)" handler for Throwable 844 throwableHandler = new Label(); 845 mv.visitLabel(throwableHandler); 846 mv.anew(RUNTIME_EXCEPTION_TYPE); 847 mv.dupX1(); 848 mv.swap(); 849 mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE), false); 850 // Fall through to rethrow handler 851 } else { 852 throwableHandler = null; 853 } 854 final Label rethrowHandler = new Label(); 855 mv.visitLabel(rethrowHandler); 856 // Rethrow handler for RuntimeException, Error, and all declared exception types 857 emitFinally(mv, currentGlobalVar, globalsDifferVar); 858 mv.athrow(); 859 final Label methodEnd = new Label(); 860 mv.visitLabel(methodEnd); 861 862 mv.visitLocalVariable("currentGlobal", GLOBAL_TYPE_DESCRIPTOR, null, setupGlobal, methodEnd, currentGlobalVar); 863 mv.visitLocalVariable("globalsDiffer", Type.BOOLEAN_TYPE.getDescriptor(), null, setupGlobal, methodEnd, globalsDifferVar); 864 865 if(throwableDeclared) { 866 mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME); 867 assert throwableHandler == null; 868 } else { 869 mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME); 870 mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME); 871 for(final String excName: exceptionNames) { 872 mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName); 873 } 874 mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME); 875 } 876 endMethod(mv); 877 } 878 879 private void convertReturnValue(final InstructionAdapter mv, final Class<?> returnType, final Type asmReturnType) { 880 switch(asmReturnType.getSort()) { 881 case Type.VOID: 882 mv.pop(); 883 break; 884 case Type.BOOLEAN: 885 JSType.TO_BOOLEAN.invoke(mv); 886 break; 887 case Type.BYTE: 888 JSType.TO_INT32.invoke(mv); 889 mv.visitInsn(Opcodes.I2B); 890 break; 891 case Type.SHORT: 892 JSType.TO_INT32.invoke(mv); 893 mv.visitInsn(Opcodes.I2S); 894 break; 895 case Type.CHAR: 896 // JSType doesn't have a TO_CHAR, so we have services supply us one. 897 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "toCharPrimitive", TO_CHAR_PRIMITIVE_METHOD_DESCRIPTOR, false); 898 break; 899 case Type.INT: 900 JSType.TO_INT32.invoke(mv); 901 break; 902 case Type.LONG: 903 JSType.TO_LONG.invoke(mv); 904 break; 905 case Type.FLOAT: 906 JSType.TO_NUMBER.invoke(mv); 907 mv.visitInsn(Opcodes.D2F); 908 break; 909 case Type.DOUBLE: 910 JSType.TO_NUMBER.invoke(mv); 911 break; 912 default: 913 if(asmReturnType.equals(OBJECT_TYPE)) { 914 // Must hide ConsString (and potentially other internal Nashorn types) from callers 915 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "exportReturnValue", EXPORT_RETURN_VALUE_METHOD_DESCRIPTOR, false); 916 } else if(asmReturnType.equals(STRING_TYPE)){ 917 // Well-known conversion to String. Not using the JSType one as we want to preserve null as null instead 918 // of the string "n,u,l,l". 919 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "toString", TO_STRING_METHOD_DESCRIPTOR, false); 920 } else { 921 // Invoke converter method handle for everything else. Note that we could have just added an asType or 922 // filterReturnValue to the invoked handle instead, but then every instance would have the function 923 // method handle wrapped in a separate converter method handle, making handle.invokeExact() megamorphic. 924 if(classOverride) { 925 mv.getstatic(generatedClassName, converterFields.get(returnType), METHOD_HANDLE_TYPE_DESCRIPTOR); 926 } else { 927 mv.visitVarInsn(ALOAD, 0); 928 mv.getfield(generatedClassName, converterFields.get(returnType), METHOD_HANDLE_TYPE_DESCRIPTOR); 929 } 930 mv.swap(); 931 emitInvokeExact(mv, MethodType.methodType(returnType, Object.class)); 932 } 933 } 934 } 935 936 private static void emitInvokeExact(final InstructionAdapter mv, final MethodType type) { 937 mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString(), false); 938 } 939 940 private static void boxStackTop(final InstructionAdapter mv, final Type t) { 941 switch(t.getSort()) { 942 case Type.BOOLEAN: 943 invokeValueOf(mv, "Boolean", 'Z'); 944 break; 945 case Type.BYTE: 946 case Type.SHORT: 947 case Type.INT: 948 // bytes and shorts get boxed as integers 949 invokeValueOf(mv, "Integer", 'I'); 950 break; 951 case Type.CHAR: 952 invokeValueOf(mv, "Character", 'C'); 953 break; 954 case Type.FLOAT: 955 // floats get boxed as doubles 956 mv.visitInsn(Opcodes.F2D); 957 invokeValueOf(mv, "Double", 'D'); 958 break; 959 case Type.LONG: 960 invokeValueOf(mv, "Long", 'J'); 961 break; 962 case Type.DOUBLE: 963 invokeValueOf(mv, "Double", 'D'); 964 break; 965 case Type.ARRAY: 966 case Type.METHOD: 967 // Already boxed 968 break; 969 case Type.OBJECT: 970 if(t.equals(OBJECT_TYPE)) { 971 mv.invokestatic(SCRIPTUTILS_TYPE_NAME, "unwrap", UNWRAP_METHOD_DESCRIPTOR, false); 972 } 973 break; 974 default: 975 // Not expecting anything else (e.g. VOID) 976 assert false; 977 break; 978 } 979 } 980 981 private static void invokeValueOf(final InstructionAdapter mv, final String boxedType, final char unboxedType) { 982 mv.invokestatic("java/lang/" + boxedType, "valueOf", "(" + unboxedType + ")Ljava/lang/" + boxedType + ";", false); 983 } 984 985 /** 986 * Emit code to restore the previous Nashorn Context when needed. 987 * @param mv the instruction adapter 988 * @param currentGlobalVar index of the local variable holding the reference to the current global at method 989 * entry. 990 * @param globalsDifferVar index of the boolean local variable that is true if the global needs to be restored. 991 */ 992 private static void emitFinally(final InstructionAdapter mv, final int currentGlobalVar, final int globalsDifferVar) { 993 // Emit code to restore the previous Nashorn global if needed 994 mv.visitVarInsn(ILOAD, globalsDifferVar); 995 final Label skip = new Label(); 996 mv.ifeq(skip); 997 mv.visitVarInsn(ALOAD, currentGlobalVar); 998 invokeSetGlobal(mv); 999 mv.visitLabel(skip); 1000 } 1001 1002 private static boolean isThrowableDeclared(final Class<?>[] exceptions) { 1003 for (final Class<?> exception : exceptions) { 1004 if (exception == Throwable.class) { 1005 return true; 1006 } 1007 } 1008 return false; 1009 } 1010 1011 private void generateSuperMethods() { 1012 for(final MethodInfo mi: methodInfos) { 1013 if(!Modifier.isAbstract(mi.method.getModifiers())) { 1014 generateSuperMethod(mi); 1015 } 1016 } 1017 } 1018 1019 private void generateSuperMethod(final MethodInfo mi) { 1020 final Method method = mi.method; 1021 1022 final String methodDesc = mi.type.toMethodDescriptorString(); 1023 final String name = mi.getName(); 1024 1025 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), 1026 SUPER_PREFIX + name, methodDesc, null, getExceptionNames(method.getExceptionTypes()))); 1027 mv.visitCode(); 1028 1029 emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc); 1030 1031 endMethod(mv); 1032 } 1033 1034 private void emitSuperCall(final InstructionAdapter mv, final Class<?> owner, final String name, final String methodDesc) { 1035 mv.visitVarInsn(ALOAD, 0); 1036 int nextParam = 1; 1037 final Type methodType = Type.getMethodType(methodDesc); 1038 for(final Type t: methodType.getArgumentTypes()) { 1039 mv.load(nextParam, t); 1040 nextParam += t.getSize(); 1041 } 1042 1043 // default method - non-abstract, interface method 1044 if (Modifier.isInterface(owner.getModifiers())) { 1045 mv.invokespecial(Type.getInternalName(owner), name, methodDesc, false); 1046 } else { 1047 mv.invokespecial(superClassName, name, methodDesc, false); 1048 } 1049 mv.areturn(methodType.getReturnType()); 1050 } 1051 1052 private void generateFinalizerMethods() { 1053 final String finalizerDelegateName = nextName("access$"); 1054 generateFinalizerDelegate(finalizerDelegateName); 1055 generateFinalizerOverride(finalizerDelegateName); 1056 } 1057 1058 private void generateFinalizerDelegate(final String finalizerDelegateName) { 1059 // Generate a delegate that will be invoked from the no-permission trampoline. Note it can be private, as we'll 1060 // refer to it with a MethodHandle constant pool entry in the overridden finalize() method (see 1061 // generateFinalizerOverride()). 1062 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PRIVATE | ACC_STATIC, 1063 finalizerDelegateName, Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE), null, null)); 1064 1065 // Simply invoke super.finalize() 1066 mv.visitVarInsn(ALOAD, 0); 1067 mv.checkcast(Type.getType(generatedClassName)); 1068 mv.invokespecial(superClassName, "finalize", Type.getMethodDescriptor(Type.VOID_TYPE), false); 1069 1070 mv.visitInsn(RETURN); 1071 endMethod(mv); 1072 } 1073 1074 private void generateFinalizerOverride(final String finalizerDelegateName) { 1075 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, "finalize", 1076 VOID_NOARG_METHOD_DESCRIPTOR, null, null)); 1077 // Overridden finalizer will take a MethodHandle to the finalizer delegating method, ... 1078 mv.aconst(new Handle(Opcodes.H_INVOKESTATIC, generatedClassName, finalizerDelegateName, 1079 Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE))); 1080 mv.visitVarInsn(ALOAD, 0); 1081 // ...and invoke it through JavaAdapterServices.invokeNoPermissions 1082 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "invokeNoPermissions", 1083 Type.getMethodDescriptor(METHOD_HANDLE_TYPE, OBJECT_TYPE), false); 1084 mv.visitInsn(RETURN); 1085 endMethod(mv); 1086 } 1087 1088 private static String[] getExceptionNames(final Class<?>[] exceptions) { 1089 final String[] exceptionNames = new String[exceptions.length]; 1090 for (int i = 0; i < exceptions.length; ++i) { 1091 exceptionNames[i] = Type.getInternalName(exceptions[i]); 1092 } 1093 return exceptionNames; 1094 } 1095 1096 private static int getAccessModifiers(final Method method) { 1097 return ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0); 1098 } 1099 1100 /** 1101 * Gathers methods that can be implemented or overridden from the specified type into this factory's 1102 * {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from 1103 * the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its 1104 * superclass and the interfaces it implements, and add further methods that were not directly declared on the 1105 * class. 1106 * @param type the type defining the methods. 1107 */ 1108 private void gatherMethods(final Class<?> type) throws AdaptationException { 1109 if (Modifier.isPublic(type.getModifiers())) { 1110 final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods(); 1111 1112 for (final Method typeMethod: typeMethods) { 1113 final String name = typeMethod.getName(); 1114 if(name.startsWith(SUPER_PREFIX)) { 1115 continue; 1116 } 1117 final int m = typeMethod.getModifiers(); 1118 if (Modifier.isStatic(m)) { 1119 continue; 1120 } 1121 if (Modifier.isPublic(m) || Modifier.isProtected(m)) { 1122 // Is it a "finalize()"? 1123 if(name.equals("finalize") && typeMethod.getParameterCount() == 0) { 1124 if(type != Object.class) { 1125 hasExplicitFinalizer = true; 1126 if(Modifier.isFinal(m)) { 1127 // Must be able to override an explicit finalizer 1128 throw new AdaptationException(Outcome.ERROR_FINAL_FINALIZER, type.getCanonicalName()); 1129 } 1130 } 1131 continue; 1132 } 1133 1134 final MethodInfo mi = new MethodInfo(typeMethod); 1135 if (Modifier.isFinal(m) || isCallerSensitive(typeMethod)) { 1136 finalMethods.add(mi); 1137 } else if (!finalMethods.contains(mi) && methodInfos.add(mi)) { 1138 if (Modifier.isAbstract(m)) { 1139 abstractMethodNames.add(mi.getName()); 1140 } 1141 mi.setIsCanonical(this); 1142 } 1143 } 1144 } 1145 } 1146 // If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done. 1147 // Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to 1148 // see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a 1149 // superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and 1150 // getMethods() does provide those declared in a superinterface. 1151 if (!type.isInterface()) { 1152 final Class<?> superType = type.getSuperclass(); 1153 if (superType != null) { 1154 gatherMethods(superType); 1155 } 1156 for (final Class<?> itf: type.getInterfaces()) { 1157 gatherMethods(itf); 1158 } 1159 } 1160 } 1161 1162 private void gatherMethods(final List<Class<?>> classes) throws AdaptationException { 1163 for(final Class<?> c: classes) { 1164 gatherMethods(c); 1165 } 1166 } 1167 1168 private static final AccessControlContext GET_DECLARED_MEMBERS_ACC_CTXT = ClassAndLoader.createPermAccCtxt("accessDeclaredMembers"); 1169 1170 /** 1171 * Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters, 1172 * as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and 1173 * {@code Object.clone()}. 1174 * @return a collection of method infos representing those methods that we never override in adapter classes. 1175 */ 1176 private static Collection<MethodInfo> getExcludedMethods() { 1177 return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() { 1178 @Override 1179 public Collection<MethodInfo> run() { 1180 try { 1181 return Arrays.asList( 1182 new MethodInfo(Object.class, "finalize"), 1183 new MethodInfo(Object.class, "clone")); 1184 } catch (final NoSuchMethodException e) { 1185 throw new AssertionError(e); 1186 } 1187 } 1188 }, GET_DECLARED_MEMBERS_ACC_CTXT); 1189 } 1190 1191 private String getCommonSuperClass(final String type1, final String type2) { 1192 try { 1193 final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader); 1194 final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader); 1195 if (c1.isAssignableFrom(c2)) { 1196 return type1; 1197 } 1198 if (c2.isAssignableFrom(c1)) { 1199 return type2; 1200 } 1201 if (c1.isInterface() || c2.isInterface()) { 1202 return OBJECT_TYPE_NAME; 1203 } 1204 return assignableSuperClass(c1, c2).getName().replace('.', '/'); 1205 } catch(final ClassNotFoundException e) { 1206 throw new RuntimeException(e); 1207 } 1208 } 1209 1210 private static Class<?> assignableSuperClass(final Class<?> c1, final Class<?> c2) { 1211 final Class<?> superClass = c1.getSuperclass(); 1212 return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2); 1213 } 1214 1215 private static boolean isCallerSensitive(final AccessibleObject e) { 1216 return e.isAnnotationPresent(CallerSensitive.class); 1217 } 1218} 1219