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