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 26 package jdk.nashorn.internal.runtime.linker; 27 28 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 29 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; 30 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 31 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 32 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_SUPER; 33 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS; 34 import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; 35 import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE; 36 import static jdk.internal.org.objectweb.asm.Opcodes.D2F; 37 import static jdk.internal.org.objectweb.asm.Opcodes.H_INVOKESTATIC; 38 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESPECIAL; 39 import static jdk.internal.org.objectweb.asm.Opcodes.I2B; 40 import static jdk.internal.org.objectweb.asm.Opcodes.I2S; 41 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 42 import static jdk.nashorn.internal.codegen.CompilerConstants.interfaceCallNoLookup; 43 import static jdk.nashorn.internal.codegen.CompilerConstants.staticCallNoLookup; 44 import static jdk.nashorn.internal.lookup.Lookup.MH; 45 import static jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR; 46 47 import java.lang.invoke.CallSite; 48 import java.lang.invoke.MethodHandle; 49 import java.lang.invoke.MethodHandles.Lookup; 50 import java.lang.invoke.MethodType; 51 import java.lang.reflect.AccessibleObject; 52 import java.lang.reflect.Constructor; 53 import java.lang.reflect.Method; 54 import java.lang.reflect.Modifier; 55 import java.security.AccessControlContext; 56 import java.security.AccessController; 57 import java.security.PrivilegedAction; 58 import java.security.ProtectionDomain; 59 import java.util.Arrays; 60 import java.util.Collection; 61 import java.util.HashSet; 62 import java.util.Iterator; 63 import java.util.List; 64 import java.util.Set; 65 import jdk.internal.org.objectweb.asm.ClassWriter; 66 import jdk.internal.org.objectweb.asm.Handle; 67 import jdk.internal.org.objectweb.asm.Label; 68 import jdk.internal.org.objectweb.asm.Opcodes; 69 import jdk.internal.org.objectweb.asm.Type; 70 import jdk.internal.org.objectweb.asm.commons.InstructionAdapter; 71 import jdk.nashorn.api.scripting.ScriptObjectMirror; 72 import jdk.nashorn.api.scripting.ScriptUtils; 73 import jdk.nashorn.internal.codegen.CompilerConstants.Call; 74 import jdk.nashorn.internal.runtime.ScriptFunction; 75 import jdk.nashorn.internal.runtime.ScriptObject; 76 import jdk.nashorn.internal.runtime.linker.AdaptationResult.Outcome; 77 import jdk.internal.reflect.CallerSensitive; 78 79 /** 80 * Generates bytecode for a Java adapter class. Used by the {@link JavaAdapterFactory}. 81 * </p><p> 82 * For every protected or public constructor in the extended class, the adapter class will have either one or two 83 * public constructors (visibility of protected constructors in the extended class is promoted to public). 84 * <li> 85 * <li>For adapter classes with instance-level overrides, a constructor taking a trailing ScriptObject argument preceded 86 * by original constructor arguments is always created on the adapter class. When such a constructor is invoked, the 87 * passed ScriptObject's member functions are used to implement and/or override methods on the original class, 88 * dispatched by name. A single JavaScript function will act as the implementation for all overloaded methods of the 89 * same name. When methods on an adapter instance are invoked, the functions are invoked having the ScriptObject passed 90 * in the instance constructor as their "this". Subsequent changes to the ScriptObject (reassignment or removal of its 91 * functions) will be reflected in the adapter instance as it is live dispatching to its members on every method invocation. 92 * {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The 93 * only restriction is that since every JavaScript object already has a {@code toString} function through the 94 * {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a 95 * {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be 96 * implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too. 97 * </li> 98 * <li> 99 * If the original types collectively have only one abstract method, or have several of them, but all share the 100 * same name, an additional constructor for instance-level override adapter is provided for every original constructor; 101 * this one takes a ScriptFunction as its last argument preceded by original constructor arguments. This constructor 102 * will use the passed function as the implementation for all abstract methods. For consistency, any concrete methods 103 * sharing the single abstract method name will also be overridden by the function. When methods on the adapter instance 104 * are invoked, the ScriptFunction is invoked with UNDEFINED or Global as its "this" depending whether the function is 105 * strict or not. 106 * </li> 107 * <li> 108 * If the adapter being generated has class-level overrides, constructors taking same arguments as the superclass 109 * constructors are created. These constructors simply delegate to the superclass constructor. They are simply used to 110 * create instances of the adapter class, with no instance-level overrides, as they don't have them. If the original 111 * class' constructor was variable arity, the adapter constructor will also be variable arity. Protected constructors 112 * are exposed as public. 113 * </li> 114 * </ul> 115 * </p><p> 116 * For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect 117 * to coerce the JavaScript function return value to the expected Java return type. 118 * </p><p> 119 * Since we are adding a trailing argument to the generated constructors in the adapter class with instance-level overrides, they will never be 120 * declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The 121 * reason we are passing the additional argument at the end of the argument list instead at the front is that the 122 * source-level script expression <code>new X(a, b) { ... }</code> (which is a proprietary syntax extension Nashorn uses 123 * to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>. 124 * </p><p> 125 * It is possible to create two different adapter classes: those that can have class-level overrides, and those that can 126 * have instance-level overrides. When {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject, ProtectionDomain)} 127 * or {@link JavaAdapterFactory#getAdapterClassFor(Class[], ScriptObject, Lookup)} is invoked 128 * with non-null {@code classOverrides} parameter, an adapter class is created that can have class-level overrides, and 129 * the passed script object will be used as the implementations for its methods, just as in the above case of the 130 * constructor taking a script object. Note that in the case of class-level overrides, a new adapter class is created on 131 * every invocation, and the implementation object is bound to the class, not to any instance. All created instances 132 * will share these functions. If it is required to have both class-level overrides and instance-level overrides, the 133 * class-level override adapter class should be subclassed with an instance-override adapter. Since adapters delegate to 134 * super class when an overriding method handle is not specified, this will behave as expected. It is not possible to 135 * have both class-level and instance-level overrides in the same class for security reasons: adapter classes are 136 * defined with a protection domain of their creator code, and an adapter class that has both class and instance level 137 * overrides would need to have two potentially different protection domains: one for class-based behavior and one for 138 * instance-based behavior; since Java classes can only belong to a single protection domain, this could not be 139 * implemented securely. 140 */ 141 final class JavaAdapterBytecodeGenerator { 142 // Field names in adapters 143 private static final String GLOBAL_FIELD_NAME = "global"; 144 private static final String DELEGATE_FIELD_NAME = "delegate"; 145 private static final String IS_FUNCTION_FIELD_NAME = "isFunction"; 146 private static final String CALL_THIS_FIELD_NAME = "callThis"; 147 148 // Initializer names 149 private static final String INIT = "<init>"; 150 private static final String CLASS_INIT = "<clinit>"; 151 152 // Types often used in generated bytecode 153 private static final Type OBJECT_TYPE = Type.getType(Object.class); 154 private static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class); 155 private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class); 156 private static final Type SCRIPT_OBJECT_MIRROR_TYPE = Type.getType(ScriptObjectMirror.class); 157 158 // JavaAdapterServices methods used in generated bytecode 159 private static final Call CHECK_FUNCTION = lookupServiceMethod("checkFunction", ScriptFunction.class, Object.class, String.class); 160 private static final Call EXPORT_RETURN_VALUE = lookupServiceMethod("exportReturnValue", Object.class, Object.class); 161 private static final Call GET_CALL_THIS = lookupServiceMethod("getCallThis", Object.class, ScriptFunction.class, Object.class); 162 private static final Call GET_CLASS_OVERRIDES = lookupServiceMethod("getClassOverrides", ScriptObject.class); 163 private static final Call GET_NON_NULL_GLOBAL = lookupServiceMethod("getNonNullGlobal", ScriptObject.class); 164 private static final Call HAS_OWN_TO_STRING = lookupServiceMethod("hasOwnToString", boolean.class, ScriptObject.class); 165 private static final Call INVOKE_NO_PERMISSIONS = lookupServiceMethod("invokeNoPermissions", void.class, MethodHandle.class, Object.class); 166 private static final Call NOT_AN_OBJECT = lookupServiceMethod("notAnObject", void.class, Object.class); 167 private static final Call SET_GLOBAL = lookupServiceMethod("setGlobal", Runnable.class, ScriptObject.class); 168 private static final Call TO_CHAR_PRIMITIVE = lookupServiceMethod("toCharPrimitive", char.class, Object.class); 169 private static final Call UNSUPPORTED = lookupServiceMethod("unsupported", UnsupportedOperationException.class); 170 private static final Call WRAP_THROWABLE = lookupServiceMethod("wrapThrowable", RuntimeException.class, Throwable.class); 171 private static final Call UNWRAP_MIRROR = lookupServiceMethod("unwrapMirror", ScriptObject.class, Object.class, boolean.class); 172 173 // Other methods invoked by the generated bytecode 174 private static final Call UNWRAP = staticCallNoLookup(ScriptUtils.class, "unwrap", Object.class, Object.class); 175 private static final Call CHAR_VALUE_OF = staticCallNoLookup(Character.class, "valueOf", Character.class, char.class); 176 private static final Call DOUBLE_VALUE_OF = staticCallNoLookup(Double.class, "valueOf", Double.class, double.class); 177 private static final Call LONG_VALUE_OF = staticCallNoLookup(Long.class, "valueOf", Long.class, long.class); 178 private static final Call RUN = interfaceCallNoLookup(Runnable.class, "run", void.class); 179 180 // ASM handle to the bootstrap method 181 private static final Handle BOOTSTRAP_HANDLE = new Handle(H_INVOKESTATIC, 182 Type.getInternalName(JavaAdapterServices.class), "bootstrap", 183 MethodType.methodType(CallSite.class, Lookup.class, String.class, 184 MethodType.class, int.class).toMethodDescriptorString(), false); 185 186 // ASM handle to the bootstrap method for array populator 187 private static final Handle CREATE_ARRAY_BOOTSTRAP_HANDLE = new Handle(H_INVOKESTATIC, 188 Type.getInternalName(JavaAdapterServices.class), "createArrayBootstrap", 189 MethodType.methodType(CallSite.class, Lookup.class, String.class, 190 MethodType.class).toMethodDescriptorString(), false); 191 192 // Field type names used in the generated bytecode 193 private static final String SCRIPT_OBJECT_TYPE_DESCRIPTOR = SCRIPT_OBJECT_TYPE.getDescriptor(); 194 private static final String OBJECT_TYPE_DESCRIPTOR = OBJECT_TYPE.getDescriptor(); 195 private static final String BOOLEAN_TYPE_DESCRIPTOR = Type.BOOLEAN_TYPE.getDescriptor(); 196 197 // Throwable names used in the generated bytecode 198 private static final String RUNTIME_EXCEPTION_TYPE_NAME = Type.getInternalName(RuntimeException.class); 199 private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class); 200 private static final String THROWABLE_TYPE_NAME = Type.getInternalName(Throwable.class); 201 202 // Some more frequently used method descriptors 203 private static final String GET_METHOD_PROPERTY_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE, SCRIPT_OBJECT_TYPE); 204 private static final String VOID_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE); 205 206 private static final String ADAPTER_PACKAGE_INTERNAL = "jdk/nashorn/javaadapters/"; 207 private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 255; 208 209 // Method name prefix for invoking super-methods 210 static final String SUPER_PREFIX = "super$"; 211 212 // Method name and type for the no-privilege finalizer delegate 213 private static final String FINALIZER_DELEGATE_NAME = "$$nashornFinalizerDelegate"; 214 private static final String FINALIZER_DELEGATE_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, OBJECT_TYPE); 215 216 /** 217 * Collection of methods we never override: Object.clone(), Object.finalize(). 218 */ 219 private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods(); 220 221 // This is the superclass for our generated adapter. 222 private final Class<?> superClass; 223 // Interfaces implemented by our generated adapter. 224 private final List<Class<?>> interfaces; 225 // Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class 226 // loader that has the visibility of all original types (class to extend and interfaces to implement) and of the 227 // Nashorn classes. 228 private final ClassLoader commonLoader; 229 // Is this a generator for the version of the class that can have overrides on the class level? 230 private final boolean classOverride; 231 // Binary name of the superClass 232 private final String superClassName; 233 // Binary name of the generated class. 234 private final String generatedClassName; 235 private final Set<String> abstractMethodNames = new HashSet<>(); 236 private final String samName; 237 private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED); 238 private final Set<MethodInfo> methodInfos = new HashSet<>(); 239 private final boolean autoConvertibleFromFunction; 240 private boolean hasExplicitFinalizer = false; 241 242 private final ClassWriter cw; 243 244 /** 245 * Creates a generator for the bytecode for the adapter for the specified superclass and interfaces. 246 * @param superClass the superclass the adapter will extend. 247 * @param interfaces the interfaces the adapter will implement. 248 * @param commonLoader the class loader that can see all of superClass, interfaces, and Nashorn classes. 249 * @param classOverride true to generate the bytecode for the adapter that has class-level overrides, false to 250 * generate the bytecode for the adapter that has instance-level overrides. 251 * @throws AdaptationException if the adapter can not be generated for some reason. 252 */ 253 JavaAdapterBytecodeGenerator(final Class<?> superClass, final List<Class<?>> interfaces, 254 final ClassLoader commonLoader, final boolean classOverride) throws AdaptationException { 255 assert superClass != null && !superClass.isInterface(); 256 assert interfaces != null; 257 258 this.superClass = superClass; 259 this.interfaces = interfaces; 260 this.classOverride = classOverride; 261 this.commonLoader = commonLoader; 262 cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { 263 @Override 264 protected String getCommonSuperClass(final String type1, final String type2) { 265 // We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class 266 // loader to find the common superclass of two types when needed. 267 return JavaAdapterBytecodeGenerator.this.getCommonSuperClass(type1, type2); 268 } 269 }; 270 superClassName = Type.getInternalName(superClass); 271 generatedClassName = getGeneratedClassName(superClass, interfaces); 272 273 cw.visit(Opcodes.V1_8, ACC_PUBLIC | ACC_SUPER, generatedClassName, null, superClassName, getInternalTypeNames(interfaces)); 274 generateField(GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 275 generateField(DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 276 277 gatherMethods(superClass); 278 gatherMethods(interfaces); 279 if (abstractMethodNames.size() == 1) { 280 samName = abstractMethodNames.iterator().next(); 281 generateField(CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR); 282 generateField(IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR); 283 } else { 284 samName = null; 285 } 286 if(classOverride) { 287 generateClassInit(); 288 } 289 autoConvertibleFromFunction = generateConstructors(); 290 generateMethods(); 291 generateSuperMethods(); 292 if (hasExplicitFinalizer) { 293 generateFinalizerMethods(); 294 } 295 // } 296 cw.visitEnd(); 297 } 298 299 private void generateField(final String name, final String fieldDesc) { 300 cw.visitField(ACC_PRIVATE | ACC_FINAL | (classOverride ? ACC_STATIC : 0), name, fieldDesc, null, null).visitEnd(); 301 } 302 303 JavaAdapterClassLoader createAdapterClassLoader() { 304 return new JavaAdapterClassLoader(generatedClassName, cw.toByteArray()); 305 } 306 307 boolean isAutoConvertibleFromFunction() { 308 return autoConvertibleFromFunction; 309 } 310 311 private static String getGeneratedClassName(final Class<?> superType, final List<Class<?>> interfaces) { 312 // The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're 313 // just implementing interfaces or extending Object), then the first implemented interface or Object. 314 final Class<?> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType; 315 final Package pkg = namingType.getPackage(); 316 final String namingTypeName = Type.getInternalName(namingType); 317 final StringBuilder buf = new StringBuilder(); 318 buf.append(ADAPTER_PACKAGE_INTERNAL).append(namingTypeName.replace('/', '_')); 319 final Iterator<Class<?>> it = interfaces.iterator(); 320 if(superType == Object.class && it.hasNext()) { 321 it.next(); // Skip first interface, it was used to primarily name the adapter 322 } 323 // Append interface names to the adapter name 324 while(it.hasNext()) { 325 buf.append("$$").append(it.next().getSimpleName()); 326 } 327 return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length())); 328 } 329 330 /** 331 * Given a list of class objects, return an array with their binary names. Used to generate the array of interface 332 * names to implement. 333 * @param classes the classes 334 * @return an array of names 335 */ 336 private static String[] getInternalTypeNames(final List<Class<?>> classes) { 337 final int interfaceCount = classes.size(); 338 final String[] interfaceNames = new String[interfaceCount]; 339 for(int i = 0; i < interfaceCount; ++i) { 340 interfaceNames[i] = Type.getInternalName(classes.get(i)); 341 } 342 return interfaceNames; 343 } 344 345 private void generateClassInit() { 346 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_STATIC, CLASS_INIT, 347 VOID_METHOD_DESCRIPTOR, null, null)); 348 349 // Assign "global = Context.getGlobal()" 350 GET_NON_NULL_GLOBAL.invoke(mv); 351 mv.putstatic(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 352 353 GET_CLASS_OVERRIDES.invoke(mv); 354 if(samName != null) { 355 // If the class is a SAM, allow having ScriptFunction passed as class overrides 356 mv.dup(); 357 mv.instanceOf(SCRIPT_FUNCTION_TYPE); 358 mv.dup(); 359 mv.putstatic(generatedClassName, IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR); 360 final Label notFunction = new Label(); 361 mv.ifeq(notFunction); 362 mv.dup(); 363 mv.checkcast(SCRIPT_FUNCTION_TYPE); 364 emitInitCallThis(mv); 365 mv.visitLabel(notFunction); 366 } 367 mv.putstatic(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 368 369 endInitMethod(mv); 370 } 371 372 /** 373 * Emit bytecode for initializing the "callThis" field. 374 */ 375 private void emitInitCallThis(final InstructionAdapter mv) { 376 loadField(mv, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 377 GET_CALL_THIS.invoke(mv); 378 if(classOverride) { 379 mv.putstatic(generatedClassName, CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR); 380 } else { 381 // It is presumed ALOAD 0 was already executed 382 mv.putfield(generatedClassName, CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR); 383 } 384 } 385 386 private boolean generateConstructors() throws AdaptationException { 387 boolean gotCtor = false; 388 boolean canBeAutoConverted = false; 389 for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) { 390 final int modifier = ctor.getModifiers(); 391 if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0 && !isCallerSensitive(ctor)) { 392 canBeAutoConverted = generateConstructors(ctor) | canBeAutoConverted; 393 gotCtor = true; 394 } 395 } 396 if(!gotCtor) { 397 throw new AdaptationException(ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName()); 398 } 399 return canBeAutoConverted; 400 } 401 402 private boolean generateConstructors(final Constructor<?> ctor) { 403 if(classOverride) { 404 // Generate a constructor that just delegates to ctor. This is used with class-level overrides, when we want 405 // to create instances without further per-instance overrides. 406 generateDelegatingConstructor(ctor); 407 return false; 408 } 409 410 // Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the 411 // beginning of its parameter list. 412 generateOverridingConstructor(ctor, false); 413 414 if (samName == null) { 415 return false; 416 } 417 // If all our abstract methods have a single name, generate an additional constructor, one that takes a 418 // ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods. 419 generateOverridingConstructor(ctor, true); 420 // If the original type only has a single abstract method name, as well as a default ctor, then it can 421 // be automatically converted from JS function. 422 return ctor.getParameterTypes().length == 0; 423 } 424 425 private void generateDelegatingConstructor(final Constructor<?> ctor) { 426 final Type originalCtorType = Type.getType(ctor); 427 final Type[] argTypes = originalCtorType.getArgumentTypes(); 428 429 // All constructors must be public, even if in the superclass they were protected. 430 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC | 431 (ctor.isVarArgs() ? ACC_VARARGS : 0), INIT, 432 Type.getMethodDescriptor(originalCtorType.getReturnType(), argTypes), null, null)); 433 434 mv.visitCode(); 435 emitSuperConstructorCall(mv, originalCtorType.getDescriptor()); 436 437 endInitMethod(mv); 438 } 439 440 /** 441 * Generates a constructor for the instance adapter class. This constructor will take the same arguments as the supertype 442 * constructor passed as the argument here, and delegate to it. However, it will take an additional argument of 443 * either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize 444 * all the method handle fields of the adapter instance with functions from the script object (or the script 445 * function itself, if that's what's passed). Additionally, it will create another constructor with an additional 446 * Object type parameter that can be used for ScriptObjectMirror objects. 447 * The constructor will also store the Nashorn global that was current at the constructor 448 * invocation time in a field named "global". The generated constructor will be public, regardless of whether the 449 * supertype constructor was public or protected. The generated constructor will not be variable arity, even if the 450 * supertype constructor was. 451 * @param ctor the supertype constructor that is serving as the base for the generated constructor. 452 * @param fromFunction true if we're generating a constructor that initializes SAM types from a single 453 * ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a 454 * ScriptObject passed to it. 455 */ 456 private void generateOverridingConstructor(final Constructor<?> ctor, final boolean fromFunction) { 457 final Type originalCtorType = Type.getType(ctor); 458 final Type[] originalArgTypes = originalCtorType.getArgumentTypes(); 459 final int argLen = originalArgTypes.length; 460 final Type[] newArgTypes = new Type[argLen + 1]; 461 462 // Insert ScriptFunction|ScriptObject as the last argument to the constructor 463 final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : SCRIPT_OBJECT_TYPE; 464 newArgTypes[argLen] = extraArgumentType; 465 System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen); 466 467 // All constructors must be public, even if in the superclass they were protected. 468 // Existing super constructor <init>(this, args...) triggers generating <init>(this, args..., delegate). 469 // Any variable arity constructors become fixed-arity with explicit array arguments. 470 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT, 471 Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null)); 472 473 mv.visitCode(); 474 // First, invoke super constructor with original arguments. 475 final int extraArgOffset = emitSuperConstructorCall(mv, originalCtorType.getDescriptor()); 476 477 // Assign "this.global = Context.getGlobal()" 478 mv.visitVarInsn(ALOAD, 0); 479 GET_NON_NULL_GLOBAL.invoke(mv); 480 mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 481 482 // Assign "this.delegate = delegate" 483 mv.visitVarInsn(ALOAD, 0); 484 mv.visitVarInsn(ALOAD, extraArgOffset); 485 mv.putfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 486 487 if (fromFunction) { 488 // Assign "isFunction = true" 489 mv.visitVarInsn(ALOAD, 0); 490 mv.iconst(1); 491 mv.putfield(generatedClassName, IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR); 492 493 mv.visitVarInsn(ALOAD, 0); 494 mv.visitVarInsn(ALOAD, extraArgOffset); 495 emitInitCallThis(mv); 496 } 497 498 endInitMethod(mv); 499 500 if (! fromFunction) { 501 newArgTypes[argLen] = OBJECT_TYPE; 502 final InstructionAdapter mv2 = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT, 503 Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null)); 504 generateOverridingConstructorWithObjectParam(mv2, originalCtorType.getDescriptor()); 505 } 506 } 507 508 // Object additional param accepting constructor for handling ScriptObjectMirror objects, which are 509 // unwrapped to work as ScriptObjects or ScriptFunctions. This also handles null and undefined values for 510 // script adapters by throwing TypeError on such script adapters. 511 private void generateOverridingConstructorWithObjectParam(final InstructionAdapter mv, final String ctorDescriptor) { 512 mv.visitCode(); 513 final int extraArgOffset = emitSuperConstructorCall(mv, ctorDescriptor); 514 515 // Check for ScriptObjectMirror 516 mv.visitVarInsn(ALOAD, extraArgOffset); 517 mv.instanceOf(SCRIPT_OBJECT_MIRROR_TYPE); 518 final Label notMirror = new Label(); 519 mv.ifeq(notMirror); 520 521 mv.visitVarInsn(ALOAD, 0); 522 mv.visitVarInsn(ALOAD, extraArgOffset); 523 mv.iconst(0); 524 UNWRAP_MIRROR.invoke(mv); 525 mv.putfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 526 527 mv.visitVarInsn(ALOAD, 0); 528 mv.visitVarInsn(ALOAD, extraArgOffset); 529 mv.iconst(1); 530 UNWRAP_MIRROR.invoke(mv); 531 mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 532 533 final Label done = new Label(); 534 535 if (samName != null) { 536 mv.visitVarInsn(ALOAD, 0); 537 mv.getfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 538 mv.instanceOf(SCRIPT_FUNCTION_TYPE); 539 mv.ifeq(done); 540 541 // Assign "isFunction = true" 542 mv.visitVarInsn(ALOAD, 0); 543 mv.iconst(1); 544 mv.putfield(generatedClassName, IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR); 545 546 mv.visitVarInsn(ALOAD, 0); 547 mv.dup(); 548 mv.getfield(generatedClassName, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 549 mv.checkcast(SCRIPT_FUNCTION_TYPE); 550 emitInitCallThis(mv); 551 mv.goTo(done); 552 } 553 554 mv.visitLabel(notMirror); 555 556 // Throw error if not a ScriptObject 557 mv.visitVarInsn(ALOAD, extraArgOffset); 558 NOT_AN_OBJECT.invoke(mv); 559 560 mv.visitLabel(done); 561 endInitMethod(mv); 562 } 563 564 private static void endInitMethod(final InstructionAdapter mv) { 565 mv.visitInsn(RETURN); 566 endMethod(mv); 567 } 568 569 private static void endMethod(final InstructionAdapter mv) { 570 mv.visitMaxs(0, 0); 571 mv.visitEnd(); 572 } 573 574 /** 575 * Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the 576 * reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the 577 * method handle serving as the implementation of this method in adapter instances. 578 * 579 */ 580 private static class MethodInfo { 581 private final Method method; 582 private final MethodType type; 583 584 private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException { 585 this(clazz.getDeclaredMethod(name, argTypes)); 586 } 587 588 private MethodInfo(final Method method) { 589 this.method = method; 590 this.type = MH.type(method.getReturnType(), method.getParameterTypes()); 591 } 592 593 @Override 594 public boolean equals(final Object obj) { 595 return obj instanceof MethodInfo && equals((MethodInfo)obj); 596 } 597 598 private boolean equals(final MethodInfo other) { 599 // Only method name and type are used for comparison; method handle field name is not. 600 return getName().equals(other.getName()) && type.equals(other.type); 601 } 602 603 String getName() { 604 return method.getName(); 605 } 606 607 @Override 608 public int hashCode() { 609 return getName().hashCode() ^ type.hashCode(); 610 } 611 } 612 613 private void generateMethods() { 614 for(final MethodInfo mi: methodInfos) { 615 generateMethod(mi); 616 } 617 } 618 619 /** 620 * Generates a method in the adapter class that adapts a method from the 621 * original class. The generated method will either invoke the delegate 622 * using a CALL dynamic operation call site (if it is a SAM method and the 623 * delegate is a ScriptFunction), or invoke GET_METHOD_PROPERTY dynamic 624 * operation with the method name as the argument and then invoke the 625 * returned ScriptFunction using the CALL dynamic operation. If 626 * GET_METHOD_PROPERTY returns null or undefined (that is, the JS object 627 * doesn't provide an implementation for the method) then the method will 628 * either do a super invocation to base class, or if the method is abstract, 629 * throw an {@link UnsupportedOperationException}. Finally, if 630 * GET_METHOD_PROPERTY returns something other than a ScriptFunction, null, 631 * or undefined, a TypeError is thrown. The current Global is checked before 632 * the dynamic operations, and if it is different than the Global used to 633 * create the adapter, the creating Global is set to be the current Global. 634 * In this case, the previously current Global is restored after the 635 * invocation. If CALL results in a Throwable that is not one of the 636 * method's declared exceptions, and is not an unchecked throwable, then it 637 * is wrapped into a {@link RuntimeException} and the runtime exception is 638 * thrown. 639 * @param mi the method info describing the method to be generated. 640 */ 641 private void generateMethod(final MethodInfo mi) { 642 final Method method = mi.method; 643 final Class<?>[] exceptions = method.getExceptionTypes(); 644 final String[] exceptionNames = getExceptionNames(exceptions); 645 final MethodType type = mi.type; 646 final String methodDesc = type.toMethodDescriptorString(); 647 final String name = mi.getName(); 648 649 final Type asmType = Type.getMethodType(methodDesc); 650 final Type[] asmArgTypes = asmType.getArgumentTypes(); 651 652 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), name, 653 methodDesc, null, exceptionNames)); 654 mv.visitCode(); 655 656 final Class<?> returnType = type.returnType(); 657 final Type asmReturnType = Type.getType(returnType); 658 659 // Determine the first index for a local variable 660 int nextLocalVar = 1; // "this" is at 0 661 for(final Type t: asmArgTypes) { 662 nextLocalVar += t.getSize(); 663 } 664 // Set our local variable index 665 final int globalRestoringRunnableVar = nextLocalVar++; 666 667 // Load the creatingGlobal object 668 loadField(mv, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 669 670 // stack: [creatingGlobal] 671 SET_GLOBAL.invoke(mv); 672 // stack: [runnable] 673 mv.visitVarInsn(ASTORE, globalRestoringRunnableVar); 674 // stack: [] 675 676 final Label tryBlockStart = new Label(); 677 mv.visitLabel(tryBlockStart); 678 679 final Label callCallee = new Label(); 680 final Label defaultBehavior = new Label(); 681 // If this is a SAM type... 682 if (samName != null) { 683 // ...every method will be checking whether we're initialized with a 684 // function. 685 loadField(mv, IS_FUNCTION_FIELD_NAME, BOOLEAN_TYPE_DESCRIPTOR); 686 // stack: [isFunction] 687 if (name.equals(samName)) { 688 final Label notFunction = new Label(); 689 mv.ifeq(notFunction); 690 // stack: [] 691 // If it's a SAM method, it'll load delegate as the "callee" and 692 // "callThis" as "this" for the call if delegate is a function. 693 loadField(mv, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 694 // NOTE: if we added "mv.checkcast(SCRIPT_FUNCTION_TYPE);" here 695 // we could emit the invokedynamic CALL instruction with signature 696 // (ScriptFunction, Object, ...) instead of (Object, Object, ...). 697 // We could combine this with an optimization in 698 // ScriptFunction.findCallMethod where it could link a call with a 699 // thinner guard when the call site statically guarantees that the 700 // callee argument is a ScriptFunction. Additionally, we could use 701 // a "ScriptFunction function" field in generated classes instead 702 // of a "boolean isFunction" field to avoid the checkcast. 703 loadField(mv, CALL_THIS_FIELD_NAME, OBJECT_TYPE_DESCRIPTOR); 704 // stack: [callThis, delegate] 705 mv.goTo(callCallee); 706 mv.visitLabel(notFunction); 707 } else { 708 // If it's not a SAM method, and the delegate is a function, 709 // it'll fall back to default behavior 710 mv.ifne(defaultBehavior); 711 // stack: [] 712 } 713 } 714 715 // At this point, this is either not a SAM method or the delegate is 716 // not a ScriptFunction. We need to emit a GET_METHOD_PROPERTY Nashorn 717 // invokedynamic. 718 719 if(name.equals("toString")) { 720 // Since every JS Object has a toString, we only override 721 // "String toString()" it if it's explicitly specified on the object. 722 loadField(mv, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 723 // stack: [delegate] 724 HAS_OWN_TO_STRING.invoke(mv); 725 // stack: [hasOwnToString] 726 mv.ifeq(defaultBehavior); 727 } 728 729 loadField(mv, DELEGATE_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); 730 mv.dup(); 731 // stack: [delegate, delegate] 732 final String encodedName = NameCodec.encode(name); 733 mv.visitInvokeDynamicInsn(encodedName, 734 GET_METHOD_PROPERTY_METHOD_DESCRIPTOR, BOOTSTRAP_HANDLE, 735 NashornCallSiteDescriptor.GET_METHOD_PROPERTY); 736 // stack: [callee, delegate] 737 mv.visitLdcInsn(name); 738 // stack: [name, callee, delegate] 739 CHECK_FUNCTION.invoke(mv); 740 // stack: [fnCalleeOrNull, delegate] 741 final Label hasFunction = new Label(); 742 mv.dup(); 743 // stack: [fnCalleeOrNull, fnCalleeOrNull, delegate] 744 mv.ifnonnull(hasFunction); 745 // stack: [null, delegate] 746 // If it's null or undefined, clear stack and fall back to default 747 // behavior. 748 mv.pop2(); 749 // stack: [] 750 751 // We can also arrive here from check for "delegate instanceof ScriptFunction" 752 // in a non-SAM method as well as from a check for "hasOwnToString(delegate)" 753 // for a toString delegate. 754 mv.visitLabel(defaultBehavior); 755 final Runnable emitFinally = ()->emitFinally(mv, globalRestoringRunnableVar); 756 final Label normalFinally = new Label(); 757 if(Modifier.isAbstract(method.getModifiers())) { 758 // If the super method is abstract, throw UnsupportedOperationException 759 UNSUPPORTED.invoke(mv); 760 // NOTE: no need to invoke emitFinally.run() as we're inside the 761 // tryBlockStart/tryBlockEnd range, so throwing this exception will 762 // transfer control to the rethrow handler and the finally block in it 763 // will execute. 764 mv.athrow(); 765 } else { 766 // If the super method is not abstract, delegate to it. 767 emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc); 768 mv.goTo(normalFinally); 769 } 770 771 mv.visitLabel(hasFunction); 772 // stack: [callee, delegate] 773 mv.swap(); 774 // stack [delegate, callee] 775 mv.visitLabel(callCallee); 776 777 778 // Load all parameters back on stack for dynamic invocation. 779 780 int varOffset = 1; 781 // If the param list length is more than 253 slots, we can't invoke it 782 // directly as with (callee, this) it'll exceed 255. 783 final boolean isVarArgCall = getParamListLengthInSlots(asmArgTypes) > 253; 784 for (final Type t : asmArgTypes) { 785 mv.load(varOffset, t); 786 convertParam(mv, t, isVarArgCall); 787 varOffset += t.getSize(); 788 } 789 // stack: [args..., callee, delegate] 790 791 // If the resulting parameter list length is too long... 792 if (isVarArgCall) { 793 // ... we pack the parameters (except callee and this) into an array 794 // and use Nashorn vararg invocation. 795 mv.visitInvokeDynamicInsn(NameCodec.EMPTY_NAME, 796 getArrayCreatorMethodType(type).toMethodDescriptorString(), 797 CREATE_ARRAY_BOOTSTRAP_HANDLE); 798 } 799 800 // Invoke the target method handle 801 mv.visitInvokeDynamicInsn(encodedName, 802 getCallMethodType(isVarArgCall, type).toMethodDescriptorString(), 803 BOOTSTRAP_HANDLE, NashornCallSiteDescriptor.CALL); 804 // stack: [returnValue] 805 convertReturnValue(mv, returnType); 806 mv.visitLabel(normalFinally); 807 emitFinally.run(); 808 mv.areturn(asmReturnType); 809 810 // If Throwable is not declared, we need an adapter from Throwable to RuntimeException 811 final boolean throwableDeclared = isThrowableDeclared(exceptions); 812 final Label throwableHandler; 813 if (!throwableDeclared) { 814 // Add "throw new RuntimeException(Throwable)" handler for Throwable 815 throwableHandler = new Label(); 816 mv.visitLabel(throwableHandler); 817 WRAP_THROWABLE.invoke(mv); 818 // Fall through to rethrow handler 819 } else { 820 throwableHandler = null; 821 } 822 final Label rethrowHandler = new Label(); 823 mv.visitLabel(rethrowHandler); 824 // Rethrow handler for RuntimeException, Error, and all declared exception types 825 emitFinally.run(); 826 mv.athrow(); 827 828 if(throwableDeclared) { 829 mv.visitTryCatchBlock(tryBlockStart, normalFinally, rethrowHandler, THROWABLE_TYPE_NAME); 830 assert throwableHandler == null; 831 } else { 832 mv.visitTryCatchBlock(tryBlockStart, normalFinally, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME); 833 mv.visitTryCatchBlock(tryBlockStart, normalFinally, rethrowHandler, ERROR_TYPE_NAME); 834 for(final String excName: exceptionNames) { 835 mv.visitTryCatchBlock(tryBlockStart, normalFinally, rethrowHandler, excName); 836 } 837 mv.visitTryCatchBlock(tryBlockStart, normalFinally, throwableHandler, THROWABLE_TYPE_NAME); 838 } 839 endMethod(mv); 840 } 841 842 private static MethodType getCallMethodType(final boolean isVarArgCall, final MethodType type) { 843 final Class<?>[] callParamTypes; 844 if (isVarArgCall) { 845 // Variable arity calls are always (Object callee, Object this, Object[] params) 846 callParamTypes = new Class<?>[] { Object.class, Object.class, Object[].class }; 847 } else { 848 // Adjust invocation type signature for conversions we instituted in 849 // convertParam; also, byte and short get passed as ints. 850 final Class<?>[] origParamTypes = type.parameterArray(); 851 callParamTypes = new Class<?>[origParamTypes.length + 2]; 852 callParamTypes[0] = Object.class; // callee; could be ScriptFunction.class ostensibly 853 callParamTypes[1] = Object.class; // this 854 for(int i = 0; i < origParamTypes.length; ++i) { 855 callParamTypes[i + 2] = getNashornParamType(origParamTypes[i], false); 856 } 857 } 858 return MethodType.methodType(getNashornReturnType(type.returnType()), callParamTypes); 859 } 860 861 private static MethodType getArrayCreatorMethodType(final MethodType type) { 862 final Class<?>[] callParamTypes = type.parameterArray(); 863 for(int i = 0; i < callParamTypes.length; ++i) { 864 callParamTypes[i] = getNashornParamType(callParamTypes[i], true); 865 } 866 return MethodType.methodType(Object[].class, callParamTypes); 867 } 868 869 private static Class<?> getNashornParamType(final Class<?> clazz, final boolean varArg) { 870 if (clazz == byte.class || clazz == short.class) { 871 return int.class; 872 } else if (clazz == float.class) { 873 // If using variable arity, we'll pass a Double instead of double 874 // so that floats don't extend the length of the parameter list. 875 // We return Object.class instead of Double.class though as the 876 // array collector will anyway operate on Object. 877 return varArg ? Object.class : double.class; 878 } else if (!clazz.isPrimitive() || clazz == long.class || clazz == char.class) { 879 return Object.class; 880 } 881 return clazz; 882 } 883 884 private static Class<?> getNashornReturnType(final Class<?> clazz) { 885 if (clazz == byte.class || clazz == short.class) { 886 return int.class; 887 } else if (clazz == float.class) { 888 return double.class; 889 } else if (clazz == void.class || clazz == char.class) { 890 return Object.class; 891 } 892 return clazz; 893 } 894 895 896 private void loadField(final InstructionAdapter mv, final String name, final String desc) { 897 if(classOverride) { 898 mv.getstatic(generatedClassName, name, desc); 899 } else { 900 mv.visitVarInsn(ALOAD, 0); 901 mv.getfield(generatedClassName, name, desc); 902 } 903 } 904 905 private static void convertReturnValue(final InstructionAdapter mv, final Class<?> origReturnType) { 906 if (origReturnType == void.class) { 907 mv.pop(); 908 } else if (origReturnType == Object.class) { 909 // Must hide ConsString (and potentially other internal Nashorn types) from callers 910 EXPORT_RETURN_VALUE.invoke(mv); 911 } else if (origReturnType == byte.class) { 912 mv.visitInsn(I2B); 913 } else if (origReturnType == short.class) { 914 mv.visitInsn(I2S); 915 } else if (origReturnType == float.class) { 916 mv.visitInsn(D2F); 917 } else if (origReturnType == char.class) { 918 TO_CHAR_PRIMITIVE.invoke(mv); 919 } 920 } 921 922 /** 923 * Emits instruction for converting a parameter on the top of the stack to 924 * a type that is understood by Nashorn. 925 * @param mv the current method visitor 926 * @param t the type on the top of the stack 927 * @param varArg if the invocation will be variable arity 928 */ 929 private static void convertParam(final InstructionAdapter mv, final Type t, final boolean varArg) { 930 // We perform conversions of some primitives to accommodate types that 931 // Nashorn can handle. 932 switch(t.getSort()) { 933 case Type.CHAR: 934 // Chars are boxed, as we don't know if the JS code wants to treat 935 // them as an effective "unsigned short" or as a single-char string. 936 CHAR_VALUE_OF.invoke(mv); 937 break; 938 case Type.FLOAT: 939 // Floats are widened to double. 940 mv.visitInsn(Opcodes.F2D); 941 if (varArg) { 942 // We'll be boxing everything anyway for the vararg invocation, 943 // so we might as well do it proactively here and thus not cause 944 // a widening in the number of slots, as that could even make 945 // the array creation invocation go over 255 param slots. 946 DOUBLE_VALUE_OF.invoke(mv); 947 } 948 break; 949 case Type.LONG: 950 // Longs are boxed as Nashorn can't represent them precisely as a 951 // primitive number. 952 LONG_VALUE_OF.invoke(mv); 953 break; 954 case Type.OBJECT: 955 if(t.equals(OBJECT_TYPE)) { 956 // Object can carry a ScriptObjectMirror and needs to be unwrapped 957 // before passing into a Nashorn function. 958 UNWRAP.invoke(mv); 959 } 960 break; 961 } 962 } 963 964 private static int getParamListLengthInSlots(final Type[] paramTypes) { 965 int len = paramTypes.length; 966 for(final Type t: paramTypes) { 967 final int sort = t.getSort(); 968 if (sort == Type.FLOAT || sort == Type.DOUBLE) { 969 // Floats are widened to double, so they'll take up two slots. 970 // Longs on the other hand are always boxed, so their width 971 // becomes 1 and thus they don't contribute an extra slot here. 972 ++len; 973 } 974 } 975 return len; 976 } 977 978 /** 979 * Emit code to restore the previous Nashorn Context when needed. 980 * @param mv the instruction adapter 981 * @param globalRestoringRunnableVar index of the local variable holding the reference to the global restoring Runnable 982 */ 983 private static void emitFinally(final InstructionAdapter mv, final int globalRestoringRunnableVar) { 984 mv.visitVarInsn(ALOAD, globalRestoringRunnableVar); 985 RUN.invoke(mv); 986 } 987 988 private static boolean isThrowableDeclared(final Class<?>[] exceptions) { 989 for (final Class<?> exception : exceptions) { 990 if (exception == Throwable.class) { 991 return true; 992 } 993 } 994 return false; 995 } 996 997 private void generateSuperMethods() { 998 for(final MethodInfo mi: methodInfos) { 999 if(!Modifier.isAbstract(mi.method.getModifiers())) { 1000 generateSuperMethod(mi); 1001 } 1002 } 1003 } 1004 1005 private void generateSuperMethod(final MethodInfo mi) { 1006 final Method method = mi.method; 1007 1008 final String methodDesc = mi.type.toMethodDescriptorString(); 1009 final String name = mi.getName(); 1010 1011 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(getAccessModifiers(method), 1012 SUPER_PREFIX + name, methodDesc, null, getExceptionNames(method.getExceptionTypes()))); 1013 mv.visitCode(); 1014 1015 emitSuperCall(mv, method.getDeclaringClass(), name, methodDesc); 1016 mv.areturn(Type.getType(mi.type.returnType())); 1017 endMethod(mv); 1018 } 1019 1020 // find the appropriate super type to use for invokespecial on the given interface 1021 private Class<?> findInvokespecialOwnerFor(final Class<?> cl) { 1022 assert Modifier.isInterface(cl.getModifiers()) : cl + " is not an interface"; 1023 1024 if (cl.isAssignableFrom(superClass)) { 1025 return superClass; 1026 } 1027 1028 for (final Class<?> iface : interfaces) { 1029 if (cl.isAssignableFrom(iface)) { 1030 return iface; 1031 } 1032 } 1033 1034 // we better that interface that extends the given interface! 1035 throw new AssertionError("can't find the class/interface that extends " + cl); 1036 } 1037 1038 private int emitSuperConstructorCall(final InstructionAdapter mv, final String methodDesc) { 1039 return emitSuperCall(mv, null, INIT, methodDesc, true); 1040 } 1041 1042 private int emitSuperCall(final InstructionAdapter mv, final Class<?> owner, final String name, final String methodDesc) { 1043 return emitSuperCall(mv, owner, name, methodDesc, false); 1044 } 1045 1046 private int emitSuperCall(final InstructionAdapter mv, final Class<?> owner, final String name, final String methodDesc, final boolean constructor) { 1047 mv.visitVarInsn(ALOAD, 0); 1048 int nextParam = 1; 1049 final Type methodType = Type.getMethodType(methodDesc); 1050 for(final Type t: methodType.getArgumentTypes()) { 1051 mv.load(nextParam, t); 1052 nextParam += t.getSize(); 1053 } 1054 1055 // default method - non-abstract, interface method 1056 if (!constructor && Modifier.isInterface(owner.getModifiers())) { 1057 // we should call default method on the immediate "super" type - not on (possibly) 1058 // the indirectly inherited interface class! 1059 final Class<?> superType = findInvokespecialOwnerFor(owner); 1060 mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(superType), name, methodDesc, 1061 Modifier.isInterface(superType.getModifiers())); 1062 } else { 1063 mv.invokespecial(superClassName, name, methodDesc, false); 1064 } 1065 return nextParam; 1066 } 1067 1068 private void generateFinalizerMethods() { 1069 generateFinalizerDelegate(); 1070 generateFinalizerOverride(); 1071 } 1072 1073 private void generateFinalizerDelegate() { 1074 // Generate a delegate that will be invoked from the no-permission trampoline. Note it can be private, as we'll 1075 // refer to it with a MethodHandle constant pool entry in the overridden finalize() method (see 1076 // generateFinalizerOverride()). 1077 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PRIVATE | ACC_STATIC, 1078 FINALIZER_DELEGATE_NAME, FINALIZER_DELEGATE_METHOD_DESCRIPTOR, null, null)); 1079 1080 // Simply invoke super.finalize() 1081 mv.visitVarInsn(ALOAD, 0); 1082 mv.checkcast(Type.getType(generatedClassName)); 1083 mv.invokespecial(superClassName, "finalize", VOID_METHOD_DESCRIPTOR, false); 1084 1085 mv.visitInsn(RETURN); 1086 endMethod(mv); 1087 } 1088 1089 private void generateFinalizerOverride() { 1090 final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, "finalize", 1091 VOID_METHOD_DESCRIPTOR, null, null)); 1092 // Overridden finalizer will take a MethodHandle to the finalizer delegating method, ... 1093 mv.aconst(new Handle(Opcodes.H_INVOKESTATIC, generatedClassName, FINALIZER_DELEGATE_NAME, 1094 FINALIZER_DELEGATE_METHOD_DESCRIPTOR, false)); 1095 mv.visitVarInsn(ALOAD, 0); 1096 // ...and invoke it through JavaAdapterServices.invokeNoPermissions 1097 INVOKE_NO_PERMISSIONS.invoke(mv); 1098 mv.visitInsn(RETURN); 1099 endMethod(mv); 1100 } 1101 1102 private static String[] getExceptionNames(final Class<?>[] exceptions) { 1103 final String[] exceptionNames = new String[exceptions.length]; 1104 for (int i = 0; i < exceptions.length; ++i) { 1105 exceptionNames[i] = Type.getInternalName(exceptions[i]); 1106 } 1107 return exceptionNames; 1108 } 1109 1110 private static int getAccessModifiers(final Method method) { 1111 return ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0); 1112 } 1113 1114 /** 1115 * Gathers methods that can be implemented or overridden from the specified type into this factory's 1116 * {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from 1117 * the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its 1118 * superclass and the interfaces it implements, and add further methods that were not directly declared on the 1119 * class. 1120 * @param type the type defining the methods. 1121 */ 1122 private void gatherMethods(final Class<?> type) throws AdaptationException { 1123 if (Modifier.isPublic(type.getModifiers())) { 1124 final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods(); 1125 1126 for (final Method typeMethod: typeMethods) { 1127 final String name = typeMethod.getName(); 1128 if(name.startsWith(SUPER_PREFIX)) { 1129 continue; 1130 } 1131 final int m = typeMethod.getModifiers(); 1132 if (Modifier.isStatic(m)) { 1133 continue; 1134 } 1135 if (Modifier.isPublic(m) || Modifier.isProtected(m)) { 1136 // Is it a "finalize()"? 1137 if(name.equals("finalize") && typeMethod.getParameterCount() == 0) { 1138 if(type != Object.class) { 1139 hasExplicitFinalizer = true; 1140 if(Modifier.isFinal(m)) { 1141 // Must be able to override an explicit finalizer 1142 throw new AdaptationException(Outcome.ERROR_FINAL_FINALIZER, type.getCanonicalName()); 1143 } 1144 } 1145 continue; 1146 } 1147 1148 final MethodInfo mi = new MethodInfo(typeMethod); 1149 if (Modifier.isFinal(m) || isCallerSensitive(typeMethod)) { 1150 finalMethods.add(mi); 1151 } else if (!finalMethods.contains(mi) && methodInfos.add(mi) && Modifier.isAbstract(m)) { 1152 abstractMethodNames.add(mi.getName()); 1153 } 1154 } 1155 } 1156 } 1157 // If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done. 1158 // Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to 1159 // see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a 1160 // superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and 1161 // getMethods() does provide those declared in a superinterface. 1162 if (!type.isInterface()) { 1163 final Class<?> superType = type.getSuperclass(); 1164 if (superType != null) { 1165 gatherMethods(superType); 1166 } 1167 for (final Class<?> itf: type.getInterfaces()) { 1168 gatherMethods(itf); 1169 } 1170 } 1171 } 1172 1173 private void gatherMethods(final List<Class<?>> classes) throws AdaptationException { 1174 for(final Class<?> c: classes) { 1175 gatherMethods(c); 1176 } 1177 } 1178 1179 private static final AccessControlContext GET_DECLARED_MEMBERS_ACC_CTXT = ClassAndLoader.createPermAccCtxt("accessDeclaredMembers"); 1180 1181 /** 1182 * Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters, 1183 * as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and 1184 * {@code Object.clone()}. 1185 * @return a collection of method infos representing those methods that we never override in adapter classes. 1186 */ 1187 private static Collection<MethodInfo> getExcludedMethods() { 1188 return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() { 1189 @Override 1190 public Collection<MethodInfo> run() { 1191 try { 1192 return Arrays.asList( 1193 new MethodInfo(Object.class, "finalize"), 1194 new MethodInfo(Object.class, "clone")); 1195 } catch (final NoSuchMethodException e) { 1196 throw new AssertionError(e); 1197 } 1198 } 1199 }, GET_DECLARED_MEMBERS_ACC_CTXT); 1200 } 1201 1202 private String getCommonSuperClass(final String type1, final String type2) { 1203 try { 1204 final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader); 1205 final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader); 1206 if (c1.isAssignableFrom(c2)) { 1207 return type1; 1208 } 1209 if (c2.isAssignableFrom(c1)) { 1210 return type2; 1211 } 1212 if (c1.isInterface() || c2.isInterface()) { 1213 return OBJECT_TYPE.getInternalName(); 1214 } 1215 return assignableSuperClass(c1, c2).getName().replace('.', '/'); 1216 } catch(final ClassNotFoundException e) { 1217 throw new RuntimeException(e); 1218 } 1219 } 1220 1221 private static Class<?> assignableSuperClass(final Class<?> c1, final Class<?> c2) { 1222 final Class<?> superClass = c1.getSuperclass(); 1223 return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2); 1224 } 1225 1226 private static boolean isCallerSensitive(final AccessibleObject e) { 1227 return e.isAnnotationPresent(CallerSensitive.class); 1228 } 1229 1230 private static Call lookupServiceMethod(final String name, final Class<?> rtype, final Class<?>... ptypes) { 1231 return staticCallNoLookup(JavaAdapterServices.class, name, rtype, ptypes); 1232 } 1233 }