JavaAdapterBytecodeGenerator.java revision 953:221a84ef44c0
1/* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.nashorn.internal.runtime.linker; 27 28import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 29import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; 30import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 31import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 32import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; 33import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS; 34import static jdk.internal.org.objectweb.asm.Opcodes.ACONST_NULL; 35import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; 36import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE; 37import static jdk.internal.org.objectweb.asm.Opcodes.DUP; 38import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL; 39import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; 40import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE; 41import static jdk.internal.org.objectweb.asm.Opcodes.POP; 42import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 43import static jdk.nashorn.internal.lookup.Lookup.MH; 44import static jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR; 45 46import java.lang.invoke.MethodHandle; 47import java.lang.invoke.MethodType; 48import java.lang.reflect.AccessibleObject; 49import java.lang.reflect.Constructor; 50import java.lang.reflect.Method; 51import java.lang.reflect.Modifier; 52import java.security.AccessControlContext; 53import java.security.AccessController; 54import java.security.PrivilegedAction; 55import java.util.Arrays; 56import java.util.Collection; 57import java.util.HashSet; 58import java.util.Iterator; 59import java.util.LinkedHashMap; 60import java.util.List; 61import java.util.Map; 62import java.util.Set; 63import jdk.internal.org.objectweb.asm.ClassWriter; 64import jdk.internal.org.objectweb.asm.Handle; 65import jdk.internal.org.objectweb.asm.Label; 66import jdk.internal.org.objectweb.asm.Opcodes; 67import jdk.internal.org.objectweb.asm.Type; 68import jdk.internal.org.objectweb.asm.commons.InstructionAdapter; 69import jdk.nashorn.internal.runtime.Context; 70import jdk.nashorn.internal.runtime.JSType; 71import jdk.nashorn.internal.runtime.ScriptFunction; 72import jdk.nashorn.internal.runtime.ScriptObject; 73import jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome; 74import sun.reflect.CallerSensitive; 75 76/** 77 * Generates bytecode for a Java adapter class. Used by the {@link JavaAdapterFactory}. 78 * </p><p> 79 * For every protected or public constructor in the extended class, the adapter class will have either one or two 80 * public constructors (visibility of protected constructors in the extended class is promoted to public). 81 * <li> 82 * <li>For adapter classes with instance-level overrides, a constructor taking a trailing ScriptObject argument preceded 83 * by original constructor arguments is always created on the adapter class. When such a constructor is invoked, the 84 * passed ScriptObject's member functions are used to implement and/or override methods on the original class, 85 * dispatched by name. A single JavaScript function will act as the implementation for all overloaded methods of the 86 * same name. When methods on an adapter instance are invoked, the functions are invoked having the ScriptObject passed 87 * in the instance constructor as their "this". Subsequent changes to the ScriptObject (reassignment or removal of its 88 * functions) are not reflected in the adapter instance; the method implementations are bound to functions at 89 * constructor invocation time. 90 * {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The 91 * only restriction is that since every JavaScript object already has a {@code toString} function through the 92 * {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a 93 * {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be 94 * implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too. 95 * </li> 96 * <li> 97 * If the original types collectively have only one abstract method, or have several of them, but all share the 98 * same name, an additional constructor for instance-level override adapter is provided for every original constructor; 99 * this one takes a ScriptFunction as its last argument preceded by original constructor arguments. This constructor 100 * will use the passed function as the implementation for all abstract methods. For consistency, any concrete methods 101 * sharing the single abstract method name will also be overridden by the function. When methods on the adapter instance 102 * are invoked, the ScriptFunction is invoked with UNDEFINED or Global as its "this" depending whether the function is 103 * strict or not. 104 * </li> 105 * <li> 106 * If the adapter being generated can have class-level overrides, constructors taking same arguments as the superclass 107 * constructors are created. These constructors simply delegate to the superclass constructor. They are simply used to 108 * create instances of the adapter class, with no instance-level overrides, as they don't have them. 109 * </li> 110 * </ul> 111 * </p><p> 112 * For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect 113 * to coerce the JavaScript function return value to the expected Java return type. 114 * </p><p> 115 * Since we are adding a trailing argument to the generated constructors in the adapter class, they will never be 116 * declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The 117 * reason we are passing the additional argument at the end of the argument list instead at the front is that the 118 * source-level script expression <code>new X(a, b) { ... }</code> (which is a proprietary syntax extension Nashorn uses 119 * to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>. 120 * </p><p> 121 * It is possible to create two different adapter classes: those that can have class-level overrides, and those that can 122 * have instance-level overrides. When {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject)} is invoked 123 * with non-null {@code classOverrides} parameter, an adapter class is created that can have class-level overrides, and 124 * the passed script object will be used as the implementations for its methods, just as in the above case of the 125 * constructor taking a script object. Note that in the case of class-level overrides, a new adapter class is created on 126 * every invocation, and the implementation object is bound to the class, not to any instance. All created instances 127 * will share these functions. If it is required to have both class-level overrides and instance-level overrides, the 128 * class-level override adapter class should be subclassed with an instance-override adapter. Since adapters delegate to 129 * super class when an overriding method handle is not specified, this will behave as expected. It is not possible to 130 * have both class-level and instance-level overrides in the same class for security reasons: adapter classes are 131 * defined with a protection domain of their creator code, and an adapter class that has both class and instance level 132 * overrides would need to have two potentially different protection domains: one for class-based behavior and one for 133 * instance-based behavior; since Java classes can only belong to a single protection domain, this could not be 134 * implemented securely. 135 */ 136final class JavaAdapterBytecodeGenerator { 137 private static final Type OBJECT_TYPE = Type.getType(Object.class); 138 private static final Type CLASS_TYPE = Type.getType(Class.class); 139 140 static final String OBJECT_TYPE_NAME = OBJECT_TYPE.getInternalName(); 141 142 static final String INIT = "<init>"; 143 144 static final String GLOBAL_FIELD_NAME = "global"; 145 146 // "global" is declared as Object instead of Global - avoid static references to internal Nashorn classes when possible. 147 static final String GLOBAL_TYPE_DESCRIPTOR = OBJECT_TYPE.getDescriptor(); 148 149 static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE); 150 static final String VOID_NOARG_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE); 151 152 private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class); 153 private static final Type STRING_TYPE = Type.getType(String.class); 154 private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class); 155 private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class); 156 private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE, 157 OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE); 158 private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE, 159 SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE); 160 private static final String GET_CLASS_INITIALIZER_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE); 161 private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class); 162 private static final Type THROWABLE_TYPE = Type.getType(Throwable.class); 163 private static final Type UNSUPPORTED_OPERATION_TYPE = Type.getType(UnsupportedOperationException.class); 164 165 private static final String SERVICES_CLASS_TYPE_NAME = Type.getInternalName(JavaAdapterServices.class); 166 private static final String RUNTIME_EXCEPTION_TYPE_NAME = RUNTIME_EXCEPTION_TYPE.getInternalName(); 167 private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class); 168 private static final String THROWABLE_TYPE_NAME = THROWABLE_TYPE.getInternalName(); 169 private static final String UNSUPPORTED_OPERATION_TYPE_NAME = UNSUPPORTED_OPERATION_TYPE.getInternalName(); 170 171 private static final String METHOD_HANDLE_TYPE_DESCRIPTOR = METHOD_HANDLE_TYPE.getDescriptor(); 172 private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE); 173 private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(CLASS_TYPE); 174 private static final String EXPORT_RETURN_VALUE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, OBJECT_TYPE); 175 private static final String GET_CONVERTER_METHOD_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE, CLASS_TYPE); 176 private static final String TO_CHAR_PRIMITIVE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.CHAR_TYPE, OBJECT_TYPE); 177 private static final String TO_STRING_METHOD_DESCRIPTOR = Type.getMethodDescriptor(STRING_TYPE, OBJECT_TYPE); 178 179 // Package used when the adapter can't be defined in the adaptee's package (either because it's sealed, or because 180 // it's a java.* package. 181 private static final String ADAPTER_PACKAGE_PREFIX = "jdk/nashorn/javaadapters/"; 182 // Class name suffix used to append to the adaptee class name, when it can be defined in the adaptee's package. 183 private static final String ADAPTER_CLASS_NAME_SUFFIX = "$$NashornJavaAdapter"; 184 private static final String JAVA_PACKAGE_PREFIX = "java/"; 185 private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 255; 186 187 private static final String CLASS_INIT = "<clinit>"; 188 static final String CONVERTER_INIT = "<converter-init>"; 189 190 // Method name prefix for invoking super-methods 191 static final String SUPER_PREFIX = "super$"; 192 193 /** 194 * Collection of methods we never override: Object.clone(), Object.finalize(). 195 */ 196 private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods(); 197 198 // This is the superclass for our generated adapter. 199 private final Class<?> superClass; 200 // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class 201 // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the 202 // Nashorn classes. 203 private final ClassLoader commonLoader; 204 // Is this a generator for the version of the class that can have overrides on the class level? 205 private final boolean classOverride; 206 // Binary name of the superClass 207 private final String superClassName; 208 // Binary name of the generated class. 209 private final String generatedClassName; 210 private final Set<String> usedFieldNames = new HashSet<>(); 211 private final Set<String> abstractMethodNames = new HashSet<>(); 212 private final String samName; 213 private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED); 214 private final Set<MethodInfo> methodInfos = new HashSet<>(); 215 private boolean autoConvertibleFromFunction = false; 216 private boolean hasExplicitFinalizer = false; 217 218 /** 219 * Names of static fields holding type converter method handles for return value conversion. We are emitting code 220 * for invoking these explicitly after the delegate handle is invoked, instead of doing an asType or 221 * filterReturnValue on the delegate handle, as that would create a new converter handle wrapping the function's 222 * handle for every instance of the adapter, causing the handle.invokeExact() call sites to become megamorphic. 223 */ 224 private final Map<Class<?>, String> converterFields = new LinkedHashMap<>(); 225 226 /** 227 * Subset of possible return types for all methods; namely, all possible return types of the SAM methods (we 228 * identify SAM types by having all of their abstract methods share a single name, so there can be multiple 229 * overloads with multiple return types. We use this set when emitting the constructor taking a ScriptFunction (the 230 * SAM initializer) to avoid populating converter fields that will never be used by SAM methods. 231 */ 232 private final Set<Class<?>> samReturnTypes = new HashSet<>(); 233 234 private final ClassWriter cw; 235 236 /** 237 * Creates a generator for the bytecode for the adapter for the specified superclass and interfaces. 238 * @param superClass the superclass the adapter will extend. 239 * @param interfaces the interfaces the adapter will implement. 240 * @param commonLoader the class loader that can see all of superClass, interfaces, and Nashorn classes. 241 * @param classOverride true to generate the bytecode for the adapter that has class-level overrides, false to 242 * generate the bytecode for the adapter that has instance-level overrides. 243 * @throws AdaptationException if the adapter can not be generated for some reason. 244 */ 245 JavaAdapterBytecodeGenerator(final Class<?> superClass, final List<Class<?>> interfaces, 246 final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException { 247 assert superClass != null && !superClass.isInterface(); 248 assert interfaces != null; 249 250 this.superClass = superClass; 251 this.classOverride = classOverride; 252 this.commonLoader = commonLoader; 253 cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { 254 @Override 255 protected String getCommonSuperClass(final String type1, final String type2) { 256 // We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class 257 // loader to find the common superclass of two types when needed. 258 return JavaAdapterBytecodeGenerator.this.getCommonSuperClass(type1, type2); 259 } 260 }; 261 superClassName = Type.getInternalName(superClass); 262 generatedClassName = getGeneratedClassName(superClass, interfaces); 263 264 cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER, generatedClassName, null, superClassName, getInternalTypeNames(interfaces)); 265 generateGlobalFields(); 266 267 gatherMethods(superClass); 268 gatherMethods(interfaces); 269 samName = abstractMethodNames.size() == 1 ? abstractMethodNames.iterator().next() : null; 270 generateHandleFields(); 271 generateConverterFields(); 272 if(classOverride) { 273 generateClassInit(); 274 } 275 generateConstructors(); 276 generateMethods(); 277 generateSuperMethods(); 278 if (hasExplicitFinalizer) { 279 generateFinalizerMethods(); 280 } 281 // } 282 cw.visitEnd(); 283 } 284 285 private void generateGlobalFields() { 286 cw.visitField(ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0), GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR, null, null).visitEnd(); 287 usedFieldNames.add(GLOBAL_FIELD_NAME); 288 } 289 290 JavaAdapterClassLoader createAdapterClassLoader() { 291 return new JavaAdapterClassLoader(generatedClassName, cw.toByteArray()); 292 } 293 294 boolean isAutoConvertibleFromFunction() { 295 return autoConvertibleFromFunction; 296 } 297 298 private static String getGeneratedClassName(final Class<?> superType, final List<Class<?>> interfaces) { 299 // The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're 300 // just implementing interfaces or extending Object), then the first implemented interface or Object. 301 final Class<?> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType; 302 final Package pkg = namingType.getPackage(); 303 final String namingTypeName = Type.getInternalName(namingType); 304 final StringBuilder buf = new StringBuilder(); 305 if (namingTypeName.startsWith(JAVA_PACKAGE_PREFIX) || pkg == null || pkg.isSealed()) { 306 // Can't define new classes in java.* packages 307 buf.append(ADAPTER_PACKAGE_PREFIX).append(namingTypeName); 308 } else { 309 buf.append(namingTypeName).append(ADAPTER_CLASS_NAME_SUFFIX); 310 } 311 final Iterator<Class<?>> it = interfaces.iterator(); 312 if(superType == Object.class && it.hasNext()) { 313 it.next(); // Skip first interface, it was used to primarily name the adapter 314 } 315 // Append interface names to the adapter name 316 while(it.hasNext()) { 317 buf.append("$$").append(it.next().getSimpleName()); 318 } 319 return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length())); 320 } 321 322 /** 323 * Given a list of class objects, return an array with their binary names. Used to generate the array of interface 324 * names to implement. 325 * @param classes the classes 326 * @return an array of names 327 */ 328 private static String[] getInternalTypeNames(final List<Class<?>> classes) { 329 final int interfaceCount = classes.size(); 330 final String[] interfaceNames = new String[interfaceCount]; 331 for(int i = 0; i < interfaceCount; ++i) { 332 interfaceNames[i] = Type.getInternalName(classes.get(i)); 333 } 334 return interfaceNames; 335 } 336 337 private void generateHandleFields() { 338 final int flags = ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0); 339 for (final MethodInfo mi: methodInfos) { 340 cw.visitField(flags, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd(); 341 } 342 } 343 344 private void generateConverterFields() { 345 final int flags = ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0); 346 for (final MethodInfo mi: methodInfos) { 347 final Class<?> returnType = mi.type.returnType(); 348 // Handle primitive types, Object, and String specially 349 if(!returnType.isPrimitive() && returnType != Object.class && returnType != String.class) { 350 if(!converterFields.containsKey(returnType)) { 351 final String name = nextName("convert"); 352 converterFields.put(returnType, name); 353 if(mi.getName().equals(samName)) { 354 samReturnTypes.add(returnType); 355 } 356 cw.visitField(flags, name, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd(); 357 } 358 } 359 } 360 } 361 362 private void generateClassInit() { 363 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_STATIC, CLASS_INIT, 364 Type.getMethodDescriptor(Type.VOID_TYPE), null, null)); 365 366 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getClassOverrides", GET_CLASS_INITIALIZER_DESCRIPTOR, false); 367 final Label initGlobal; 368 if(samName != null) { 369 // If the class is a SAM, allow having a ScriptFunction passed as class overrides 370 final Label notAFunction = new Label(); 371 mv.dup(); 372 mv.instanceOf(SCRIPT_FUNCTION_TYPE); 373 mv.ifeq(notAFunction); 374 mv.checkcast(SCRIPT_FUNCTION_TYPE); 375 376 // Assign MethodHandle fields through invoking getHandle() for a ScriptFunction, only assigning the SAM 377 // method(s). 378 for (final MethodInfo mi : methodInfos) { 379 if(mi.getName().equals(samName)) { 380 mv.dup(); 381 loadMethodTypeAndGetHandle(mv, mi, GET_HANDLE_FUNCTION_DESCRIPTOR); 382 } else { 383 mv.visitInsn(ACONST_NULL); 384 } 385 mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); 386 } 387 initGlobal = new Label(); 388 mv.goTo(initGlobal); 389 mv.visitLabel(notAFunction); 390 } else { 391 initGlobal = null; 392 } 393 // Assign MethodHandle fields through invoking getHandle() for a ScriptObject 394 for (final MethodInfo mi : methodInfos) { 395 mv.dup(); 396 mv.aconst(mi.getName()); 397 loadMethodTypeAndGetHandle(mv, mi, GET_HANDLE_OBJECT_DESCRIPTOR); 398 mv.putstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); 399 } 400 401 if(initGlobal != null) { 402 mv.visitLabel(initGlobal); 403 } 404 // Assign "global = Context.getGlobal()" 405 invokeGetGlobalWithNullCheck(mv); 406 mv.putstatic(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR); 407 408 generateConverterInit(mv, false); 409 endInitMethod(mv); 410 } 411 412 private void generateConverterInit(final InstructionAdapter mv, final boolean samOnly) { 413 assert !samOnly || !classOverride; 414 for(final Map.Entry<Class<?>, String> converterField: converterFields.entrySet()) { 415 final Class<?> returnType = converterField.getKey(); 416 if(!classOverride) { 417 mv.visitVarInsn(ALOAD, 0); 418 } 419 420 if(samOnly && !samReturnTypes.contains(returnType)) { 421 mv.visitInsn(ACONST_NULL); 422 } else { 423 mv.aconst(Type.getType(converterField.getKey())); 424 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getObjectConverter", GET_CONVERTER_METHOD_DESCRIPTOR, false); 425 } 426 427 if(classOverride) { 428 mv.putstatic(generatedClassName, converterField.getValue(), METHOD_HANDLE_TYPE_DESCRIPTOR); 429 } else { 430 mv.putfield(generatedClassName, converterField.getValue(), METHOD_HANDLE_TYPE_DESCRIPTOR); 431 } 432 } 433 } 434 435 private static void loadMethodTypeAndGetHandle(final InstructionAdapter mv, final MethodInfo mi, final String getHandleDescriptor) { 436 // NOTE: we're using generic() here because we'll be linking to the "generic" invoker version of 437 // the functions anyway, so we cut down on megamorphism in the invokeExact() calls in adapter 438 // bodies. Once we start linking to type-specializing invokers, this should be changed. 439 mv.aconst(Type.getMethodType(mi.type.generic().toMethodDescriptorString())); 440 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor, false); 441 } 442 443 private static void invokeGetGlobalWithNullCheck(final InstructionAdapter mv) { 444 invokeGetGlobal(mv); 445 mv.dup(); 446 mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR, false); // check against null Context 447 mv.pop(); 448 } 449 450 private void generateConstructors() throws AdaptationException { 451 boolean gotCtor = false; 452 for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) { 453 final int modifier = ctor.getModifiers(); 454 if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0 && !isCallerSensitive(ctor)) { 455 generateConstructors(ctor); 456 gotCtor = true; 457 } 458 } 459 if(!gotCtor) { 460 throw new AdaptationException(ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName()); 461 } 462 } 463 464 private void generateConstructors(final Constructor<?> ctor) { 465 if(classOverride) { 466 // Generate a constructor that just delegates to ctor. This is used with class-level overrides, when we want 467 // to create instances without further per-instance overrides. 468 generateDelegatingConstructor(ctor); 469 } else { 470 // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the 471 // beginning of its parameter list. 472 generateOverridingConstructor(ctor, false); 473 474 if (samName != null) { 475 if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) { 476 // If the original type only has a single abstract method name, as well as a default ctor, then it can 477 // be automatically converted from JS function. 478 autoConvertibleFromFunction = true; 479 } 480 // If all our abstract methods have a single name, generate an additional constructor, one that takes a 481 // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods. 482 generateOverridingConstructor(ctor, true); 483 } 484 } 485 } 486 487 private void generateDelegatingConstructor(final Constructor<?> ctor) { 488 final Type originalCtorType = Type.getType(ctor); 489 final Type[] argTypes = originalCtorType.getArgumentTypes(); 490 491 // All constructors must be public, even if in the superclass they were protected. 492 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT, 493 Type.getMethodDescriptor(originalCtorType.getReturnType(), argTypes), null, null)); 494 495 mv.visitCode(); 496 // Invoke super constructor with the same arguments. 497 mv.visitVarInsn(ALOAD, 0); 498 int offset = 1; // First arg is at position 1, after this. 499 for (final Type argType: argTypes) { 500 mv.load(offset, argType); 501 offset += argType.getSize(); 502 } 503 mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor(), false); 504 505 endInitMethod(mv); 506 } 507 508 /** 509 * Generates a constructor for the instance adapter class. This constructor will take the same arguments as the supertype 510 * constructor passed as the argument here, and delegate to it. However, it will take an additional argument of 511 * either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize 512 * all the method handle fields of the adapter instance with functions from the script object (or the script 513 * function itself, if that's what's passed). There is one method handle field in the adapter class for every method 514 * that can be implemented or overridden; the name of every field is same as the name of the method, with a number 515 * suffix that makes it unique in case of overloaded methods. The generated constructor will invoke 516 * {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType, 517 * boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity 518 * adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}. 519 * The constructor that takes a script function will only initialize the methods with the same name as the single 520 * abstract method. The constructor will also store the Nashorn global that was current at the constructor 521 * invocation time in a field named "global". The generated constructor will be public, regardless of whether the 522 * supertype constructor was public or protected. The generated constructor will not be variable arity, even if the 523 * supertype constructor was. 524 * @param ctor the supertype constructor that is serving as the base for the generated constructor. 525 * @param fromFunction true if we're generating a constructor that initializes SAM types from a single 526 * ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a 527 * ScriptObject passed to it. 528 */ 529 private void generateOverridingConstructor(final Constructor<?> ctor, final boolean fromFunction) { 530 final Type originalCtorType = Type.getType(ctor); 531 final Type[] originalArgTypes = originalCtorType.getArgumentTypes(); 532 final int argLen = originalArgTypes.length; 533 final Type[] newArgTypes = new Type[argLen + 1]; 534 535 // Insert ScriptFunction|Object as the last argument to the constructor 536 final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : OBJECT_TYPE; 537 newArgTypes[argLen] = extraArgumentType; 538 System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen); 539 540 // All constructors must be public, even if in the superclass they were protected. 541 // Existing super constructor <init>(this, args...) triggers generating <init>(this, scriptObj, args...). 542 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT, 543 Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null)); 544 545 mv.visitCode(); 546 // First, invoke super constructor with original arguments. If the form of the constructor we're generating is 547 // <init>(this, args..., scriptFn), then we're invoking super.<init>(this, args...). 548 mv.visitVarInsn(ALOAD, 0); 549 final Class<?>[] argTypes = ctor.getParameterTypes(); 550 int offset = 1; // First arg is at position 1, after this. 551 for (int i = 0; i < argLen; ++i) { 552 final Type argType = Type.getType(argTypes[i]); 553 mv.load(offset, argType); 554 offset += argType.getSize(); 555 } 556 mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor(), false); 557 558 // Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method. 559 final String getHandleDescriptor = fromFunction ? GET_HANDLE_FUNCTION_DESCRIPTOR : GET_HANDLE_OBJECT_DESCRIPTOR; 560 561 // Assign MethodHandle fields through invoking getHandle() 562 for (final MethodInfo mi : methodInfos) { 563 mv.visitVarInsn(ALOAD, 0); 564 if (fromFunction && !mi.getName().equals(samName)) { 565 // Constructors initializing from a ScriptFunction only initialize methods with the SAM name. 566 // NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This 567 // is a deliberate design choice. All other method handles are initialized to null. 568 mv.visitInsn(ACONST_NULL); 569 } else { 570 mv.visitVarInsn(ALOAD, offset); 571 if(!fromFunction) { 572 mv.aconst(mi.getName()); 573 } 574 loadMethodTypeAndGetHandle(mv, mi, getHandleDescriptor); 575 } 576 mv.putfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); 577 } 578 579 // Assign "this.global = Context.getGlobal()" 580 mv.visitVarInsn(ALOAD, 0); 581 invokeGetGlobalWithNullCheck(mv); 582 mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR); 583 584 // Initialize converters 585 generateConverterInit(mv, fromFunction); 586 endInitMethod(mv); 587 } 588 589 private static void endInitMethod(final InstructionAdapter mv) { 590 mv.visitInsn(RETURN); 591 endMethod(mv); 592 } 593 594 private static void endMethod(final InstructionAdapter mv) { 595 mv.visitMaxs(0, 0); 596 mv.visitEnd(); 597 } 598 599 private static void invokeGetGlobal(final InstructionAdapter mv) { 600 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR, false); 601 } 602 603 private static void invokeSetGlobal(final InstructionAdapter mv) { 604 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR, false); 605 } 606 607 /** 608 * Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the 609 * reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the 610 * method handle serving as the implementation of this method in adapter instances. 611 * 612 */ 613 private static class MethodInfo { 614 private final Method method; 615 private final MethodType type; 616 private String methodHandleFieldName; 617 618 private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException { 619 this(clazz.getDeclaredMethod(name, argTypes)); 620 } 621 622 private MethodInfo(final Method method) { 623 this.method = method; 624 this.type = MH.type(method.getReturnType(), method.getParameterTypes()); 625 } 626 627 @Override 628 public boolean equals(final Object obj) { 629 return obj instanceof MethodInfo && equals((MethodInfo)obj); 630 } 631 632 private boolean equals(final MethodInfo other) { 633 // Only method name and type are used for comparison; method handle field name is not. 634 return getName().equals(other.getName()) && type.equals(other.type); 635 } 636 637 String getName() { 638 return method.getName(); 639 } 640 641 @Override 642 public int hashCode() { 643 return getName().hashCode() ^ type.hashCode(); 644 } 645 646 void setIsCanonical(final JavaAdapterBytecodeGenerator self) { 647 methodHandleFieldName = self.nextName(getName()); 648 } 649 } 650 651 private String nextName(final String name) { 652 int i = 0; 653 String nextName = name; 654 while (!usedFieldNames.add(nextName)) { 655 final String ordinal = String.valueOf(i++); 656 final int maxNameLen = 255 - ordinal.length(); 657 nextName = (name.length() <= maxNameLen ? name : name.substring(0, maxNameLen)).concat(ordinal); 658 } 659 return nextName; 660 } 661 662 private void generateMethods() { 663 for(final MethodInfo mi: methodInfos) { 664 generateMethod(mi); 665 } 666 } 667 668 /** 669 * Generates a method in the adapter class that adapts a method from the original class. The generated methods will 670 * inspect the method handle field assigned to them. If it is null (the JS object doesn't provide an implementation 671 * for the method) then it will either invoke its version in the supertype, or if it is abstract, throw an 672 * {@link UnsupportedOperationException}. Otherwise, if the method handle field's value is not null, the handle is 673 * invoked using invokeExact (signature polymorphic invocation as per JLS 15.12.3). Before the invocation, the 674 * current Nashorn {@link Context} is checked, and if it is different than the global used to create the adapter 675 * instance, the creating global is set to be the current global. In this case, the previously current global is 676 * restored after the invocation. If invokeExact results in a Throwable that is not one of the method's declared 677 * exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime 678 * exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of 679 * the method; this is guaranteed by the way constructors of the adapter class obtain them using 680 * {@link #getHandle(Object, String, MethodType, boolean)}. 681 * @param mi the method info describing the method to be generated. 682 */ 683 private void generateMethod(final MethodInfo mi) { 684 final Method method = mi.method; 685 final Class<?>[] exceptions = method.getExceptionTypes(); 686 final String[] exceptionNames = getExceptionNames(exceptions); 687 final MethodType type = mi.type; 688 final String methodDesc = type.toMethodDescriptorString(); 689 final String name = mi.getName(); 690 691 final Type asmType = Type.getMethodType(methodDesc); 692 final Type[] asmArgTypes = asmType.getArgumentTypes(); 693 694 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), name, 695 methodDesc, null, exceptionNames)); 696 mv.visitCode(); 697 698 final Label handleDefined = new Label(); 699 700 final Class<?> returnType = type.returnType(); 701 final Type asmReturnType = Type.getType(returnType); 702 703 // See if we have overriding method handle defined 704 if(classOverride) { 705 mv.getstatic(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); 706 } else { 707 mv.visitVarInsn(ALOAD, 0); 708 mv.getfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); 709 } 710 // stack: [handle] 711 mv.visitInsn(DUP); 712 mv.visitJumpInsn(IFNONNULL, handleDefined); 713 714 // No handle is available, fall back to default behavior 715 if(Modifier.isAbstract(method.getModifiers())) { 716 // If the super method is abstract, throw an exception 717 mv.anew(UNSUPPORTED_OPERATION_TYPE); 718 mv.dup(); 719 mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG_METHOD_DESCRIPTOR, false); 720 mv.athrow(); 721 } else { 722 mv.visitInsn(POP); 723 // If the super method is not abstract, delegate to it. 724 emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc); 725 } 726 727 mv.visitLabel(handleDefined); 728 // Load the creatingGlobal object 729 if(classOverride) { 730 // If class handle is defined, load the static defining global 731 mv.getstatic(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR); 732 } else { 733 mv.visitVarInsn(ALOAD, 0); 734 mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, GLOBAL_TYPE_DESCRIPTOR); 735 } 736 // stack: [creatingGlobal, handle] 737 final Label setupGlobal = new Label(); 738 mv.visitLabel(setupGlobal); 739 740 // Determine the first index for a local variable 741 int nextLocalVar = 1; // "this" is at 0 742 for(final Type t: asmArgTypes) { 743 nextLocalVar += t.getSize(); 744 } 745 // Set our local variable indices 746 final int currentGlobalVar = nextLocalVar++; 747 final int globalsDifferVar = nextLocalVar++; 748 749 mv.dup(); 750 // stack: [creatingGlobal, creatingGlobal, handle] 751 752 // Emit code for switching to the creating global 753 // Global currentGlobal = Context.getGlobal(); 754 invokeGetGlobal(mv); 755 mv.dup(); 756 757 mv.visitVarInsn(ASTORE, currentGlobalVar); 758 // stack: [currentGlobal, creatingGlobal, creatingGlobal, handle] 759 // if(definingGlobal == currentGlobal) { 760 final Label globalsDiffer = new Label(); 761 mv.ifacmpne(globalsDiffer); 762 // stack: [creatingGlobal, handle] 763 // globalsDiffer = false 764 mv.pop(); 765 // stack: [handle] 766 mv.iconst(0); // false 767 // stack: [false, handle] 768 final Label invokeHandle = new Label(); 769 mv.goTo(invokeHandle); 770 mv.visitLabel(globalsDiffer); 771 // } else { 772 // Context.setGlobal(definingGlobal); 773 // stack: [creatingGlobal, handle] 774 invokeSetGlobal(mv); 775 // stack: [handle] 776 // globalsDiffer = true 777 mv.iconst(1); 778 // stack: [true, handle] 779 780 mv.visitLabel(invokeHandle); 781 mv.visitVarInsn(ISTORE, globalsDifferVar); 782 // stack: [handle] 783 784 // Load all parameters back on stack for dynamic invocation. NOTE: since we're using a generic 785 // Object(Object, Object, ...) type signature for the method, we must box all arguments here. 786 int varOffset = 1; 787 for (final Type t : asmArgTypes) { 788 mv.load(varOffset, t); 789 boxStackTop(mv, t); 790 varOffset += t.getSize(); 791 } 792 793 // Invoke the target method handle 794 final Label tryBlockStart = new Label(); 795 mv.visitLabel(tryBlockStart); 796 emitInvokeExact(mv, type.generic()); 797 convertReturnValue(mv, returnType, asmReturnType); 798 final Label tryBlockEnd = new Label(); 799 mv.visitLabel(tryBlockEnd); 800 emitFinally(mv, currentGlobalVar, globalsDifferVar); 801 mv.areturn(asmReturnType); 802 803 // If Throwable is not declared, we need an adapter from Throwable to RuntimeException 804 final boolean throwableDeclared = isThrowableDeclared(exceptions); 805 final Label throwableHandler; 806 if (!throwableDeclared) { 807 // Add "throw new RuntimeException(Throwable)" handler for Throwable 808 throwableHandler = new Label(); 809 mv.visitLabel(throwableHandler); 810 mv.anew(RUNTIME_EXCEPTION_TYPE); 811 mv.dupX1(); 812 mv.swap(); 813 mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE), false); 814 // Fall through to rethrow handler 815 } else { 816 throwableHandler = null; 817 } 818 final Label rethrowHandler = new Label(); 819 mv.visitLabel(rethrowHandler); 820 // Rethrow handler for RuntimeException, Error, and all declared exception types 821 emitFinally(mv, currentGlobalVar, globalsDifferVar); 822 mv.athrow(); 823 final Label methodEnd = new Label(); 824 mv.visitLabel(methodEnd); 825 826 mv.visitLocalVariable("currentGlobal", GLOBAL_TYPE_DESCRIPTOR, null, setupGlobal, methodEnd, currentGlobalVar); 827 mv.visitLocalVariable("globalsDiffer", Type.BOOLEAN_TYPE.getDescriptor(), null, setupGlobal, methodEnd, globalsDifferVar); 828 829 if(throwableDeclared) { 830 mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME); 831 assert throwableHandler == null; 832 } else { 833 mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME); 834 mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME); 835 for(final String excName: exceptionNames) { 836 mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName); 837 } 838 mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME); 839 } 840 endMethod(mv); 841 } 842 843 private void convertReturnValue(final InstructionAdapter mv, final Class<?> returnType, final Type asmReturnType) { 844 switch(asmReturnType.getSort()) { 845 case Type.VOID: 846 mv.pop(); 847 break; 848 case Type.BOOLEAN: 849 JSType.TO_BOOLEAN.invoke(mv); 850 break; 851 case Type.BYTE: 852 JSType.TO_INT32.invoke(mv); 853 mv.visitInsn(Opcodes.I2B); 854 break; 855 case Type.SHORT: 856 JSType.TO_INT32.invoke(mv); 857 mv.visitInsn(Opcodes.I2S); 858 break; 859 case Type.CHAR: 860 // JSType doesn't have a TO_CHAR, so we have services supply us one. 861 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "toCharPrimitive", TO_CHAR_PRIMITIVE_METHOD_DESCRIPTOR, false); 862 break; 863 case Type.INT: 864 JSType.TO_INT32.invoke(mv); 865 break; 866 case Type.LONG: 867 JSType.TO_LONG.invoke(mv); 868 break; 869 case Type.FLOAT: 870 JSType.TO_NUMBER.invoke(mv); 871 mv.visitInsn(Opcodes.D2F); 872 break; 873 case Type.DOUBLE: 874 JSType.TO_NUMBER.invoke(mv); 875 break; 876 default: 877 if(asmReturnType.equals(OBJECT_TYPE)) { 878 // Must hide ConsString (and potentially other internal Nashorn types) from callers 879 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "exportReturnValue", EXPORT_RETURN_VALUE_METHOD_DESCRIPTOR, false); 880 } else if(asmReturnType.equals(STRING_TYPE)){ 881 // Well-known conversion to String. Not using the JSType one as we want to preserve null as null instead 882 // of the string "n,u,l,l". 883 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "toString", TO_STRING_METHOD_DESCRIPTOR, false); 884 } else { 885 // Invoke converter method handle for everything else. Note that we could have just added an asType or 886 // filterReturnValue to the invoked handle instead, but then every instance would have the function 887 // method handle wrapped in a separate converter method handle, making handle.invokeExact() megamorphic. 888 if(classOverride) { 889 mv.getstatic(generatedClassName, converterFields.get(returnType), METHOD_HANDLE_TYPE_DESCRIPTOR); 890 } else { 891 mv.visitVarInsn(ALOAD, 0); 892 mv.getfield(generatedClassName, converterFields.get(returnType), METHOD_HANDLE_TYPE_DESCRIPTOR); 893 } 894 mv.swap(); 895 emitInvokeExact(mv, MethodType.methodType(returnType, Object.class)); 896 } 897 } 898 } 899 900 private static void emitInvokeExact(final InstructionAdapter mv, final MethodType type) { 901 mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString(), false); 902 } 903 904 private static void boxStackTop(final InstructionAdapter mv, final Type t) { 905 switch(t.getSort()) { 906 case Type.BOOLEAN: 907 invokeValueOf(mv, "Boolean", 'Z'); 908 break; 909 case Type.BYTE: 910 case Type.SHORT: 911 case Type.INT: 912 // bytes and shorts get boxed as integers 913 invokeValueOf(mv, "Integer", 'I'); 914 break; 915 case Type.CHAR: 916 invokeValueOf(mv, "Character", 'C'); 917 break; 918 case Type.FLOAT: 919 // floats get boxed as doubles 920 mv.visitInsn(Opcodes.F2D); 921 invokeValueOf(mv, "Double", 'D'); 922 break; 923 case Type.LONG: 924 invokeValueOf(mv, "Long", 'J'); 925 break; 926 case Type.DOUBLE: 927 invokeValueOf(mv, "Double", 'D'); 928 break; 929 case Type.ARRAY: 930 case Type.OBJECT: 931 case Type.METHOD: 932 // Already boxed 933 break; 934 default: 935 // Not expecting anything else (e.g. VOID) 936 assert false; 937 break; 938 } 939 } 940 941 private static void invokeValueOf(final InstructionAdapter mv, final String boxedType, final char unboxedType) { 942 mv.invokestatic("java/lang/" + boxedType, "valueOf", "(" + unboxedType + ")Ljava/lang/" + boxedType + ";", false); 943 } 944 945 /** 946 * Emit code to restore the previous Nashorn Context when needed. 947 * @param mv the instruction adapter 948 * @param currentGlobalVar index of the local variable holding the reference to the current global at method 949 * entry. 950 * @param globalsDifferVar index of the boolean local variable that is true if the global needs to be restored. 951 */ 952 private static void emitFinally(final InstructionAdapter mv, final int currentGlobalVar, final int globalsDifferVar) { 953 // Emit code to restore the previous Nashorn global if needed 954 mv.visitVarInsn(ILOAD, globalsDifferVar); 955 final Label skip = new Label(); 956 mv.ifeq(skip); 957 mv.visitVarInsn(ALOAD, currentGlobalVar); 958 invokeSetGlobal(mv); 959 mv.visitLabel(skip); 960 } 961 962 private static boolean isThrowableDeclared(final Class<?>[] exceptions) { 963 for (final Class<?> exception : exceptions) { 964 if (exception == Throwable.class) { 965 return true; 966 } 967 } 968 return false; 969 } 970 971 private void generateSuperMethods() { 972 for(final MethodInfo mi: methodInfos) { 973 if(!Modifier.isAbstract(mi.method.getModifiers())) { 974 generateSuperMethod(mi); 975 } 976 } 977 } 978 979 private void generateSuperMethod(final MethodInfo mi) { 980 final Method method = mi.method; 981 982 final String methodDesc = mi.type.toMethodDescriptorString(); 983 final String name = mi.getName(); 984 985 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), 986 SUPER_PREFIX + name, methodDesc, null, getExceptionNames(method.getExceptionTypes()))); 987 mv.visitCode(); 988 989 emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc); 990 991 endMethod(mv); 992 } 993 994 private void emitSuperCall(final InstructionAdapter mv, final Class<?> owner, final String name, final String methodDesc) { 995 mv.visitVarInsn(ALOAD, 0); 996 int nextParam = 1; 997 final Type methodType = Type.getMethodType(methodDesc); 998 for(final Type t: methodType.getArgumentTypes()) { 999 mv.load(nextParam, t); 1000 nextParam += t.getSize(); 1001 } 1002 1003 // default method - non-abstract, interface method 1004 if (Modifier.isInterface(owner.getModifiers())) { 1005 mv.invokespecial(Type.getInternalName(owner), name, methodDesc, false); 1006 } else { 1007 mv.invokespecial(superClassName, name, methodDesc, false); 1008 } 1009 mv.areturn(methodType.getReturnType()); 1010 } 1011 1012 private void generateFinalizerMethods() { 1013 final String finalizerDelegateName = nextName("access$"); 1014 generateFinalizerDelegate(finalizerDelegateName); 1015 generateFinalizerOverride(finalizerDelegateName); 1016 } 1017 1018 private void generateFinalizerDelegate(final String finalizerDelegateName) { 1019 // Generate a delegate that will be invoked from the no-permission trampoline. Note it can be private, as we'll 1020 // refer to it with a MethodHandle constant pool entry in the overridden finalize() method (see 1021 // generateFinalizerOverride()). 1022 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PRIVATE | ACC_STATIC, 1023 finalizerDelegateName, Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE), null, null)); 1024 1025 // Simply invoke super.finalize() 1026 mv.visitVarInsn(ALOAD, 0); 1027 mv.checkcast(Type.getType(generatedClassName)); 1028 mv.invokespecial(superClassName, "finalize", Type.getMethodDescriptor(Type.VOID_TYPE), false); 1029 1030 mv.visitInsn(RETURN); 1031 endMethod(mv); 1032 } 1033 1034 private void generateFinalizerOverride(final String finalizerDelegateName) { 1035 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, "finalize", 1036 VOID_NOARG_METHOD_DESCRIPTOR, null, null)); 1037 // Overridden finalizer will take a MethodHandle to the finalizer delegating method, ... 1038 mv.aconst(new Handle(Opcodes.H_INVOKESTATIC, generatedClassName, finalizerDelegateName, 1039 Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE))); 1040 mv.visitVarInsn(ALOAD, 0); 1041 // ...and invoke it through JavaAdapterServices.invokeNoPermissions 1042 mv.invokestatic(SERVICES_CLASS_TYPE_NAME, "invokeNoPermissions", 1043 Type.getMethodDescriptor(METHOD_HANDLE_TYPE, OBJECT_TYPE), false); 1044 mv.visitInsn(RETURN); 1045 endMethod(mv); 1046 } 1047 1048 private static String[] getExceptionNames(final Class<?>[] exceptions) { 1049 final String[] exceptionNames = new String[exceptions.length]; 1050 for (int i = 0; i < exceptions.length; ++i) { 1051 exceptionNames[i] = Type.getInternalName(exceptions[i]); 1052 } 1053 return exceptionNames; 1054 } 1055 1056 private static int getAccessModifiers(final Method method) { 1057 return ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0); 1058 } 1059 1060 /** 1061 * Gathers methods that can be implemented or overridden from the specified type into this factory's 1062 * {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from 1063 * the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its 1064 * superclass and the interfaces it implements, and add further methods that were not directly declared on the 1065 * class. 1066 * @param type the type defining the methods. 1067 */ 1068 private void gatherMethods(final Class<?> type) throws AdaptationException { 1069 if (Modifier.isPublic(type.getModifiers())) { 1070 final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods(); 1071 1072 for (final Method typeMethod: typeMethods) { 1073 final String name = typeMethod.getName(); 1074 if(name.startsWith(SUPER_PREFIX)) { 1075 continue; 1076 } 1077 final int m = typeMethod.getModifiers(); 1078 if (Modifier.isStatic(m)) { 1079 continue; 1080 } 1081 if (Modifier.isPublic(m) || Modifier.isProtected(m)) { 1082 // Is it a "finalize()"? 1083 if(name.equals("finalize") && typeMethod.getParameterCount() == 0) { 1084 if(type != Object.class) { 1085 hasExplicitFinalizer = true; 1086 if(Modifier.isFinal(m)) { 1087 // Must be able to override an explicit finalizer 1088 throw new AdaptationException(Outcome.ERROR_FINAL_FINALIZER, type.getCanonicalName()); 1089 } 1090 } 1091 continue; 1092 } 1093 1094 final MethodInfo mi = new MethodInfo(typeMethod); 1095 if (Modifier.isFinal(m) || isCallerSensitive(typeMethod)) { 1096 finalMethods.add(mi); 1097 } else if (!finalMethods.contains(mi) && methodInfos.add(mi)) { 1098 if (Modifier.isAbstract(m)) { 1099 abstractMethodNames.add(mi.getName()); 1100 } 1101 mi.setIsCanonical(this); 1102 } 1103 } 1104 } 1105 } 1106 // If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done. 1107 // Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to 1108 // see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a 1109 // superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and 1110 // getMethods() does provide those declared in a superinterface. 1111 if (!type.isInterface()) { 1112 final Class<?> superType = type.getSuperclass(); 1113 if (superType != null) { 1114 gatherMethods(superType); 1115 } 1116 for (final Class<?> itf: type.getInterfaces()) { 1117 gatherMethods(itf); 1118 } 1119 } 1120 } 1121 1122 private void gatherMethods(final List<Class<?>> classes) throws AdaptationException { 1123 for(final Class<?> c: classes) { 1124 gatherMethods(c); 1125 } 1126 } 1127 1128 private static final AccessControlContext GET_DECLARED_MEMBERS_ACC_CTXT = ClassAndLoader.createPermAccCtxt("accessDeclaredMembers"); 1129 1130 /** 1131 * Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters, 1132 * as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and 1133 * {@code Object.clone()}. 1134 * @return a collection of method infos representing those methods that we never override in adapter classes. 1135 */ 1136 private static Collection<MethodInfo> getExcludedMethods() { 1137 return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() { 1138 @Override 1139 public Collection<MethodInfo> run() { 1140 try { 1141 return Arrays.asList( 1142 new MethodInfo(Object.class, "finalize"), 1143 new MethodInfo(Object.class, "clone")); 1144 } catch (final NoSuchMethodException e) { 1145 throw new AssertionError(e); 1146 } 1147 } 1148 }, GET_DECLARED_MEMBERS_ACC_CTXT); 1149 } 1150 1151 private String getCommonSuperClass(final String type1, final String type2) { 1152 try { 1153 final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader); 1154 final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader); 1155 if (c1.isAssignableFrom(c2)) { 1156 return type1; 1157 } 1158 if (c2.isAssignableFrom(c1)) { 1159 return type2; 1160 } 1161 if (c1.isInterface() || c2.isInterface()) { 1162 return OBJECT_TYPE_NAME; 1163 } 1164 return assignableSuperClass(c1, c2).getName().replace('.', '/'); 1165 } catch(final ClassNotFoundException e) { 1166 throw new RuntimeException(e); 1167 } 1168 } 1169 1170 private static Class<?> assignableSuperClass(final Class<?> c1, final Class<?> c2) { 1171 final Class<?> superClass = c1.getSuperclass(); 1172 return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2); 1173 } 1174 1175 private static boolean isCallerSensitive(final AccessibleObject e) { 1176 return e.isAnnotationPresent(CallerSensitive.class); 1177 } 1178} 1179