author | sundar |
Tue, 12 Mar 2013 18:12:42 +0530 | |
changeset 16522 | d643e3ee819c |
parent 16277 | fd698c5ee684 |
child 16525 | 1409942e618e |
permissions | -rw-r--r-- |
16147 | 1 |
/* |
16151 | 2 |
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. |
16147 | 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.ACONST_NULL; |
|
35 |
import static jdk.internal.org.objectweb.asm.Opcodes.ALOAD; |
|
36 |
import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; |
|
37 |
import static jdk.internal.org.objectweb.asm.Opcodes.ASTORE; |
|
38 |
import static jdk.internal.org.objectweb.asm.Opcodes.DUP; |
|
39 |
import static jdk.internal.org.objectweb.asm.Opcodes.IFNONNULL; |
|
40 |
import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; |
|
41 |
import static jdk.internal.org.objectweb.asm.Opcodes.ISTORE; |
|
42 |
import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; |
|
43 |
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; |
|
16277 | 44 |
import static jdk.nashorn.internal.lookup.Lookup.MH; |
16147 | 45 |
|
46 |
import java.lang.invoke.MethodHandle; |
|
47 |
import java.lang.invoke.MethodType; |
|
48 |
import java.lang.reflect.Constructor; |
|
49 |
import java.lang.reflect.Method; |
|
50 |
import java.lang.reflect.Modifier; |
|
51 |
import java.security.AccessController; |
|
52 |
import java.security.AllPermission; |
|
53 |
import java.security.CodeSigner; |
|
54 |
import java.security.CodeSource; |
|
55 |
import java.security.Permissions; |
|
56 |
import java.security.PrivilegedAction; |
|
16522 | 57 |
import java.security.PrivilegedExceptionAction; |
16147 | 58 |
import java.security.ProtectionDomain; |
59 |
import java.security.SecureClassLoader; |
|
60 |
import java.security.SecureRandom; |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
61 |
import java.util.ArrayList; |
16147 | 62 |
import java.util.Arrays; |
63 |
import java.util.Collection; |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
64 |
import java.util.Collections; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
65 |
import java.util.HashMap; |
16147 | 66 |
import java.util.HashSet; |
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
67 |
import java.util.Iterator; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
68 |
import java.util.LinkedHashMap; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
69 |
import java.util.LinkedList; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
70 |
import java.util.List; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
71 |
import java.util.Map; |
16147 | 72 |
import java.util.Random; |
73 |
import java.util.Set; |
|
16234
86cb162cec6c
8008085: Integrate Dynalink source code into Nashorn codebase
attila
parents:
16221
diff
changeset
|
74 |
import jdk.internal.dynalink.beans.StaticClass; |
86cb162cec6c
8008085: Integrate Dynalink source code into Nashorn codebase
attila
parents:
16221
diff
changeset
|
75 |
import jdk.internal.dynalink.support.LinkRequestImpl; |
16147 | 76 |
import jdk.internal.org.objectweb.asm.ClassWriter; |
77 |
import jdk.internal.org.objectweb.asm.Label; |
|
78 |
import jdk.internal.org.objectweb.asm.Opcodes; |
|
79 |
import jdk.internal.org.objectweb.asm.Type; |
|
80 |
import jdk.internal.org.objectweb.asm.commons.InstructionAdapter; |
|
81 |
import jdk.nashorn.internal.objects.NativeJava; |
|
82 |
import jdk.nashorn.internal.runtime.Context; |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
83 |
import jdk.nashorn.internal.runtime.ECMAErrors; |
16147 | 84 |
import jdk.nashorn.internal.runtime.ECMAException; |
85 |
import jdk.nashorn.internal.runtime.ScriptFunction; |
|
86 |
import jdk.nashorn.internal.runtime.ScriptObject; |
|
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
87 |
import jdk.nashorn.internal.runtime.ScriptRuntime; |
16147 | 88 |
import jdk.nashorn.internal.runtime.Undefined; |
89 |
||
90 |
/** |
|
16272 | 91 |
* <p>A factory class that generates adapter classes. Adapter classes allow implementation of Java interfaces and |
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
92 |
* extending of Java classes from JavaScript. For every combination of a superclass to extend and interfaces to |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
93 |
* implement (collectively: "original types"), exactly one adapter class is generated that extends the specified |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
94 |
* superclass and implements the specified interfaces. |
16147 | 95 |
* </p><p> |
96 |
* The adapter class is generated in a new secure class loader that inherits Nashorn's protection domain, and has either |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
97 |
* one of the original types' class loader or the Nashorn's class loader as its parent - the parent class loader |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
98 |
* is chosen so that all the original types and the Nashorn core classes are visible from it (as the adapter will have |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
99 |
* constant pool references to ScriptObject and ScriptFunction classes). In case none of the candidate class loaders has |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
100 |
* visibility of all the required types, an error is thrown. |
16147 | 101 |
* </p><p> |
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
102 |
* For every protected or public constructor in the extended class, the adapter class will have one or two public |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
103 |
* constructors (visibility of protected constructors in the extended class is promoted to public). In every case, for |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
104 |
* every original constructor, a new constructor taking a trailing ScriptObject argument preceded by original |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
105 |
* constructor arguments is present on the adapter class. When such a constructor is invoked, the passed ScriptObject's |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
106 |
* member functions are used to implement and/or override methods on the original class, dispatched by name. A single |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
107 |
* JavaScript function will act as the implementation for all overloaded methods of the same name. When methods on an |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
108 |
* adapter instance are invoked, the functions are invoked having the ScriptObject passed in the instance constructor as |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
109 |
* their "this". Subsequent changes to the ScriptObject (reassignment or removal of its functions) are not reflected in |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
110 |
* the adapter instance; the method implementations are bound to functions at constructor invocation time. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
111 |
* {@code java.lang.Object} methods {@code equals}, {@code hashCode}, and {@code toString} can also be overridden. The |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
112 |
* only restriction is that since every JavaScript object already has a {@code toString} function through the |
16147 | 113 |
* {@code Object.prototype}, the {@code toString} in the adapter is only overridden if the passed ScriptObject has a |
114 |
* {@code toString} function as its own property, and not inherited from a prototype. All other adapter methods can be |
|
115 |
* implemented or overridden through a prototype-inherited function of the ScriptObject passed to the constructor too. |
|
116 |
* </p><p> |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
117 |
* If the original types collectively have only one abstract method, or have several of them, but all share the |
16147 | 118 |
* same name, an additional constructor is provided for every original constructor; this one takes a ScriptFunction as |
16167
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
119 |
* its last argument preceded by original constructor arguments. This constructor will use the passed function as the |
16147 | 120 |
* implementation for all abstract methods. For consistency, any concrete methods sharing the single abstract method |
121 |
* name will also be overridden by the function. When methods on the adapter instance are invoked, the ScriptFunction is |
|
122 |
* invoked with {@code null} as its "this". |
|
16167
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
123 |
* </p><p> |
16147 | 124 |
* For adapter methods that return values, all the JavaScript-to-Java conversions supported by Nashorn will be in effect |
125 |
* to coerce the JavaScript function return value to the expected Java return type. |
|
126 |
* </p><p> |
|
16167
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
127 |
* Since we are adding a trailing argument to the generated constructors in the adapter class, they will never be |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
128 |
* declared as variable arity, even if the original constructor in the superclass was declared as variable arity. The |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
129 |
* reason we are passing the additional argument at the end of the argument list instead at the front is that the |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
130 |
* source-level script expression <code>new X(a, b) { ... }</code> (which is a proprietary syntax extension Nashorn uses |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
131 |
* to resemble Java anonymous classes) is actually equivalent to <code>new X(a, b, { ... })</code>. |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
132 |
* </p><p> |
16147 | 133 |
* You normally don't use this class directly, but rather either create adapters from script using |
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
134 |
* {@link NativeJava#extend(Object, Object...)}, using the {@code new} operator on abstract classes and interfaces (see |
16147 | 135 |
* {@link NativeJava#type(Object, Object)}), or implicitly when passing script functions to Java methods expecting SAM |
136 |
* types. |
|
137 |
* </p> |
|
138 |
*/ |
|
139 |
||
16185 | 140 |
public final class JavaAdapterFactory { |
16147 | 141 |
private static final Type SCRIPT_FUNCTION_TYPE = Type.getType(ScriptFunction.class); |
142 |
private static final Type SCRIPT_OBJECT_TYPE = Type.getType(ScriptObject.class); |
|
143 |
private static final Type OBJECT_TYPE = Type.getType(Object.class); |
|
144 |
private static final Type STRING_TYPE = Type.getType(String.class); |
|
145 |
private static final Type CONTEXT_TYPE = Type.getType(Context.class); |
|
146 |
private static final Type METHOD_TYPE_TYPE = Type.getType(MethodType.class); |
|
147 |
private static final Type METHOD_HANDLE_TYPE = Type.getType(MethodHandle.class); |
|
148 |
private static final String GET_HANDLE_OBJECT_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE, |
|
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
149 |
OBJECT_TYPE, STRING_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE); |
16147 | 150 |
private static final String GET_HANDLE_FUNCTION_DESCRIPTOR = Type.getMethodDescriptor(METHOD_HANDLE_TYPE, |
151 |
SCRIPT_FUNCTION_TYPE, METHOD_TYPE_TYPE, Type.BOOLEAN_TYPE); |
|
152 |
private static final Type RUNTIME_EXCEPTION_TYPE = Type.getType(RuntimeException.class); |
|
153 |
private static final Type THROWABLE_TYPE = Type.getType(Throwable.class); |
|
154 |
private static final Type PRIVILEGED_ACTION_TYPE = Type.getType(PrivilegedAction.class); |
|
155 |
private static final Type UNSUPPORTED_OPERATION_TYPE = Type.getType(UnsupportedOperationException.class); |
|
156 |
||
157 |
private static final String THIS_CLASS_TYPE_NAME = Type.getInternalName(JavaAdapterFactory.class); |
|
158 |
private static final String RUNTIME_EXCEPTION_TYPE_NAME = RUNTIME_EXCEPTION_TYPE.getInternalName(); |
|
159 |
private static final String ERROR_TYPE_NAME = Type.getInternalName(Error.class); |
|
160 |
private static final String THROWABLE_TYPE_NAME = THROWABLE_TYPE.getInternalName(); |
|
161 |
private static final String CONTEXT_TYPE_NAME = CONTEXT_TYPE.getInternalName(); |
|
162 |
private static final String OBJECT_TYPE_NAME = OBJECT_TYPE.getInternalName(); |
|
163 |
private static final String PRIVILEGED_ACTION_TYPE_NAME = PRIVILEGED_ACTION_TYPE.getInternalName(); |
|
164 |
private static final String UNSUPPORTED_OPERATION_TYPE_NAME = UNSUPPORTED_OPERATION_TYPE.getInternalName(); |
|
165 |
||
166 |
private static final String METHOD_HANDLE_TYPE_DESCRIPTOR = METHOD_HANDLE_TYPE.getDescriptor(); |
|
167 |
private static final String SCRIPT_OBJECT_TYPE_DESCRIPTOR = SCRIPT_OBJECT_TYPE.getDescriptor(); |
|
168 |
private static final String GET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(SCRIPT_OBJECT_TYPE); |
|
169 |
private static final String SET_GLOBAL_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.VOID_TYPE, SCRIPT_OBJECT_TYPE); |
|
170 |
private static final String GET_CLASS_METHOD_DESCRIPTOR = Type.getMethodDescriptor(Type.getType(Class.class)); |
|
171 |
private static final String PRIVILEGED_RUN_METHOD_DESCRIPTOR = Type.getMethodDescriptor(OBJECT_TYPE); |
|
172 |
||
173 |
// Package used when the adapter can't be defined in the adaptee's package (either because it's sealed, or because |
|
174 |
// it's a java.* package. |
|
175 |
private static final String ADAPTER_PACKAGE_PREFIX = "jdk/nashorn/internal/javaadapters/"; |
|
176 |
// Class name suffix used to append to the adaptee class name, when it can be defined in the adaptee's package. |
|
177 |
private static final String ADAPTER_CLASS_NAME_SUFFIX = "$$NashornJavaAdapter"; |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
178 |
private static final String JAVA_PACKAGE_PREFIX = "java/"; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
179 |
private static final int MAX_GENERATED_TYPE_NAME_LENGTH = 238; //255 - 17; 17 is the maximum possible length for the global setter inner class suffix |
16147 | 180 |
|
181 |
private static final String INIT = "<init>"; |
|
182 |
private static final String VOID_NOARG = Type.getMethodDescriptor(Type.VOID_TYPE); |
|
183 |
private static final String GLOBAL_FIELD_NAME = "global"; |
|
184 |
||
185 |
/** |
|
186 |
* Contains various outcomes for attempting to generate an adapter class. These are stored in AdapterInfo instances. |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
187 |
* We have a successful outcome (adapter class was generated) and four possible error outcomes: superclass is final, |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
188 |
* superclass is not public, superclass has no public or protected constructor, more than one superclass was |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
189 |
* specified. We don't throw exceptions when we try to generate the adapter, but rather just record these error |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
190 |
* conditions as they are still useful as partial outcomes, as Nashorn's linker can still successfully check whether |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
191 |
* the class can be autoconverted from a script function even when it is not possible to generate an adapter for it. |
16147 | 192 |
*/ |
193 |
private enum AdaptationOutcome { |
|
194 |
SUCCESS, |
|
195 |
ERROR_FINAL_CLASS, |
|
196 |
ERROR_NON_PUBLIC_CLASS, |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
197 |
ERROR_NO_ACCESSIBLE_CONSTRUCTOR, |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
198 |
ERROR_MULTIPLE_SUPERCLASSES, |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
199 |
ERROR_NO_COMMON_LOADER |
16147 | 200 |
} |
201 |
||
202 |
/** |
|
203 |
* Collection of methods we never override: Object.clone(), Object.finalize(). |
|
204 |
*/ |
|
205 |
private static final Collection<MethodInfo> EXCLUDED = getExcludedMethods(); |
|
206 |
||
207 |
/** |
|
208 |
* A mapping from an original Class object to AdapterInfo representing the adapter for the class it represents. |
|
209 |
*/ |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
210 |
private static final ClassValue<Map<List<Class<?>>, AdapterInfo>> ADAPTER_INFO_MAPS = new ClassValue<Map<List<Class<?>>, AdapterInfo>>() { |
16147 | 211 |
@Override |
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
212 |
protected Map<List<Class<?>>, AdapterInfo> computeValue(final Class<?> type) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
213 |
return new HashMap<>(); |
16147 | 214 |
} |
215 |
}; |
|
216 |
||
217 |
private static final Random random = new SecureRandom(); |
|
218 |
private static final ProtectionDomain GENERATED_PROTECTION_DOMAIN = createGeneratedProtectionDomain(); |
|
219 |
||
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
220 |
// This is the superclass for our generated adapter. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
221 |
private final Class<?> superClass; |
16147 | 222 |
// Class loader used as the parent for the class loader we'll create to load the generated class. It will be a class |
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
223 |
// loader that has the visibility of all original types (class to extend and interfaces to implement) and of the |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
224 |
// Nashorn classes. |
16147 | 225 |
private final ClassLoader commonLoader; |
226 |
||
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
227 |
// Binary name of the superClass |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
228 |
private final String superClassName; |
16147 | 229 |
// Binary name of the generated class. |
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
230 |
private final String generatedClassName; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
231 |
// Binary name of the PrivilegedAction inner class that is used to |
16147 | 232 |
private final String globalSetterClassName; |
233 |
private final Set<String> usedFieldNames = new HashSet<>(); |
|
234 |
private final Set<String> abstractMethodNames = new HashSet<>(); |
|
235 |
private final String samName; |
|
236 |
private final Set<MethodInfo> finalMethods = new HashSet<>(EXCLUDED); |
|
237 |
private final Set<MethodInfo> methodInfos = new HashSet<>(); |
|
238 |
private boolean autoConvertibleFromFunction = false; |
|
239 |
||
240 |
private final ClassWriter cw; |
|
241 |
||
242 |
/** |
|
243 |
* Creates a factory that will produce the adapter type for the specified original type. |
|
244 |
* @param originalType the type for which this factory will generate the adapter type. |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
245 |
* @param definingClassAndLoader the class in whose ClassValue we'll store the generated adapter, and its class loader. |
16147 | 246 |
* @throws AdaptationException if the adapter can not be generated for some reason. |
247 |
*/ |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
248 |
private JavaAdapterFactory(final Class<?> superType, final List<Class<?>> interfaces, final ClassAndLoader definingClassAndLoader) throws AdaptationException { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
249 |
assert superType != null && !superType.isInterface(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
250 |
assert interfaces != null; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
251 |
assert definingClassAndLoader != null; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
252 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
253 |
this.superClass = superType; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
254 |
this.commonLoader = findCommonLoader(definingClassAndLoader); |
16147 | 255 |
cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS) { |
256 |
@Override |
|
257 |
protected String getCommonSuperClass(final String type1, final String type2) { |
|
258 |
// We need to override ClassWriter.getCommonSuperClass to use this factory's commonLoader as a class |
|
259 |
// loader to find the common superclass of two types when needed. |
|
260 |
return JavaAdapterFactory.this.getCommonSuperClass(type1, type2); |
|
261 |
} |
|
262 |
}; |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
263 |
superClassName = Type.getInternalName(superType); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
264 |
generatedClassName = getGeneratedClassName(superType, interfaces); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
265 |
|
16147 | 266 |
// Randomize the name of the privileged global setter, to make it non-feasible to find. |
267 |
final long l; |
|
268 |
synchronized(random) { |
|
269 |
l = random.nextLong(); |
|
270 |
} |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
271 |
// NOTE: they way this class name is calculated affects the value of MAX_GENERATED_TYPE_NAME_LENGTH constant. If |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
272 |
// you change the calculation of globalSetterClassName, adjust the constant too. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
273 |
globalSetterClassName = generatedClassName.concat("$" + Long.toHexString(l & Long.MAX_VALUE)); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
274 |
cw.visit(Opcodes.V1_7, ACC_PUBLIC | ACC_SUPER | ACC_FINAL, generatedClassName, null, superClassName, getInternalTypeNames(interfaces)); |
16147 | 275 |
cw.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd(); |
276 |
usedFieldNames.add(GLOBAL_FIELD_NAME); |
|
277 |
||
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
278 |
gatherMethods(superType); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
279 |
gatherMethods(interfaces); |
16147 | 280 |
samName = abstractMethodNames.size() == 1 ? abstractMethodNames.iterator().next() : null; |
281 |
generateFields(); |
|
282 |
generateConstructors(); |
|
283 |
generateMethods(); |
|
284 |
// } |
|
285 |
cw.visitEnd(); |
|
286 |
} |
|
287 |
||
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
288 |
private static String getGeneratedClassName(final Class<?> superType, final List<Class<?>> interfaces) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
289 |
// The class we use to primarily name our adapter is either the superclass, or if it is Object (meaning we're |
16204
c53eef0eb729
8007140: Java.extend crashes when attempting to extend java.lang.Object
sundar
parents:
16188
diff
changeset
|
290 |
// just implementing interfaces or extending Object), then the first implemented interface or Object. |
c53eef0eb729
8007140: Java.extend crashes when attempting to extend java.lang.Object
sundar
parents:
16188
diff
changeset
|
291 |
final Class<?> namingType = superType == Object.class ? (interfaces.isEmpty()? Object.class : interfaces.get(0)) : superType; |
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
292 |
final Package pkg = namingType.getPackage(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
293 |
final String namingTypeName = Type.getInternalName(namingType); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
294 |
final StringBuilder buf = new StringBuilder(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
295 |
if (namingTypeName.startsWith(JAVA_PACKAGE_PREFIX) || pkg == null || pkg.isSealed()) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
296 |
// Can't define new classes in java.* packages |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
297 |
buf.append(ADAPTER_PACKAGE_PREFIX).append(namingTypeName); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
298 |
} else { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
299 |
buf.append(namingTypeName).append(ADAPTER_CLASS_NAME_SUFFIX); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
300 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
301 |
final Iterator<Class<?>> it = interfaces.iterator(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
302 |
if(superType == Object.class && it.hasNext()) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
303 |
it.next(); // Skip first interface, it was used to primarily name the adapter |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
304 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
305 |
// Append interface names to the adapter name |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
306 |
while(it.hasNext()) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
307 |
buf.append("$$").append(it.next().getSimpleName()); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
308 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
309 |
return buf.toString().substring(0, Math.min(MAX_GENERATED_TYPE_NAME_LENGTH, buf.length())); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
310 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
311 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
312 |
/** |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
313 |
* Given a list of class objects, return an array with their binary names. Used to generate the array of interface |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
314 |
* names to implement. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
315 |
* @param classes the classes |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
316 |
* @return an array of names |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
317 |
*/ |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
318 |
private static String[] getInternalTypeNames(final List<Class<?>> classes) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
319 |
final int interfaceCount = classes.size(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
320 |
final String[] interfaceNames = new String[interfaceCount]; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
321 |
for(int i = 0; i < interfaceCount; ++i) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
322 |
interfaceNames[i] = Type.getInternalName(classes.get(i)); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
323 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
324 |
return interfaceNames; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
325 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
326 |
|
16147 | 327 |
/** |
328 |
* Utility method used by few other places in the code. Tests if the class has the abstract modifier and is not an |
|
329 |
* array class. For some reason, array classes have the abstract modifier set in HotSpot JVM, and we don't want to |
|
330 |
* treat array classes as abstract. |
|
331 |
* @param clazz the inspected class |
|
332 |
* @return true if the class is abstract and is not an array type. |
|
333 |
*/ |
|
334 |
static boolean isAbstractClass(final Class<?> clazz) { |
|
335 |
return Modifier.isAbstract(clazz.getModifiers()) && !clazz.isArray(); |
|
336 |
} |
|
337 |
||
338 |
/** |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
339 |
* Returns an adapter class for the specified original types. The adapter class extends/implements the original |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
340 |
* class/interfaces. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
341 |
* @param types the original types. The caller must pass at least one Java type representing either a public |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
342 |
* interface or a non-final public class with at least one public or protected constructor. If more than one type is |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
343 |
* specified, at most one can be a class and the rest have to be interfaces. The class can be in any position in the |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
344 |
* array. Invoking the method twice with exactly the same types in the same order will return the same adapter |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
345 |
* class, any reordering of types or even addition or removal of redundant types (i.e. interfaces that other types |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
346 |
* in the list already implement/extend, or {@code java.lang.Object} in a list of types consisting purely of |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
347 |
* interfaces) will result in a different adapter class, even though those adapter classes are functionally |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
348 |
* identical; we deliberately don't want to incur the additional processing cost of canonicalizing type lists. |
16147 | 349 |
* @return an adapter class. See this class' documentation for details on the generated adapter class. |
350 |
* @throws ECMAException with a TypeError if the adapter class can not be generated because the original class is |
|
351 |
* final, non-public, or has no public or protected constructors. |
|
352 |
*/ |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
353 |
public static StaticClass getAdapterClassFor(final Class<?>[] types) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
354 |
assert types != null && types.length > 0; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
355 |
final AdapterInfo adapterInfo = getAdapterInfo(types); |
16147 | 356 |
|
357 |
final StaticClass clazz = adapterInfo.adapterClass; |
|
358 |
if (clazz != null) { |
|
359 |
return clazz; |
|
360 |
} |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
361 |
adapterInfo.adaptationOutcome.typeError(); |
16147 | 362 |
|
363 |
throw new AssertionError(); |
|
364 |
} |
|
365 |
||
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
366 |
private static AdapterInfo getAdapterInfo(final Class<?>[] types) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
367 |
final ClassAndLoader definingClassAndLoader = getDefiningClassAndLoader(types); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
368 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
369 |
final Map<List<Class<?>>, AdapterInfo> adapterInfoMap = ADAPTER_INFO_MAPS.get(definingClassAndLoader.clazz); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
370 |
final List<Class<?>> typeList = types.length == 1 ? getSingletonClassList(types[0]) : Arrays.asList(types.clone()); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
371 |
AdapterInfo adapterInfo; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
372 |
synchronized(adapterInfoMap) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
373 |
adapterInfo = adapterInfoMap.get(typeList); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
374 |
if(adapterInfo == null) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
375 |
adapterInfo = createAdapterInfo(types, definingClassAndLoader); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
376 |
adapterInfoMap.put(typeList, adapterInfo); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
377 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
378 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
379 |
return adapterInfo; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
380 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
381 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
382 |
@SuppressWarnings({ "unchecked", "rawtypes" }) |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
383 |
private static List<Class<?>> getSingletonClassList(final Class<?> clazz) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
384 |
return (List)Collections.singletonList(clazz); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
385 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
386 |
|
16147 | 387 |
/** |
388 |
* Returns whether an instance of the specified class/interface can be generated from a ScriptFunction. Returns true |
|
389 |
* iff: the adapter for the class/interface can be created, it is abstract (this includes interfaces), it has at |
|
390 |
* least one abstract method, all the abstract methods share the same name, and it has a public or protected default |
|
391 |
* constructor. Note that invoking this class will most likely result in the adapter class being defined in the JVM |
|
392 |
* if it hasn't been already. |
|
393 |
* @param clazz the inspected class |
|
394 |
* @return true iff an instance of the specified class/interface can be generated from a ScriptFunction. |
|
395 |
*/ |
|
396 |
static boolean isAutoConvertibleFromFunction(final Class<?> clazz) { |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
397 |
return getAdapterInfo(new Class<?>[] { clazz }).autoConvertibleFromFunction; |
16147 | 398 |
} |
399 |
||
400 |
/** |
|
401 |
* Returns a method handle representing a constructor that takes a single argument of the source type (which, |
|
402 |
* really, should be one of {@link ScriptObject}, {@link ScriptFunction}, or {@link Object}, and returns an instance |
|
403 |
* of the adapter for the target type. Used to implement the function autoconverters as well as the Nashorn's |
|
404 |
* JSR-223 script engine's {@code getInterface()} method. |
|
405 |
* @param sourceType the source type; should be either {@link ScriptObject}, {@link ScriptFunction}, or |
|
406 |
* {@link Object}. In case of {@code Object}, it will return a method handle that dispatches to either the script |
|
407 |
* object or function constructor at invocation based on the actual argument. |
|
408 |
* @param targetType the target type, for which adapter instances will be created |
|
409 |
* @return the constructor method handle. |
|
410 |
* @throws Exception if anything goes wrong |
|
411 |
*/ |
|
412 |
public static MethodHandle getConstructor(final Class<?> sourceType, final Class<?> targetType) throws Exception { |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
413 |
final StaticClass adapterClass = getAdapterClassFor(new Class<?>[] { targetType }); |
16522 | 414 |
return AccessController.doPrivileged(new PrivilegedExceptionAction<MethodHandle>() { |
415 |
public MethodHandle run() throws Exception { |
|
416 |
return MH.bindTo(Bootstrap.getLinkerServices().getGuardedInvocation(new LinkRequestImpl(NashornCallSiteDescriptor.get( |
|
417 |
"dyn:new", MethodType.methodType(targetType, StaticClass.class, sourceType), 0), false, |
|
418 |
adapterClass, null)).getInvocation(), adapterClass); |
|
419 |
} |
|
420 |
}); |
|
16147 | 421 |
} |
422 |
||
423 |
/** |
|
424 |
* Finishes the bytecode generation for the adapter class that was started in the constructor, and loads the |
|
425 |
* bytecode as a new class into the JVM. |
|
426 |
* @return the generated adapter class |
|
427 |
*/ |
|
428 |
private Class<?> generateClass() { |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
429 |
final String binaryName = generatedClassName.replace('/', '.'); |
16147 | 430 |
try { |
431 |
return Class.forName(binaryName, true, createClassLoader(commonLoader, binaryName, cw.toByteArray(), |
|
432 |
globalSetterClassName.replace('/', '.'))); |
|
433 |
} catch (final ClassNotFoundException e) { |
|
434 |
throw new AssertionError(e); // cannot happen |
|
435 |
} |
|
436 |
} |
|
437 |
||
16188 | 438 |
/** |
439 |
* Tells if the given Class is an adapter or support class |
|
440 |
* @param clazz Class object |
|
441 |
* @return true if the Class given is adapter or support class |
|
442 |
*/ |
|
443 |
public static boolean isAdapterClass(Class<?> clazz) { |
|
444 |
return clazz.getClassLoader() instanceof AdapterLoader; |
|
445 |
} |
|
446 |
||
447 |
private static class AdapterLoader extends SecureClassLoader { |
|
448 |
AdapterLoader(ClassLoader parent) { |
|
449 |
super(parent); |
|
450 |
} |
|
451 |
} |
|
452 |
||
16147 | 453 |
// Creation of class loader is in a separate static method so that it doesn't retain a reference to the factory |
454 |
// instance. Note that the adapter class is created in the protection domain of the class/interface being |
|
455 |
// extended/implemented, and only the privileged global setter action class is generated in the protection domain |
|
456 |
// of Nashorn itself. Also note that the creation and loading of the global setter is deferred until it is |
|
457 |
// required by JVM linker, which will only happen on first invocation of any of the adapted method. We could defer |
|
458 |
// it even more by separating its invocation into a separate static method on the adapter class, but then someone |
|
459 |
// with ability to introspect on the class and use setAccessible(true) on it could invoke the method. It's a |
|
460 |
// security tradeoff... |
|
461 |
private static ClassLoader createClassLoader(final ClassLoader parentLoader, final String className, |
|
462 |
final byte[] classBytes, final String privilegedActionClassName) { |
|
16188 | 463 |
return new AdapterLoader(parentLoader) { |
16522 | 464 |
private final ClassLoader myLoader = getClass().getClassLoader(); |
16221
38ac51eba133
8007715: Make sure that not all tests run with AllPermission
sundar
parents:
16204
diff
changeset
|
465 |
private final ProtectionDomain myProtectionDomain = getClass().getProtectionDomain(); |
38ac51eba133
8007715: Make sure that not all tests run with AllPermission
sundar
parents:
16204
diff
changeset
|
466 |
|
16147 | 467 |
@Override |
16522 | 468 |
public Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException { |
469 |
try { |
|
470 |
return super.loadClass(name, resolve); |
|
471 |
} catch (final SecurityException se) { |
|
472 |
// we may be implementing an interface or extending a class that was |
|
473 |
// loaded by a loader that prevents package.access. If so, it'd throw |
|
474 |
// SecurityException for nashorn's classes!. For adapter's to work, we |
|
475 |
// should be able to refer to nashorn classes. |
|
476 |
if (name.startsWith("jdk.nashorn.internal.")) { |
|
477 |
return myLoader.loadClass(name); |
|
478 |
} |
|
479 |
throw se; |
|
480 |
} |
|
481 |
} |
|
482 |
||
483 |
@Override |
|
16147 | 484 |
protected Class<?> findClass(final String name) throws ClassNotFoundException { |
485 |
if(name.equals(className)) { |
|
486 |
final byte[] bytes = classBytes; |
|
487 |
return defineClass(name, bytes, 0, bytes.length, GENERATED_PROTECTION_DOMAIN); |
|
488 |
} else if(name.equals(privilegedActionClassName)) { |
|
489 |
final byte[] bytes = generatePrivilegedActionClassBytes(privilegedActionClassName.replace('.', '/')); |
|
16221
38ac51eba133
8007715: Make sure that not all tests run with AllPermission
sundar
parents:
16204
diff
changeset
|
490 |
return defineClass(name, bytes, 0, bytes.length, myProtectionDomain); |
16147 | 491 |
} else { |
492 |
throw new ClassNotFoundException(name); |
|
493 |
} |
|
494 |
} |
|
495 |
}; |
|
496 |
} |
|
497 |
||
498 |
/** |
|
499 |
* Generates a PrivilegedAction implementation class for invoking {@link Context#setGlobal(ScriptObject)} from the |
|
500 |
* adapter class. |
|
501 |
*/ |
|
502 |
private static byte[] generatePrivilegedActionClassBytes(final String className) { |
|
503 |
final ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); |
|
504 |
// class GlobalSetter implements PrivilegedAction { |
|
505 |
w.visit(Opcodes.V1_7, ACC_SUPER | ACC_FINAL, className, null, OBJECT_TYPE_NAME, new String[] { |
|
506 |
PRIVILEGED_ACTION_TYPE_NAME |
|
507 |
}); |
|
508 |
||
509 |
// private final ScriptObject global; |
|
510 |
w.visitField(ACC_PRIVATE | ACC_FINAL, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, null).visitEnd(); |
|
511 |
||
16185 | 512 |
// private GlobalSetter(ScriptObject global) { |
16147 | 513 |
InstructionAdapter mv = new InstructionAdapter(w.visitMethod(ACC_PRIVATE, INIT, |
514 |
SET_GLOBAL_METHOD_DESCRIPTOR, null, new String[0])); |
|
515 |
mv.visitCode(); |
|
516 |
// super(); |
|
517 |
mv.visitVarInsn(ALOAD, 0); |
|
518 |
mv.invokespecial(OBJECT_TYPE_NAME, INIT, VOID_NOARG); |
|
519 |
// this.global = global; |
|
520 |
mv.visitVarInsn(ALOAD, 0); |
|
521 |
mv.visitVarInsn(ALOAD, 1); |
|
522 |
mv.putfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); |
|
523 |
||
524 |
mv.visitInsn(RETURN); |
|
525 |
mv.visitEnd(); |
|
526 |
mv.visitMaxs(0, 0); |
|
527 |
||
528 |
// public Object run() { |
|
529 |
mv = new InstructionAdapter(w.visitMethod(ACC_PUBLIC, "run", PRIVILEGED_RUN_METHOD_DESCRIPTOR, null, |
|
530 |
new String[0])); |
|
531 |
mv.visitCode(); |
|
532 |
// Context.setGlobal(this.global); |
|
533 |
mv.visitVarInsn(ALOAD, 0); |
|
534 |
mv.getfield(className, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); |
|
535 |
mv.invokestatic(CONTEXT_TYPE_NAME, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR); |
|
536 |
// return null; |
|
537 |
mv.visitInsn(ACONST_NULL); |
|
538 |
mv.visitInsn(ARETURN); |
|
539 |
||
540 |
mv.visitEnd(); |
|
541 |
mv.visitMaxs(0, 0); |
|
542 |
||
543 |
// static void setGlobal(ScriptObject global) { |
|
544 |
mv = new InstructionAdapter(w.visitMethod(ACC_STATIC, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR, null, |
|
545 |
new String[0])); |
|
546 |
mv.visitCode(); |
|
547 |
// new GlobalSetter(ScriptObject global) |
|
548 |
mv.anew(Type.getType("L" + className + ";")); |
|
549 |
mv.dup(); |
|
550 |
mv.visitVarInsn(ALOAD, 0); |
|
551 |
mv.invokespecial(className, INIT, SET_GLOBAL_METHOD_DESCRIPTOR); |
|
552 |
// AccessController.doPrivileged(...) |
|
553 |
mv.invokestatic(Type.getInternalName(AccessController.class), "doPrivileged", Type.getMethodDescriptor( |
|
554 |
OBJECT_TYPE, PRIVILEGED_ACTION_TYPE)); |
|
555 |
mv.pop(); |
|
556 |
mv.visitInsn(RETURN); |
|
557 |
||
558 |
mv.visitEnd(); |
|
559 |
mv.visitMaxs(0, 0); |
|
560 |
||
561 |
return w.toByteArray(); |
|
562 |
} |
|
563 |
||
564 |
private void generateFields() { |
|
565 |
for (final MethodInfo mi: methodInfos) { |
|
566 |
cw.visitField(ACC_PRIVATE | ACC_FINAL, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR, null, null).visitEnd(); |
|
567 |
} |
|
568 |
} |
|
569 |
||
570 |
private void generateConstructors() throws AdaptationException { |
|
571 |
boolean gotCtor = false; |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
572 |
for (final Constructor<?> ctor: superClass.getDeclaredConstructors()) { |
16147 | 573 |
final int modifier = ctor.getModifiers(); |
574 |
if((modifier & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) { |
|
575 |
generateConstructor(ctor); |
|
576 |
gotCtor = true; |
|
577 |
} |
|
578 |
} |
|
579 |
if(!gotCtor) { |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
580 |
throw new AdaptationException(AdaptationOutcome.ERROR_NO_ACCESSIBLE_CONSTRUCTOR, superClass.getCanonicalName()); |
16147 | 581 |
} |
582 |
} |
|
583 |
||
584 |
boolean isAutoConvertibleFromFunction() { |
|
585 |
return autoConvertibleFromFunction; |
|
586 |
} |
|
587 |
||
588 |
private void generateConstructor(final Constructor<?> ctor) { |
|
589 |
// Generate a constructor that delegates to ctor, but takes an additional ScriptObject parameter at the |
|
590 |
// beginning of its parameter list. |
|
591 |
generateConstructor(ctor, false); |
|
592 |
||
593 |
if (samName != null) { |
|
594 |
if (!autoConvertibleFromFunction && ctor.getParameterTypes().length == 0) { |
|
595 |
// If the original type only has a single abstract method name, as well as a default ctor, then it can |
|
596 |
// be automatically converted from JS function. |
|
597 |
autoConvertibleFromFunction = true; |
|
598 |
} |
|
599 |
// If all our abstract methods have a single name, generate an additional constructor, one that takes a |
|
600 |
// ScriptFunction as its first parameter and assigns it as the implementation for all abstract methods. |
|
601 |
generateConstructor(ctor, true); |
|
602 |
} |
|
603 |
} |
|
604 |
||
605 |
/** |
|
606 |
* Generates a constructor for the adapter class. This constructor will take the same arguments as the supertype |
|
607 |
* constructor passed as the argument here, and delegate to it. However, it will take an additional argument of |
|
608 |
* either ScriptObject or ScriptFunction type (based on the value of the "fromFunction" parameter), and initialize |
|
609 |
* all the method handle fields of the adapter instance with functions from the script object (or the script |
|
610 |
* function itself, if that's what's passed). There is one method handle field in the adapter class for every method |
|
611 |
* that can be implemented or overridden; the name of every field is same as the name of the method, with a number |
|
612 |
* suffix that makes it unique in case of overloaded methods. The generated constructor will invoke |
|
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
613 |
* {@link #getHandle(ScriptFunction, MethodType, boolean)} or {@link #getHandle(Object, String, MethodType, |
16147 | 614 |
* boolean)} to obtain the method handles; these methods make sure to add the necessary conversions and arity |
615 |
* adjustments so that the resulting method handles can be invoked from generated methods using {@code invokeExact}. |
|
16167
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
616 |
* The constructor that takes a script function will only initialize the methods with the same name as the single |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
617 |
* abstract method. The constructor will also store the Nashorn global that was current at the constructor |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
618 |
* invocation time in a field named "global". The generated constructor will be public, regardless of whether the |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
619 |
* supertype constructor was public or protected. The generated constructor will not be variable arity, even if the |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
620 |
* supertype constructor was. |
16147 | 621 |
* @param ctor the supertype constructor that is serving as the base for the generated constructor. |
16167
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
622 |
* @param fromFunction true if we're generating a constructor that initializes SAM types from a single |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
623 |
* ScriptFunction passed to it, false if we're generating a constructor that initializes an arbitrary type from a |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
624 |
* ScriptObject passed to it. |
16147 | 625 |
*/ |
626 |
private void generateConstructor(final Constructor<?> ctor, final boolean fromFunction) { |
|
627 |
final Type originalCtorType = Type.getType(ctor); |
|
628 |
final Type[] originalArgTypes = originalCtorType.getArgumentTypes(); |
|
629 |
final int argLen = originalArgTypes.length; |
|
630 |
final Type[] newArgTypes = new Type[argLen + 1]; |
|
631 |
||
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
632 |
// Insert ScriptFunction|Object as the last argument to the constructor |
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
633 |
final Type extraArgumentType = fromFunction ? SCRIPT_FUNCTION_TYPE : OBJECT_TYPE; |
16167
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
634 |
newArgTypes[argLen] = extraArgumentType; |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
635 |
System.arraycopy(originalArgTypes, 0, newArgTypes, 0, argLen); |
16147 | 636 |
|
637 |
// All constructors must be public, even if in the superclass they were protected. |
|
638 |
// Existing super constructor <init>(this, args...) triggers generating <init>(this, scriptObj, args...). |
|
16167
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
639 |
final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(ACC_PUBLIC, INIT, |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
640 |
Type.getMethodDescriptor(originalCtorType.getReturnType(), newArgTypes), null, null)); |
16147 | 641 |
|
642 |
mv.visitCode(); |
|
16167
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
643 |
// First, invoke super constructor with original arguments. If the form of the constructor we're generating is |
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
644 |
// <init>(this, args..., scriptFn), then we're invoking super.<init>(this, args...). |
16147 | 645 |
mv.visitVarInsn(ALOAD, 0); |
646 |
final Class<?>[] argTypes = ctor.getParameterTypes(); |
|
16167
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
647 |
int offset = 1; // First arg is at position 1, after this. |
16147 | 648 |
for (int i = 0; i < argLen; ++i) { |
649 |
final Type argType = Type.getType(argTypes[i]); |
|
650 |
mv.load(offset, argType); |
|
651 |
offset += argType.getSize(); |
|
652 |
} |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
653 |
mv.invokespecial(superClassName, INIT, originalCtorType.getDescriptor()); |
16147 | 654 |
|
655 |
// Get a descriptor to the appropriate "JavaAdapterFactory.getHandle" method. |
|
656 |
final String getHandleDescriptor = fromFunction ? GET_HANDLE_FUNCTION_DESCRIPTOR : GET_HANDLE_OBJECT_DESCRIPTOR; |
|
657 |
||
658 |
// Assign MethodHandle fields through invoking getHandle() |
|
659 |
for (final MethodInfo mi : methodInfos) { |
|
660 |
mv.visitVarInsn(ALOAD, 0); |
|
661 |
if (fromFunction && !mi.getName().equals(samName)) { |
|
662 |
// Constructors initializing from a ScriptFunction only initialize methods with the SAM name. |
|
663 |
// NOTE: if there's a concrete overloaded method sharing the SAM name, it'll be overriden too. This |
|
664 |
// is a deliberate design choice. All other method handles are initialized to null. |
|
665 |
mv.visitInsn(ACONST_NULL); |
|
666 |
} else { |
|
16167
d99db3541813
8005983: JavaAdapterFactory generated proxy classes should take extra constructor arguments at the end
attila
parents:
16151
diff
changeset
|
667 |
mv.visitVarInsn(ALOAD, offset); |
16147 | 668 |
if(!fromFunction) { |
669 |
mv.aconst(mi.getName()); |
|
670 |
} |
|
671 |
mv.aconst(Type.getMethodType(mi.type.toMethodDescriptorString())); |
|
672 |
mv.iconst(mi.method.isVarArgs() ? 1 : 0); |
|
673 |
mv.invokestatic(THIS_CLASS_TYPE_NAME, "getHandle", getHandleDescriptor); |
|
674 |
} |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
675 |
mv.putfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); |
16147 | 676 |
} |
677 |
||
678 |
// Assign "this.global = Context.getGlobal()" |
|
679 |
mv.visitVarInsn(ALOAD, 0); |
|
680 |
invokeGetGlobal(mv); |
|
681 |
mv.dup(); |
|
682 |
mv.invokevirtual(OBJECT_TYPE_NAME, "getClass", GET_CLASS_METHOD_DESCRIPTOR); // check against null Context |
|
683 |
mv.pop(); |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
684 |
mv.putfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); |
16147 | 685 |
|
686 |
// Wrap up |
|
687 |
mv.visitInsn(RETURN); |
|
688 |
mv.visitMaxs(0, 0); |
|
689 |
mv.visitEnd(); |
|
690 |
} |
|
691 |
||
692 |
private static void invokeGetGlobal(final InstructionAdapter mv) { |
|
693 |
mv.invokestatic(CONTEXT_TYPE_NAME, "getGlobal", GET_GLOBAL_METHOD_DESCRIPTOR); |
|
694 |
} |
|
695 |
||
696 |
private void invokeSetGlobal(final InstructionAdapter mv) { |
|
697 |
mv.invokestatic(globalSetterClassName, "setGlobal", SET_GLOBAL_METHOD_DESCRIPTOR); |
|
698 |
} |
|
699 |
||
700 |
/** |
|
701 |
* Given a JS script function, binds it to null JS "this", and adapts its parameter types, return types, and arity |
|
702 |
* to the specified type and arity. This method is public mainly for implementation reasons, so the adapter classes |
|
703 |
* can invoke it from their constructors that take a ScriptFunction in its first argument to obtain the method |
|
704 |
* handles for their abstract method implementations. |
|
705 |
* @param fn the script function |
|
706 |
* @param type the method type it has to conform to |
|
707 |
* @param varArg if the Java method for which the function is being adapted is a variable arity method |
|
708 |
* @return the appropriately adapted method handle for invoking the script function. |
|
709 |
*/ |
|
710 |
public static MethodHandle getHandle(final ScriptFunction fn, final MethodType type, final boolean varArg) { |
|
711 |
// JS "this" will be null for SAMs |
|
712 |
return adaptHandle(fn.getBoundInvokeHandle(null), type, varArg); |
|
713 |
} |
|
714 |
||
715 |
/** |
|
716 |
* Given a JS script object, retrieves a function from it by name, binds it to the script object as its "this", and |
|
717 |
* adapts its parameter types, return types, and arity to the specified type and arity. This method is public mainly |
|
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
718 |
* for implementation reasons, so the adapter classes can invoke it from their constructors that take a Object |
16147 | 719 |
* in its first argument to obtain the method handles for their method implementations. |
720 |
* @param obj the script obj |
|
721 |
* @param name the name of the property that contains the function |
|
722 |
* @param type the method type it has to conform to |
|
723 |
* @param varArg if the Java method for which the function is being adapted is a variable arity method |
|
724 |
* @return the appropriately adapted method handle for invoking the script function, or null if the value of the |
|
725 |
* property is either null or undefined, or "toString" was requested as the name, but the object doesn't directly |
|
726 |
* define it but just inherits it through prototype. |
|
727 |
*/ |
|
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
728 |
public static MethodHandle getHandle(final Object obj, final String name, final MethodType type, final boolean varArg) { |
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
729 |
if (! (obj instanceof ScriptObject)) { |
16256
f2d9a0c49914
8007002: Replace implicit exception throwing methods with explicit throws - simplify control flow and remove useless code
lagergren
parents:
16234
diff
changeset
|
730 |
throw typeError("not.an.object", ScriptRuntime.safeToString(obj)); |
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
731 |
} |
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
732 |
|
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
733 |
final ScriptObject sobj = (ScriptObject)obj; |
16147 | 734 |
// Since every JS Object has a toString, we only override "String toString()" it if it's explicitly specified |
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
735 |
if ("toString".equals(name) && !sobj.hasOwnProperty("toString")) { |
16147 | 736 |
return null; |
737 |
} |
|
738 |
||
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
739 |
final Object fnObj = sobj.get(name); |
16147 | 740 |
if (fnObj instanceof ScriptFunction) { |
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
741 |
return adaptHandle(((ScriptFunction)fnObj).getBoundInvokeHandle(sobj), type, varArg); |
16147 | 742 |
} else if(fnObj == null || fnObj instanceof Undefined) { |
743 |
return null; |
|
744 |
} else { |
|
16256
f2d9a0c49914
8007002: Replace implicit exception throwing methods with explicit throws - simplify control flow and remove useless code
lagergren
parents:
16234
diff
changeset
|
745 |
throw typeError("not.a.function", name); |
16147 | 746 |
} |
747 |
} |
|
748 |
||
749 |
private static MethodHandle adaptHandle(final MethodHandle handle, final MethodType type, final boolean varArg) { |
|
750 |
return Bootstrap.getLinkerServices().asType(ScriptObject.pairArguments(handle, type, varArg), type); |
|
751 |
} |
|
752 |
||
753 |
/** |
|
754 |
* Encapsulation of the information used to generate methods in the adapter classes. Basically, a wrapper around the |
|
755 |
* reflective Method object, a cached MethodType, and the name of the field in the adapter class that will hold the |
|
756 |
* method handle serving as the implementation of this method in adapter instances. |
|
757 |
* |
|
758 |
*/ |
|
759 |
private static class MethodInfo { |
|
760 |
private final Method method; |
|
761 |
private final MethodType type; |
|
762 |
private String methodHandleFieldName; |
|
763 |
||
764 |
private MethodInfo(final Class<?> clazz, final String name, final Class<?>... argTypes) throws NoSuchMethodException { |
|
765 |
this(clazz.getDeclaredMethod(name, argTypes)); |
|
766 |
} |
|
767 |
||
768 |
private MethodInfo(final Method method) { |
|
769 |
this.method = method; |
|
770 |
this.type = MH.type(method.getReturnType(), method.getParameterTypes()); |
|
771 |
} |
|
772 |
||
773 |
@Override |
|
774 |
public boolean equals(final Object obj) { |
|
775 |
return obj instanceof MethodInfo && equals((MethodInfo)obj); |
|
776 |
} |
|
777 |
||
778 |
private boolean equals(final MethodInfo other) { |
|
779 |
// Only method name and type are used for comparison; method handle field name is not. |
|
780 |
return getName().equals(other.getName()) && type.equals(other.type); |
|
781 |
} |
|
782 |
||
783 |
String getName() { |
|
784 |
return method.getName(); |
|
785 |
} |
|
786 |
||
787 |
@Override |
|
788 |
public int hashCode() { |
|
789 |
return getName().hashCode() ^ type.hashCode(); |
|
790 |
} |
|
791 |
||
792 |
void setIsCanonical(final Set<String> usedFieldNames) { |
|
793 |
int i = 0; |
|
794 |
String fieldName = getName(); |
|
795 |
while(!usedFieldNames.add(fieldName)) { |
|
796 |
fieldName = getName() + (i++); |
|
797 |
} |
|
798 |
methodHandleFieldName = fieldName; |
|
799 |
} |
|
800 |
} |
|
801 |
||
802 |
private void generateMethods() { |
|
803 |
for(final MethodInfo mi: methodInfos) { |
|
804 |
generateMethod(mi); |
|
805 |
} |
|
806 |
} |
|
807 |
||
808 |
/** |
|
809 |
* Generates a method in the adapter class that adapts a method from the original class. The generated methods will |
|
810 |
* inspect the method handle field assigned to them. If it is null (the JS object doesn't provide an implementation |
|
811 |
* for the method) then it will either invoke its version in the supertype, or if it is abstract, throw an |
|
812 |
* {@link UnsupportedOperationException}. Otherwise, if the method handle field's value is not null, the handle is |
|
813 |
* invoked using invokeExact (signature polymorphic invocation as per JLS 15.12.3). Before the invocation, the |
|
814 |
* current Nashorn {@link Context} is checked, and if it is different than the global used to create the adapter |
|
815 |
* instance, the creating global is set to be the current global. In this case, the previously current global is |
|
816 |
* restored after the invocation. If invokeExact results in a Throwable that is not one of the method's declared |
|
817 |
* exceptions, and is not an unchecked throwable, then it is wrapped into a {@link RuntimeException} and the runtime |
|
818 |
* exception is thrown. The method handle retrieved from the field is guaranteed to exactly match the signature of |
|
819 |
* the method; this is guaranteed by the way constructors of the adapter class obtain them using |
|
16178
2704dd3b2691
8006424: Passing null or undefined to adapter class constructors results in NPE or ClassCastException
sundar
parents:
16171
diff
changeset
|
820 |
* {@link #getHandle(Object, String, MethodType, boolean)}. |
16147 | 821 |
* @param mi the method info describing the method to be generated. |
822 |
*/ |
|
823 |
private void generateMethod(final MethodInfo mi) { |
|
824 |
final Method method = mi.method; |
|
825 |
final int mod = method.getModifiers(); |
|
826 |
final int access = ACC_PUBLIC | (method.isVarArgs() ? ACC_VARARGS : 0); |
|
827 |
final Class<?>[] exceptions = method.getExceptionTypes(); |
|
828 |
final String[] exceptionNames = new String[exceptions.length]; |
|
829 |
for (int i = 0; i < exceptions.length; ++i) { |
|
830 |
exceptionNames[i] = Type.getInternalName(exceptions[i]); |
|
831 |
} |
|
832 |
final MethodType type = mi.type; |
|
833 |
final String methodDesc = type.toMethodDescriptorString(); |
|
834 |
final String name = mi.getName(); |
|
835 |
||
836 |
final Type asmType = Type.getMethodType(methodDesc); |
|
837 |
final Type[] asmArgTypes = asmType.getArgumentTypes(); |
|
838 |
||
839 |
// Determine the first index for a local variable |
|
840 |
int nextLocalVar = 1; // this |
|
841 |
for(final Type t: asmArgTypes) { |
|
842 |
nextLocalVar += t.getSize(); |
|
843 |
} |
|
844 |
||
845 |
final InstructionAdapter mv = new InstructionAdapter(cw.visitMethod(access, name, methodDesc, null, |
|
846 |
exceptionNames)); |
|
847 |
mv.visitCode(); |
|
848 |
||
849 |
final Label methodHandleNotNull = new Label(); |
|
850 |
final Label methodEnd = new Label(); |
|
851 |
||
852 |
final Type returnType = Type.getType(type.returnType()); |
|
853 |
||
854 |
// Get the method handle |
|
855 |
mv.visitVarInsn(ALOAD, 0); |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
856 |
mv.getfield(generatedClassName, mi.methodHandleFieldName, METHOD_HANDLE_TYPE_DESCRIPTOR); |
16147 | 857 |
mv.visitInsn(DUP); // It'll remain on the stack all the way until the invocation |
858 |
// Check if the method handle is null |
|
859 |
mv.visitJumpInsn(IFNONNULL, methodHandleNotNull); |
|
860 |
if(Modifier.isAbstract(mod)) { |
|
861 |
// If it's null, and the method is abstract, throw an exception |
|
862 |
mv.anew(UNSUPPORTED_OPERATION_TYPE); |
|
863 |
mv.dup(); |
|
864 |
mv.invokespecial(UNSUPPORTED_OPERATION_TYPE_NAME, INIT, VOID_NOARG); |
|
865 |
mv.athrow(); |
|
866 |
} else { |
|
867 |
// If it's null, and the method is not abstract, delegate to super method. |
|
868 |
mv.visitVarInsn(ALOAD, 0); |
|
869 |
int nextParam = 1; |
|
870 |
for(final Type t: asmArgTypes) { |
|
871 |
mv.load(nextParam, t); |
|
872 |
nextParam += t.getSize(); |
|
873 |
} |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
874 |
mv.invokespecial(superClassName, name, methodDesc); |
16147 | 875 |
mv.areturn(returnType); |
876 |
} |
|
877 |
||
878 |
mv.visitLabel(methodHandleNotNull); |
|
879 |
final int currentGlobalVar = nextLocalVar++; |
|
880 |
final int globalsDifferVar = nextLocalVar++; |
|
881 |
||
882 |
// Emit code for switching to the creating global |
|
883 |
// ScriptObject currentGlobal = Context.getGlobal(); |
|
884 |
invokeGetGlobal(mv); |
|
885 |
mv.dup(); |
|
886 |
mv.visitVarInsn(ASTORE, currentGlobalVar); |
|
887 |
// if(this.global == currentGlobal) { |
|
888 |
loadGlobalOnStack(mv); |
|
889 |
final Label globalsDiffer = new Label(); |
|
890 |
mv.ifacmpne(globalsDiffer); |
|
891 |
// globalsDiffer = false |
|
892 |
mv.iconst(0); // false |
|
893 |
final Label proceed = new Label(); |
|
894 |
mv.goTo(proceed); |
|
895 |
mv.visitLabel(globalsDiffer); |
|
896 |
// } else { |
|
897 |
// Context.setGlobal(this.global); |
|
898 |
loadGlobalOnStack(mv); |
|
899 |
invokeSetGlobal(mv); |
|
900 |
// globalsDiffer = true |
|
901 |
mv.iconst(1); |
|
902 |
||
903 |
mv.visitLabel(proceed); |
|
904 |
mv.visitVarInsn(ISTORE, globalsDifferVar); |
|
905 |
||
906 |
// Load all parameters back on stack for dynamic invocation. |
|
907 |
int varOffset = 1; |
|
908 |
for (final Type t : asmArgTypes) { |
|
909 |
mv.load(varOffset, t); |
|
910 |
varOffset += t.getSize(); |
|
911 |
} |
|
912 |
||
913 |
// Invoke the target method handle |
|
914 |
final Label tryBlockStart = new Label(); |
|
915 |
mv.visitLabel(tryBlockStart); |
|
916 |
mv.invokevirtual(METHOD_HANDLE_TYPE.getInternalName(), "invokeExact", type.toMethodDescriptorString()); |
|
917 |
final Label tryBlockEnd = new Label(); |
|
918 |
mv.visitLabel(tryBlockEnd); |
|
919 |
emitFinally(mv, currentGlobalVar, globalsDifferVar); |
|
920 |
mv.areturn(returnType); |
|
921 |
||
922 |
// If Throwable is not declared, we need an adapter from Throwable to RuntimeException |
|
923 |
final boolean throwableDeclared = isThrowableDeclared(exceptions); |
|
924 |
final Label throwableHandler; |
|
925 |
if (!throwableDeclared) { |
|
926 |
// Add "throw new RuntimeException(Throwable)" handler for Throwable |
|
927 |
throwableHandler = new Label(); |
|
928 |
mv.visitLabel(throwableHandler); |
|
929 |
mv.anew(RUNTIME_EXCEPTION_TYPE); |
|
930 |
mv.dupX1(); |
|
931 |
mv.swap(); |
|
932 |
mv.invokespecial(RUNTIME_EXCEPTION_TYPE_NAME, INIT, Type.getMethodDescriptor(Type.VOID_TYPE, THROWABLE_TYPE)); |
|
933 |
// Fall through to rethrow handler |
|
934 |
} else { |
|
935 |
throwableHandler = null; |
|
936 |
} |
|
937 |
final Label rethrowHandler = new Label(); |
|
938 |
mv.visitLabel(rethrowHandler); |
|
939 |
// Rethrow handler for RuntimeException, Error, and all declared exception types |
|
940 |
emitFinally(mv, currentGlobalVar, globalsDifferVar); |
|
941 |
mv.athrow(); |
|
942 |
mv.visitLabel(methodEnd); |
|
943 |
||
944 |
mv.visitLocalVariable("currentGlobal", SCRIPT_OBJECT_TYPE_DESCRIPTOR, null, methodHandleNotNull, methodEnd, currentGlobalVar); |
|
945 |
mv.visitLocalVariable("globalsDiffer", Type.INT_TYPE.getDescriptor(), null, methodHandleNotNull, methodEnd, globalsDifferVar); |
|
946 |
||
947 |
if(throwableDeclared) { |
|
948 |
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, THROWABLE_TYPE_NAME); |
|
949 |
assert throwableHandler == null; |
|
950 |
} else { |
|
951 |
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, RUNTIME_EXCEPTION_TYPE_NAME); |
|
952 |
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, ERROR_TYPE_NAME); |
|
953 |
for(final String excName: exceptionNames) { |
|
954 |
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, rethrowHandler, excName); |
|
955 |
} |
|
956 |
mv.visitTryCatchBlock(tryBlockStart, tryBlockEnd, throwableHandler, THROWABLE_TYPE_NAME); |
|
957 |
} |
|
958 |
mv.visitMaxs(0, 0); |
|
959 |
mv.visitEnd(); |
|
960 |
} |
|
961 |
||
962 |
/** |
|
963 |
* Emit code to restore the previous Nashorn Context when needed. |
|
964 |
* @param mv the instruction adapter |
|
965 |
* @param currentGlobalVar index of the local variable holding the reference to the current global at method |
|
966 |
* entry. |
|
967 |
* @param globalsDifferVar index of the boolean local variable that is true if the global needs to be restored. |
|
968 |
*/ |
|
969 |
private void emitFinally(final InstructionAdapter mv, final int currentGlobalVar, final int globalsDifferVar) { |
|
970 |
// Emit code to restore the previous Nashorn global if needed |
|
971 |
mv.visitVarInsn(ILOAD, globalsDifferVar); |
|
972 |
final Label skip = new Label(); |
|
973 |
mv.ifeq(skip); |
|
974 |
mv.visitVarInsn(ALOAD, currentGlobalVar); |
|
975 |
invokeSetGlobal(mv); |
|
976 |
mv.visitLabel(skip); |
|
977 |
} |
|
978 |
||
979 |
private void loadGlobalOnStack(final InstructionAdapter mv) { |
|
980 |
mv.visitVarInsn(ALOAD, 0); |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
981 |
mv.getfield(generatedClassName, GLOBAL_FIELD_NAME, SCRIPT_OBJECT_TYPE_DESCRIPTOR); |
16147 | 982 |
} |
983 |
||
984 |
private static boolean isThrowableDeclared(final Class<?>[] exceptions) { |
|
985 |
for (final Class<?> exception : exceptions) { |
|
986 |
if (exception == Throwable.class) { |
|
987 |
return true; |
|
988 |
} |
|
989 |
} |
|
990 |
return false; |
|
991 |
} |
|
992 |
||
993 |
/** |
|
994 |
* Gathers methods that can be implemented or overridden from the specified type into this factory's |
|
995 |
* {@link #methodInfos} set. It will add all non-final, non-static methods that are either public or protected from |
|
996 |
* the type if the type itself is public. If the type is a class, the method will recursively invoke itself for its |
|
997 |
* superclass and the interfaces it implements, and add further methods that were not directly declared on the |
|
998 |
* class. |
|
999 |
* @param type the type defining the methods. |
|
1000 |
*/ |
|
1001 |
private void gatherMethods(final Class<?> type) { |
|
1002 |
if (Modifier.isPublic(type.getModifiers())) { |
|
1003 |
final Method[] typeMethods = type.isInterface() ? type.getMethods() : type.getDeclaredMethods(); |
|
1004 |
||
1005 |
for (final Method typeMethod: typeMethods) { |
|
1006 |
final int m = typeMethod.getModifiers(); |
|
1007 |
if (Modifier.isStatic(m)) { |
|
1008 |
continue; |
|
1009 |
} |
|
1010 |
if (Modifier.isPublic(m) || Modifier.isProtected(m)) { |
|
1011 |
final MethodInfo mi = new MethodInfo(typeMethod); |
|
1012 |
if (Modifier.isFinal(m)) { |
|
1013 |
finalMethods.add(mi); |
|
1014 |
} else if (!finalMethods.contains(mi) && methodInfos.add(mi)) { |
|
1015 |
if (Modifier.isAbstract(m)) { |
|
1016 |
abstractMethodNames.add(mi.getName()); |
|
1017 |
} |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1018 |
mi.setIsCanonical(usedFieldNames); |
16147 | 1019 |
} |
1020 |
} |
|
1021 |
} |
|
1022 |
} |
|
1023 |
// If the type is a class, visit its superclasses and declared interfaces. If it's an interface, we're done. |
|
1024 |
// Needing to invoke the method recursively for a non-interface Class object is the consequence of needing to |
|
1025 |
// see all declared protected methods, and Class.getDeclaredMethods() doesn't provide those declared in a |
|
1026 |
// superclass. For interfaces, we used Class.getMethods(), as we're only interested in public ones there, and |
|
1027 |
// getMethods() does provide those declared in a superinterface. |
|
1028 |
if (!type.isInterface()) { |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1029 |
final Class<?> superType = type.getSuperclass(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1030 |
if (superType != null) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1031 |
gatherMethods(superType); |
16147 | 1032 |
} |
1033 |
for (final Class<?> itf: type.getInterfaces()) { |
|
1034 |
gatherMethods(itf); |
|
1035 |
} |
|
1036 |
} |
|
1037 |
} |
|
1038 |
||
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1039 |
private void gatherMethods(final List<Class<?>> classes) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1040 |
for(final Class<?> c: classes) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1041 |
gatherMethods(c); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1042 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1043 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1044 |
|
16147 | 1045 |
/** |
1046 |
* Creates a collection of methods that are not final, but we still never allow them to be overridden in adapters, |
|
1047 |
* as explicitly declaring them automatically is a bad idea. Currently, this means {@code Object.finalize()} and |
|
1048 |
* {@code Object.clone()}. |
|
1049 |
* @return a collection of method infos representing those methods that we never override in adapter classes. |
|
1050 |
*/ |
|
1051 |
private static Collection<MethodInfo> getExcludedMethods() { |
|
1052 |
return AccessController.doPrivileged(new PrivilegedAction<Collection<MethodInfo>>() { |
|
1053 |
@Override |
|
1054 |
public Collection<MethodInfo> run() { |
|
1055 |
try { |
|
1056 |
return Arrays.asList( |
|
1057 |
new MethodInfo(Object.class, "finalize"), |
|
1058 |
new MethodInfo(Object.class, "clone")); |
|
1059 |
} catch (final NoSuchMethodException e) { |
|
1060 |
throw new AssertionError(e); |
|
1061 |
} |
|
1062 |
} |
|
1063 |
}); |
|
1064 |
} |
|
1065 |
||
1066 |
private static ProtectionDomain createGeneratedProtectionDomain() { |
|
1067 |
// Generated classes need to have AllPermission. Since we require the "createClassLoader" RuntimePermission, we |
|
1068 |
// can create a class loader that'll load new classes with any permissions. Our generated classes are just |
|
1069 |
// delegating adapters, so having AllPermission can't cause anything wrong; the effective set of permissions for |
|
1070 |
// the executing script functions will still be limited by the permissions of the caller and the permissions of |
|
1071 |
// the script. |
|
1072 |
final Permissions permissions = new Permissions(); |
|
1073 |
permissions.add(new AllPermission()); |
|
1074 |
return new ProtectionDomain(new CodeSource(null, (CodeSigner[])null), permissions); |
|
1075 |
} |
|
1076 |
||
1077 |
private static class AdapterInfo { |
|
1078 |
final StaticClass adapterClass; |
|
1079 |
final boolean autoConvertibleFromFunction; |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1080 |
final AnnotatedAdaptationOutcome adaptationOutcome; |
16147 | 1081 |
|
1082 |
AdapterInfo(final StaticClass adapterClass, final boolean autoConvertibleFromFunction) { |
|
1083 |
this.adapterClass = adapterClass; |
|
1084 |
this.autoConvertibleFromFunction = autoConvertibleFromFunction; |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1085 |
this.adaptationOutcome = AnnotatedAdaptationOutcome.SUCCESS; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1086 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1087 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1088 |
AdapterInfo(final AdaptationOutcome outcome, final String classList) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1089 |
this(new AnnotatedAdaptationOutcome(outcome, classList)); |
16147 | 1090 |
} |
1091 |
||
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1092 |
AdapterInfo(final AnnotatedAdaptationOutcome adaptationOutcome) { |
16147 | 1093 |
this.adapterClass = null; |
1094 |
this.autoConvertibleFromFunction = false; |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1095 |
this.adaptationOutcome = adaptationOutcome; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1096 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1097 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1098 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1099 |
/** |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1100 |
* An adaptation outcome accompanied with a name of a class (or a list of multiple class names) that are the reason |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1101 |
* an adapter could not be generated. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1102 |
*/ |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1103 |
private static class AnnotatedAdaptationOutcome { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1104 |
static final AnnotatedAdaptationOutcome SUCCESS = new AnnotatedAdaptationOutcome(AdaptationOutcome.SUCCESS, ""); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1105 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1106 |
private final AdaptationOutcome adaptationOutcome; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1107 |
private final String classList; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1108 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1109 |
AnnotatedAdaptationOutcome(final AdaptationOutcome adaptationOutcome, final String classList) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1110 |
this.adaptationOutcome = adaptationOutcome; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1111 |
this.classList = classList; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1112 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1113 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1114 |
void typeError() { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1115 |
assert adaptationOutcome != AdaptationOutcome.SUCCESS; |
16256
f2d9a0c49914
8007002: Replace implicit exception throwing methods with explicit throws - simplify control flow and remove useless code
lagergren
parents:
16234
diff
changeset
|
1116 |
throw ECMAErrors.typeError("extend." + adaptationOutcome, classList); |
16147 | 1117 |
} |
1118 |
} |
|
1119 |
||
1120 |
/** |
|
1121 |
* For a given class, create its adapter class and associated info. |
|
1122 |
* @param type the class for which the adapter is created |
|
1123 |
* @return the adapter info for the class. |
|
1124 |
*/ |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1125 |
private static AdapterInfo createAdapterInfo(final Class<?>[] types, final ClassAndLoader definingClassAndLoader) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1126 |
Class<?> superClass = null; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1127 |
final List<Class<?>> interfaces = new ArrayList<>(types.length); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1128 |
for(final Class<?> t: types) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1129 |
final int mod = t.getModifiers(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1130 |
if(!t.isInterface()) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1131 |
if(superClass != null) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1132 |
return new AdapterInfo(AdaptationOutcome.ERROR_MULTIPLE_SUPERCLASSES, t.getCanonicalName() + " and " + superClass.getCanonicalName()); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1133 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1134 |
if (Modifier.isFinal(mod)) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1135 |
return new AdapterInfo(AdaptationOutcome.ERROR_FINAL_CLASS, t.getCanonicalName()); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1136 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1137 |
superClass = t; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1138 |
} else { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1139 |
interfaces.add(t); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1140 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1141 |
if(!Modifier.isPublic(mod)) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1142 |
return new AdapterInfo(AdaptationOutcome.ERROR_NON_PUBLIC_CLASS, t.getCanonicalName()); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1143 |
} |
16147 | 1144 |
} |
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1145 |
final Class<?> effectiveSuperClass = superClass == null ? Object.class : superClass; |
16147 | 1146 |
return AccessController.doPrivileged(new PrivilegedAction<AdapterInfo>() { |
1147 |
@Override |
|
1148 |
public AdapterInfo run() { |
|
1149 |
try { |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1150 |
final JavaAdapterFactory factory = new JavaAdapterFactory(effectiveSuperClass, interfaces, definingClassAndLoader); |
16147 | 1151 |
return new AdapterInfo(StaticClass.forClass(factory.generateClass()), |
1152 |
factory.isAutoConvertibleFromFunction()); |
|
1153 |
} catch (final AdaptationException e) { |
|
1154 |
return new AdapterInfo(e.outcome); |
|
1155 |
} |
|
1156 |
} |
|
1157 |
}); |
|
1158 |
} |
|
1159 |
||
1160 |
@SuppressWarnings("serial") |
|
1161 |
private static class AdaptationException extends Exception { |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1162 |
private final AnnotatedAdaptationOutcome outcome; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1163 |
AdaptationException(final AdaptationOutcome outcome, final String classList) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1164 |
this.outcome = new AnnotatedAdaptationOutcome(outcome, classList); |
16147 | 1165 |
} |
1166 |
} |
|
1167 |
||
1168 |
private String getCommonSuperClass(final String type1, final String type2) { |
|
1169 |
try { |
|
1170 |
final Class<?> c1 = Class.forName(type1.replace('/', '.'), false, commonLoader); |
|
1171 |
final Class<?> c2 = Class.forName(type2.replace('/', '.'), false, commonLoader); |
|
1172 |
if (c1.isAssignableFrom(c2)) { |
|
1173 |
return type1; |
|
1174 |
} |
|
1175 |
if (c2.isAssignableFrom(c1)) { |
|
1176 |
return type2; |
|
1177 |
} |
|
1178 |
if (c1.isInterface() || c2.isInterface()) { |
|
1179 |
return "java/lang/Object"; |
|
1180 |
} |
|
1181 |
return assignableSuperClass(c1, c2).getName().replace('.', '/'); |
|
1182 |
} catch(final ClassNotFoundException e) { |
|
1183 |
throw new RuntimeException(e); |
|
1184 |
} |
|
1185 |
} |
|
1186 |
||
1187 |
private static Class<?> assignableSuperClass(final Class<?> c1, final Class<?> c2) { |
|
1188 |
final Class<?> superClass = c1.getSuperclass(); |
|
1189 |
return superClass.isAssignableFrom(c2) ? superClass : assignableSuperClass(superClass, c2); |
|
1190 |
} |
|
1191 |
||
1192 |
/** |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1193 |
* Choose between the passed class loader and the class loader that defines the ScriptObject class, based on which |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1194 |
* of the two can see the classes in both. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1195 |
* @param classAndLoader the loader and a representative class from it that will be used to add the generated |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1196 |
* adapter to its ADAPTER_INFO_MAPS. |
16147 | 1197 |
* @return the class loader that sees both the specified class and Nashorn classes. |
1198 |
* @throws IllegalStateException if no such class loader is found. |
|
1199 |
*/ |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1200 |
private static ClassLoader findCommonLoader(final ClassAndLoader classAndLoader) throws AdaptationException { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1201 |
final ClassLoader loader = classAndLoader.getLoader(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1202 |
if (canSeeClass(loader, ScriptObject.class)) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1203 |
return loader; |
16147 | 1204 |
} |
1205 |
||
1206 |
final ClassLoader nashornLoader = ScriptObject.class.getClassLoader(); |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1207 |
if(canSeeClass(nashornLoader, classAndLoader.clazz)) { |
16147 | 1208 |
return nashornLoader; |
1209 |
} |
|
1210 |
||
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1211 |
throw new AdaptationException(AdaptationOutcome.ERROR_NO_COMMON_LOADER, classAndLoader.clazz.getCanonicalName()); |
16147 | 1212 |
} |
1213 |
||
1214 |
private static boolean canSeeClass(final ClassLoader cl, final Class<?> clazz) { |
|
1215 |
try { |
|
1216 |
return Class.forName(clazz.getName(), false, cl) == clazz; |
|
1217 |
} catch (final ClassNotFoundException e) { |
|
1218 |
return false; |
|
1219 |
} |
|
1220 |
} |
|
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1221 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1222 |
/** |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1223 |
* Given a list of types that define the superclass/interfaces for an adapter class, returns a single type from the |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1224 |
* list that will be used to attach the adapter to its ClassValue. The first type in the array that is defined in a |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1225 |
* class loader that can also see all other types is returned. If there is no such loader, an exception is thrown. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1226 |
* @param types the input types |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1227 |
* @return the first type from the array that is defined in a class loader that can also see all other types. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1228 |
*/ |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1229 |
private static ClassAndLoader getDefiningClassAndLoader(final Class<?>[] types) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1230 |
// Short circuit the cheap case |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1231 |
if(types.length == 1) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1232 |
return new ClassAndLoader(types[0], false); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1233 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1234 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1235 |
return AccessController.doPrivileged(new PrivilegedAction<ClassAndLoader>() { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1236 |
@Override |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1237 |
public ClassAndLoader run() { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1238 |
return getDefiningClassAndLoaderPrivileged(types); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1239 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1240 |
}); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1241 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1242 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1243 |
private static ClassAndLoader getDefiningClassAndLoaderPrivileged(final Class<?>[] types) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1244 |
final Collection<ClassAndLoader> maximumVisibilityLoaders = getMaximumVisibilityLoaders(types); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1245 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1246 |
final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1247 |
if(maximumVisibilityLoaders.size() == 1) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1248 |
// Fortunate case - single maximally specific class loader; return its representative class. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1249 |
return it.next(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1250 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1251 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1252 |
// Ambiguity; throw an error. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1253 |
assert maximumVisibilityLoaders.size() > 1; // basically, can't be zero |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1254 |
final StringBuilder b = new StringBuilder(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1255 |
b.append(it.next().clazz.getCanonicalName()); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1256 |
while(it.hasNext()) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1257 |
b.append(", ").append(it.next().clazz.getCanonicalName()); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1258 |
} |
16256
f2d9a0c49914
8007002: Replace implicit exception throwing methods with explicit throws - simplify control flow and remove useless code
lagergren
parents:
16234
diff
changeset
|
1259 |
throw typeError("extend.ambiguous.defining.class", b.toString()); |
16171
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1260 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1261 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1262 |
/** |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1263 |
* Given an array of types, return a subset of their class loaders that are maximal according to the |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1264 |
* "can see other loaders' classes" relation, which is presumed to be a partial ordering. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1265 |
* @param types types |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1266 |
* @return a collection of maximum visibility class loaders. It is guaranteed to have at least one element. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1267 |
*/ |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1268 |
private static Collection<ClassAndLoader> getMaximumVisibilityLoaders(final Class<?>[] types) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1269 |
final List<ClassAndLoader> maximumVisibilityLoaders = new LinkedList<>(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1270 |
outer: for(final ClassAndLoader maxCandidate: getClassLoadersForTypes(types)) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1271 |
final Iterator<ClassAndLoader> it = maximumVisibilityLoaders.iterator(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1272 |
while(it.hasNext()) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1273 |
final ClassAndLoader existingMax = it.next(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1274 |
final boolean candidateSeesExisting = canSeeClass(maxCandidate.getRetrievedLoader(), existingMax.clazz); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1275 |
final boolean exitingSeesCandidate = canSeeClass(existingMax.getRetrievedLoader(), maxCandidate.clazz); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1276 |
if(candidateSeesExisting) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1277 |
if(!exitingSeesCandidate) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1278 |
// The candidate sees the the existing maximum, so drop the existing one as it's no longer maximal. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1279 |
it.remove(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1280 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1281 |
// NOTE: there's also the anomalous case where both loaders see each other. Not sure what to do |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1282 |
// about that one, as two distinct class loaders both seeing each other's classes is weird and |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1283 |
// violates the assumption that the relation "sees others' classes" is a partial ordering. We'll |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1284 |
// just not do anything, and treat them as incomparable; hopefully some later class loader that |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1285 |
// comes along can eliminate both of them, if it can not, we'll end up with ambiguity anyway and |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1286 |
// throw an error at the end. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1287 |
} else if(exitingSeesCandidate) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1288 |
// Existing sees the candidate, so drop the candidate. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1289 |
continue outer; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1290 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1291 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1292 |
// If we get here, no existing maximum visibility loader could see the candidate, so the candidate is a new |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1293 |
// maximum. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1294 |
maximumVisibilityLoaders.add(maxCandidate); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1295 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1296 |
return maximumVisibilityLoaders; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1297 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1298 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1299 |
private static Collection<ClassAndLoader> getClassLoadersForTypes(final Class<?>[] types) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1300 |
final Map<ClassAndLoader, ClassAndLoader> classesAndLoaders = new LinkedHashMap<>(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1301 |
for(final Class<?> c: types) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1302 |
final ClassAndLoader cl = new ClassAndLoader(c, true); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1303 |
if(!classesAndLoaders.containsKey(cl)) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1304 |
classesAndLoaders.put(cl, cl); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1305 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1306 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1307 |
return classesAndLoaders.keySet(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1308 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1309 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1310 |
/** |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1311 |
* A tuple of a class loader and a single class representative of the classes that can be loaded through it. Its |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1312 |
* equals/hashCode is defined in terms of the identity of the class loader. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1313 |
*/ |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1314 |
private static final class ClassAndLoader { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1315 |
private final Class<?> clazz; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1316 |
// Don't access this directly; most of the time, use getRetrievedLoader(), or if you know what you're doing, |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1317 |
// getLoader(). |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1318 |
private ClassLoader loader; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1319 |
// We have mild affinity against eagerly retrieving the loader, as we need to do it in a privileged block. For |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1320 |
// the most basic case of looking up an already-generated adapter info for a single type, we avoid it. |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1321 |
private boolean loaderRetrieved; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1322 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1323 |
ClassAndLoader(final Class<?> clazz, final boolean retrieveLoader) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1324 |
this.clazz = clazz; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1325 |
if(retrieveLoader) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1326 |
retrieveLoader(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1327 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1328 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1329 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1330 |
ClassLoader getLoader() { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1331 |
if(!loaderRetrieved) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1332 |
retrieveLoader(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1333 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1334 |
return getRetrievedLoader(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1335 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1336 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1337 |
ClassLoader getRetrievedLoader() { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1338 |
assert loaderRetrieved; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1339 |
return loader; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1340 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1341 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1342 |
private void retrieveLoader() { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1343 |
loader = clazz.getClassLoader(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1344 |
loaderRetrieved = true; |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1345 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1346 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1347 |
@Override |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1348 |
public boolean equals(final Object obj) { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1349 |
return obj instanceof ClassAndLoader && ((ClassAndLoader)obj).getRetrievedLoader() == getRetrievedLoader(); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1350 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1351 |
|
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1352 |
@Override |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1353 |
public int hashCode() { |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1354 |
return System.identityHashCode(getRetrievedLoader()); |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1355 |
} |
90dcd4fc42f0
8006168: ability to generate multi-type Java adapters
attila
parents:
16167
diff
changeset
|
1356 |
} |
16147 | 1357 |
} |