jaroslav@1646
|
1 |
/*
|
jaroslav@1646
|
2 |
* Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
|
jaroslav@1646
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
jaroslav@1646
|
4 |
*
|
jaroslav@1646
|
5 |
* This code is free software; you can redistribute it and/or modify it
|
jaroslav@1646
|
6 |
* under the terms of the GNU General Public License version 2 only, as
|
jaroslav@1646
|
7 |
* published by the Free Software Foundation. Oracle designates this
|
jaroslav@1646
|
8 |
* particular file as subject to the "Classpath" exception as provided
|
jaroslav@1646
|
9 |
* by Oracle in the LICENSE file that accompanied this code.
|
jaroslav@1646
|
10 |
*
|
jaroslav@1646
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT
|
jaroslav@1646
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
jaroslav@1646
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
jaroslav@1646
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that
|
jaroslav@1646
|
15 |
* accompanied this code).
|
jaroslav@1646
|
16 |
*
|
jaroslav@1646
|
17 |
* You should have received a copy of the GNU General Public License version
|
jaroslav@1646
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation,
|
jaroslav@1646
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
jaroslav@1646
|
20 |
*
|
jaroslav@1646
|
21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
jaroslav@1646
|
22 |
* or visit www.oracle.com if you need additional information or have any
|
jaroslav@1646
|
23 |
* questions.
|
jaroslav@1646
|
24 |
*/
|
jaroslav@1646
|
25 |
|
jaroslav@1646
|
26 |
package java.lang.invoke;
|
jaroslav@1646
|
27 |
|
jaroslav@1646
|
28 |
import jdk.internal.org.objectweb.asm.*;
|
jaroslav@1646
|
29 |
import sun.invoke.util.BytecodeDescriptor;
|
jaroslav@1646
|
30 |
import sun.misc.Unsafe;
|
jaroslav@1646
|
31 |
import sun.security.action.GetPropertyAction;
|
jaroslav@1646
|
32 |
|
jaroslav@1646
|
33 |
import java.io.FilePermission;
|
jaroslav@1646
|
34 |
import java.io.Serializable;
|
jaroslav@1646
|
35 |
import java.lang.reflect.Constructor;
|
jaroslav@1646
|
36 |
import java.security.AccessController;
|
jaroslav@1646
|
37 |
import java.security.PrivilegedAction;
|
jaroslav@1646
|
38 |
import java.util.LinkedHashSet;
|
jaroslav@1646
|
39 |
import java.util.concurrent.atomic.AtomicInteger;
|
jaroslav@1646
|
40 |
import java.util.PropertyPermission;
|
jaroslav@1646
|
41 |
import java.util.Set;
|
jaroslav@1646
|
42 |
|
jaroslav@1646
|
43 |
import static jdk.internal.org.objectweb.asm.Opcodes.*;
|
jaroslav@1646
|
44 |
|
jaroslav@1646
|
45 |
/**
|
jaroslav@1646
|
46 |
* Lambda metafactory implementation which dynamically creates an
|
jaroslav@1646
|
47 |
* inner-class-like class per lambda callsite.
|
jaroslav@1646
|
48 |
*
|
jaroslav@1646
|
49 |
* @see LambdaMetafactory
|
jaroslav@1646
|
50 |
*/
|
jaroslav@1646
|
51 |
/* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
|
jaroslav@1646
|
52 |
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
|
jaroslav@1646
|
53 |
|
jaroslav@1646
|
54 |
private static final int CLASSFILE_VERSION = 52;
|
jaroslav@1646
|
55 |
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
|
jaroslav@1646
|
56 |
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
|
jaroslav@1646
|
57 |
private static final String NAME_CTOR = "<init>";
|
jaroslav@1646
|
58 |
private static final String NAME_FACTORY = "get$Lambda";
|
jaroslav@1646
|
59 |
|
jaroslav@1646
|
60 |
//Serialization support
|
jaroslav@1646
|
61 |
private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
|
jaroslav@1646
|
62 |
private static final String NAME_NOT_SERIALIZABLE_EXCEPTION = "java/io/NotSerializableException";
|
jaroslav@1646
|
63 |
private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
|
jaroslav@1646
|
64 |
private static final String DESCR_METHOD_WRITE_OBJECT = "(Ljava/io/ObjectOutputStream;)V";
|
jaroslav@1646
|
65 |
private static final String DESCR_METHOD_READ_OBJECT = "(Ljava/io/ObjectInputStream;)V";
|
jaroslav@1646
|
66 |
private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
|
jaroslav@1646
|
67 |
private static final String NAME_METHOD_READ_OBJECT = "readObject";
|
jaroslav@1646
|
68 |
private static final String NAME_METHOD_WRITE_OBJECT = "writeObject";
|
jaroslav@1646
|
69 |
private static final String DESCR_CTOR_SERIALIZED_LAMBDA
|
jaroslav@1646
|
70 |
= MethodType.methodType(void.class,
|
jaroslav@1646
|
71 |
Class.class,
|
jaroslav@1646
|
72 |
String.class, String.class, String.class,
|
jaroslav@1646
|
73 |
int.class, String.class, String.class, String.class,
|
jaroslav@1646
|
74 |
String.class,
|
jaroslav@1646
|
75 |
Object[].class).toMethodDescriptorString();
|
jaroslav@1646
|
76 |
private static final String DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION
|
jaroslav@1646
|
77 |
= MethodType.methodType(void.class, String.class).toMethodDescriptorString();
|
jaroslav@1646
|
78 |
private static final String[] SER_HOSTILE_EXCEPTIONS = new String[] {NAME_NOT_SERIALIZABLE_EXCEPTION};
|
jaroslav@1646
|
79 |
|
jaroslav@1646
|
80 |
|
jaroslav@1646
|
81 |
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
jaroslav@1646
|
82 |
|
jaroslav@1646
|
83 |
// Used to ensure that each spun class name is unique
|
jaroslav@1646
|
84 |
private static final AtomicInteger counter = new AtomicInteger(0);
|
jaroslav@1646
|
85 |
|
jaroslav@1646
|
86 |
// For dumping generated classes to disk, for debugging purposes
|
jaroslav@1646
|
87 |
private static final ProxyClassesDumper dumper;
|
jaroslav@1646
|
88 |
|
jaroslav@1646
|
89 |
static {
|
jaroslav@1646
|
90 |
final String key = "jdk.internal.lambda.dumpProxyClasses";
|
jaroslav@1646
|
91 |
String path = AccessController.doPrivileged(
|
jaroslav@1646
|
92 |
new GetPropertyAction(key), null,
|
jaroslav@1646
|
93 |
new PropertyPermission(key , "read"));
|
jaroslav@1646
|
94 |
dumper = (null == path) ? null : ProxyClassesDumper.getInstance(path);
|
jaroslav@1646
|
95 |
}
|
jaroslav@1646
|
96 |
|
jaroslav@1646
|
97 |
// See context values in AbstractValidatingLambdaMetafactory
|
jaroslav@1646
|
98 |
private final String implMethodClassName; // Name of type containing implementation "CC"
|
jaroslav@1646
|
99 |
private final String implMethodName; // Name of implementation method "impl"
|
jaroslav@1646
|
100 |
private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
|
jaroslav@1646
|
101 |
private final Class<?> implMethodReturnClass; // class for implementaion method return type "Ljava/lang/String;"
|
jaroslav@1646
|
102 |
private final MethodType constructorType; // Generated class constructor type "(CC)void"
|
jaroslav@1646
|
103 |
private final ClassWriter cw; // ASM class writer
|
jaroslav@1646
|
104 |
private final String[] argNames; // Generated names for the constructor arguments
|
jaroslav@1646
|
105 |
private final String[] argDescs; // Type descriptors for the constructor arguments
|
jaroslav@1646
|
106 |
private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
|
jaroslav@1646
|
107 |
|
jaroslav@1646
|
108 |
/**
|
jaroslav@1646
|
109 |
* General meta-factory constructor, supporting both standard cases and
|
jaroslav@1646
|
110 |
* allowing for uncommon options such as serialization or bridging.
|
jaroslav@1646
|
111 |
*
|
jaroslav@1646
|
112 |
* @param caller Stacked automatically by VM; represents a lookup context
|
jaroslav@1646
|
113 |
* with the accessibility privileges of the caller.
|
jaroslav@1646
|
114 |
* @param invokedType Stacked automatically by VM; the signature of the
|
jaroslav@1646
|
115 |
* invoked method, which includes the expected static
|
jaroslav@1646
|
116 |
* type of the returned lambda object, and the static
|
jaroslav@1646
|
117 |
* types of the captured arguments for the lambda. In
|
jaroslav@1646
|
118 |
* the event that the implementation method is an
|
jaroslav@1646
|
119 |
* instance method, the first argument in the invocation
|
jaroslav@1646
|
120 |
* signature will correspond to the receiver.
|
jaroslav@1646
|
121 |
* @param samMethodName Name of the method in the functional interface to
|
jaroslav@1646
|
122 |
* which the lambda or method reference is being
|
jaroslav@1646
|
123 |
* converted, represented as a String.
|
jaroslav@1646
|
124 |
* @param samMethodType Type of the method in the functional interface to
|
jaroslav@1646
|
125 |
* which the lambda or method reference is being
|
jaroslav@1646
|
126 |
* converted, represented as a MethodType.
|
jaroslav@1646
|
127 |
* @param implMethod The implementation method which should be called (with
|
jaroslav@1646
|
128 |
* suitable adaptation of argument types, return types,
|
jaroslav@1646
|
129 |
* and adjustment for captured arguments) when methods of
|
jaroslav@1646
|
130 |
* the resulting functional interface instance are invoked.
|
jaroslav@1646
|
131 |
* @param instantiatedMethodType The signature of the primary functional
|
jaroslav@1646
|
132 |
* interface method after type variables are
|
jaroslav@1646
|
133 |
* substituted with their instantiation from
|
jaroslav@1646
|
134 |
* the capture site
|
jaroslav@1646
|
135 |
* @param isSerializable Should the lambda be made serializable? If set,
|
jaroslav@1646
|
136 |
* either the target type or one of the additional SAM
|
jaroslav@1646
|
137 |
* types must extend {@code Serializable}.
|
jaroslav@1646
|
138 |
* @param markerInterfaces Additional interfaces which the lambda object
|
jaroslav@1646
|
139 |
* should implement.
|
jaroslav@1646
|
140 |
* @param additionalBridges Method types for additional signatures to be
|
jaroslav@1646
|
141 |
* bridged to the implementation method
|
jaroslav@1646
|
142 |
* @throws LambdaConversionException If any of the meta-factory protocol
|
jaroslav@1646
|
143 |
* invariants are violated
|
jaroslav@1646
|
144 |
*/
|
jaroslav@1646
|
145 |
public InnerClassLambdaMetafactory(MethodHandles.Lookup caller,
|
jaroslav@1646
|
146 |
MethodType invokedType,
|
jaroslav@1646
|
147 |
String samMethodName,
|
jaroslav@1646
|
148 |
MethodType samMethodType,
|
jaroslav@1646
|
149 |
MethodHandle implMethod,
|
jaroslav@1646
|
150 |
MethodType instantiatedMethodType,
|
jaroslav@1646
|
151 |
boolean isSerializable,
|
jaroslav@1646
|
152 |
Class<?>[] markerInterfaces,
|
jaroslav@1646
|
153 |
MethodType[] additionalBridges)
|
jaroslav@1646
|
154 |
throws LambdaConversionException {
|
jaroslav@1646
|
155 |
super(caller, invokedType, samMethodName, samMethodType,
|
jaroslav@1646
|
156 |
implMethod, instantiatedMethodType,
|
jaroslav@1646
|
157 |
isSerializable, markerInterfaces, additionalBridges);
|
jaroslav@1646
|
158 |
implMethodClassName = implDefiningClass.getName().replace('.', '/');
|
jaroslav@1646
|
159 |
implMethodName = implInfo.getName();
|
jaroslav@1646
|
160 |
implMethodDesc = implMethodType.toMethodDescriptorString();
|
jaroslav@1646
|
161 |
implMethodReturnClass = (implKind == MethodHandleInfo.REF_newInvokeSpecial)
|
jaroslav@1646
|
162 |
? implDefiningClass
|
jaroslav@1646
|
163 |
: implMethodType.returnType();
|
jaroslav@1646
|
164 |
constructorType = invokedType.changeReturnType(Void.TYPE);
|
jaroslav@1646
|
165 |
lambdaClassName = targetClass.getName().replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
|
jaroslav@1646
|
166 |
cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
|
jaroslav@1646
|
167 |
int parameterCount = invokedType.parameterCount();
|
jaroslav@1646
|
168 |
if (parameterCount > 0) {
|
jaroslav@1646
|
169 |
argNames = new String[parameterCount];
|
jaroslav@1646
|
170 |
argDescs = new String[parameterCount];
|
jaroslav@1646
|
171 |
for (int i = 0; i < parameterCount; i++) {
|
jaroslav@1646
|
172 |
argNames[i] = "arg$" + (i + 1);
|
jaroslav@1646
|
173 |
argDescs[i] = BytecodeDescriptor.unparse(invokedType.parameterType(i));
|
jaroslav@1646
|
174 |
}
|
jaroslav@1646
|
175 |
} else {
|
jaroslav@1646
|
176 |
argNames = argDescs = EMPTY_STRING_ARRAY;
|
jaroslav@1646
|
177 |
}
|
jaroslav@1646
|
178 |
}
|
jaroslav@1646
|
179 |
|
jaroslav@1646
|
180 |
/**
|
jaroslav@1646
|
181 |
* Build the CallSite. Generate a class file which implements the functional
|
jaroslav@1646
|
182 |
* interface, define the class, if there are no parameters create an instance
|
jaroslav@1646
|
183 |
* of the class which the CallSite will return, otherwise, generate handles
|
jaroslav@1646
|
184 |
* which will call the class' constructor.
|
jaroslav@1646
|
185 |
*
|
jaroslav@1646
|
186 |
* @return a CallSite, which, when invoked, will return an instance of the
|
jaroslav@1646
|
187 |
* functional interface
|
jaroslav@1646
|
188 |
* @throws ReflectiveOperationException
|
jaroslav@1646
|
189 |
* @throws LambdaConversionException If properly formed functional interface
|
jaroslav@1646
|
190 |
* is not found
|
jaroslav@1646
|
191 |
*/
|
jaroslav@1646
|
192 |
@Override
|
jaroslav@1646
|
193 |
CallSite buildCallSite() throws LambdaConversionException {
|
jaroslav@1646
|
194 |
final Class<?> innerClass = spinInnerClass();
|
jaroslav@1646
|
195 |
if (invokedType.parameterCount() == 0) {
|
jaroslav@1646
|
196 |
final Constructor[] ctrs = AccessController.doPrivileged(
|
jaroslav@1646
|
197 |
new PrivilegedAction<Constructor[]>() {
|
jaroslav@1646
|
198 |
@Override
|
jaroslav@1646
|
199 |
public Constructor[] run() {
|
jaroslav@1646
|
200 |
Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
|
jaroslav@1646
|
201 |
if (ctrs.length == 1) {
|
jaroslav@1646
|
202 |
// The lambda implementing inner class constructor is private, set
|
jaroslav@1646
|
203 |
// it accessible (by us) before creating the constant sole instance
|
jaroslav@1646
|
204 |
ctrs[0].setAccessible(true);
|
jaroslav@1646
|
205 |
}
|
jaroslav@1646
|
206 |
return ctrs;
|
jaroslav@1646
|
207 |
}
|
jaroslav@1646
|
208 |
});
|
jaroslav@1646
|
209 |
if (ctrs.length != 1) {
|
jaroslav@1646
|
210 |
throw new LambdaConversionException("Expected one lambda constructor for "
|
jaroslav@1646
|
211 |
+ innerClass.getCanonicalName() + ", got " + ctrs.length);
|
jaroslav@1646
|
212 |
}
|
jaroslav@1646
|
213 |
|
jaroslav@1646
|
214 |
try {
|
jaroslav@1646
|
215 |
Object inst = ctrs[0].newInstance();
|
jaroslav@1646
|
216 |
return new ConstantCallSite(MethodHandles.constant(samBase, inst));
|
jaroslav@1646
|
217 |
}
|
jaroslav@1646
|
218 |
catch (ReflectiveOperationException e) {
|
jaroslav@1646
|
219 |
throw new LambdaConversionException("Exception instantiating lambda object", e);
|
jaroslav@1646
|
220 |
}
|
jaroslav@1646
|
221 |
} else {
|
jaroslav@1646
|
222 |
try {
|
jaroslav@1646
|
223 |
UNSAFE.ensureClassInitialized(innerClass);
|
jaroslav@1646
|
224 |
return new ConstantCallSite(
|
jaroslav@1646
|
225 |
MethodHandles.Lookup.IMPL_LOOKUP
|
jaroslav@1646
|
226 |
.findStatic(innerClass, NAME_FACTORY, invokedType));
|
jaroslav@1646
|
227 |
}
|
jaroslav@1646
|
228 |
catch (ReflectiveOperationException e) {
|
jaroslav@1646
|
229 |
throw new LambdaConversionException("Exception finding constructor", e);
|
jaroslav@1646
|
230 |
}
|
jaroslav@1646
|
231 |
}
|
jaroslav@1646
|
232 |
}
|
jaroslav@1646
|
233 |
|
jaroslav@1646
|
234 |
/**
|
jaroslav@1646
|
235 |
* Generate a class file which implements the functional
|
jaroslav@1646
|
236 |
* interface, define and return the class.
|
jaroslav@1646
|
237 |
*
|
jaroslav@1646
|
238 |
* @implNote The class that is generated does not include signature
|
jaroslav@1646
|
239 |
* information for exceptions that may be present on the SAM method.
|
jaroslav@1646
|
240 |
* This is to reduce classfile size, and is harmless as checked exceptions
|
jaroslav@1646
|
241 |
* are erased anyway, no one will ever compile against this classfile,
|
jaroslav@1646
|
242 |
* and we make no guarantees about the reflective properties of lambda
|
jaroslav@1646
|
243 |
* objects.
|
jaroslav@1646
|
244 |
*
|
jaroslav@1646
|
245 |
* @return a Class which implements the functional interface
|
jaroslav@1646
|
246 |
* @throws LambdaConversionException If properly formed functional interface
|
jaroslav@1646
|
247 |
* is not found
|
jaroslav@1646
|
248 |
*/
|
jaroslav@1646
|
249 |
private Class<?> spinInnerClass() throws LambdaConversionException {
|
jaroslav@1646
|
250 |
String[] interfaces;
|
jaroslav@1646
|
251 |
String samIntf = samBase.getName().replace('.', '/');
|
jaroslav@1646
|
252 |
boolean accidentallySerializable = !isSerializable && Serializable.class.isAssignableFrom(samBase);
|
jaroslav@1646
|
253 |
if (markerInterfaces.length == 0) {
|
jaroslav@1646
|
254 |
interfaces = new String[]{samIntf};
|
jaroslav@1646
|
255 |
} else {
|
jaroslav@1646
|
256 |
// Assure no duplicate interfaces (ClassFormatError)
|
jaroslav@1646
|
257 |
Set<String> itfs = new LinkedHashSet<>(markerInterfaces.length + 1);
|
jaroslav@1646
|
258 |
itfs.add(samIntf);
|
jaroslav@1646
|
259 |
for (Class<?> markerInterface : markerInterfaces) {
|
jaroslav@1646
|
260 |
itfs.add(markerInterface.getName().replace('.', '/'));
|
jaroslav@1646
|
261 |
accidentallySerializable |= !isSerializable && Serializable.class.isAssignableFrom(markerInterface);
|
jaroslav@1646
|
262 |
}
|
jaroslav@1646
|
263 |
interfaces = itfs.toArray(new String[itfs.size()]);
|
jaroslav@1646
|
264 |
}
|
jaroslav@1646
|
265 |
|
jaroslav@1646
|
266 |
cw.visit(CLASSFILE_VERSION, ACC_SUPER + ACC_FINAL + ACC_SYNTHETIC,
|
jaroslav@1646
|
267 |
lambdaClassName, null,
|
jaroslav@1646
|
268 |
JAVA_LANG_OBJECT, interfaces);
|
jaroslav@1646
|
269 |
|
jaroslav@1646
|
270 |
// Generate final fields to be filled in by constructor
|
jaroslav@1646
|
271 |
for (int i = 0; i < argDescs.length; i++) {
|
jaroslav@1646
|
272 |
FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL,
|
jaroslav@1646
|
273 |
argNames[i],
|
jaroslav@1646
|
274 |
argDescs[i],
|
jaroslav@1646
|
275 |
null, null);
|
jaroslav@1646
|
276 |
fv.visitEnd();
|
jaroslav@1646
|
277 |
}
|
jaroslav@1646
|
278 |
|
jaroslav@1646
|
279 |
generateConstructor();
|
jaroslav@1646
|
280 |
|
jaroslav@1646
|
281 |
if (invokedType.parameterCount() != 0) {
|
jaroslav@1646
|
282 |
generateFactory();
|
jaroslav@1646
|
283 |
}
|
jaroslav@1646
|
284 |
|
jaroslav@1646
|
285 |
// Forward the SAM method
|
jaroslav@1646
|
286 |
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
|
jaroslav@1646
|
287 |
samMethodType.toMethodDescriptorString(), null, null);
|
jaroslav@1646
|
288 |
new ForwardingMethodGenerator(mv).generate(samMethodType);
|
jaroslav@1646
|
289 |
|
jaroslav@1646
|
290 |
// Forward the bridges
|
jaroslav@1646
|
291 |
if (additionalBridges != null) {
|
jaroslav@1646
|
292 |
for (MethodType mt : additionalBridges) {
|
jaroslav@1646
|
293 |
mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
|
jaroslav@1646
|
294 |
mt.toMethodDescriptorString(), null, null);
|
jaroslav@1646
|
295 |
new ForwardingMethodGenerator(mv).generate(mt);
|
jaroslav@1646
|
296 |
}
|
jaroslav@1646
|
297 |
}
|
jaroslav@1646
|
298 |
|
jaroslav@1646
|
299 |
if (isSerializable)
|
jaroslav@1646
|
300 |
generateSerializationFriendlyMethods();
|
jaroslav@1646
|
301 |
else if (accidentallySerializable)
|
jaroslav@1646
|
302 |
generateSerializationHostileMethods();
|
jaroslav@1646
|
303 |
|
jaroslav@1646
|
304 |
cw.visitEnd();
|
jaroslav@1646
|
305 |
|
jaroslav@1646
|
306 |
// Define the generated class in this VM.
|
jaroslav@1646
|
307 |
|
jaroslav@1646
|
308 |
final byte[] classBytes = cw.toByteArray();
|
jaroslav@1646
|
309 |
|
jaroslav@1646
|
310 |
// If requested, dump out to a file for debugging purposes
|
jaroslav@1646
|
311 |
if (dumper != null) {
|
jaroslav@1646
|
312 |
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
jaroslav@1646
|
313 |
@Override
|
jaroslav@1646
|
314 |
public Void run() {
|
jaroslav@1646
|
315 |
dumper.dumpClass(lambdaClassName, classBytes);
|
jaroslav@1646
|
316 |
return null;
|
jaroslav@1646
|
317 |
}
|
jaroslav@1646
|
318 |
}, null,
|
jaroslav@1646
|
319 |
new FilePermission("<<ALL FILES>>", "read, write"),
|
jaroslav@1646
|
320 |
// createDirectories may need it
|
jaroslav@1646
|
321 |
new PropertyPermission("user.dir", "read"));
|
jaroslav@1646
|
322 |
}
|
jaroslav@1646
|
323 |
|
jaroslav@1646
|
324 |
return UNSAFE.defineAnonymousClass(targetClass, classBytes, null);
|
jaroslav@1646
|
325 |
}
|
jaroslav@1646
|
326 |
|
jaroslav@1646
|
327 |
/**
|
jaroslav@1646
|
328 |
* Generate the factory method for the class
|
jaroslav@1646
|
329 |
*/
|
jaroslav@1646
|
330 |
private void generateFactory() {
|
jaroslav@1646
|
331 |
MethodVisitor m = cw.visitMethod(ACC_PRIVATE | ACC_STATIC, NAME_FACTORY, invokedType.toMethodDescriptorString(), null, null);
|
jaroslav@1646
|
332 |
m.visitCode();
|
jaroslav@1646
|
333 |
m.visitTypeInsn(NEW, lambdaClassName);
|
jaroslav@1646
|
334 |
m.visitInsn(Opcodes.DUP);
|
jaroslav@1646
|
335 |
int parameterCount = invokedType.parameterCount();
|
jaroslav@1646
|
336 |
for (int typeIndex = 0, varIndex = 0; typeIndex < parameterCount; typeIndex++) {
|
jaroslav@1646
|
337 |
Class<?> argType = invokedType.parameterType(typeIndex);
|
jaroslav@1646
|
338 |
m.visitVarInsn(getLoadOpcode(argType), varIndex);
|
jaroslav@1646
|
339 |
varIndex += getParameterSize(argType);
|
jaroslav@1646
|
340 |
}
|
jaroslav@1646
|
341 |
m.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString());
|
jaroslav@1646
|
342 |
m.visitInsn(ARETURN);
|
jaroslav@1646
|
343 |
m.visitMaxs(-1, -1);
|
jaroslav@1646
|
344 |
m.visitEnd();
|
jaroslav@1646
|
345 |
}
|
jaroslav@1646
|
346 |
|
jaroslav@1646
|
347 |
/**
|
jaroslav@1646
|
348 |
* Generate the constructor for the class
|
jaroslav@1646
|
349 |
*/
|
jaroslav@1646
|
350 |
private void generateConstructor() {
|
jaroslav@1646
|
351 |
// Generate constructor
|
jaroslav@1646
|
352 |
MethodVisitor ctor = cw.visitMethod(ACC_PRIVATE, NAME_CTOR,
|
jaroslav@1646
|
353 |
constructorType.toMethodDescriptorString(), null, null);
|
jaroslav@1646
|
354 |
ctor.visitCode();
|
jaroslav@1646
|
355 |
ctor.visitVarInsn(ALOAD, 0);
|
jaroslav@1646
|
356 |
ctor.visitMethodInsn(INVOKESPECIAL, JAVA_LANG_OBJECT, NAME_CTOR,
|
jaroslav@1646
|
357 |
METHOD_DESCRIPTOR_VOID);
|
jaroslav@1646
|
358 |
int parameterCount = invokedType.parameterCount();
|
jaroslav@1646
|
359 |
for (int i = 0, lvIndex = 0; i < parameterCount; i++) {
|
jaroslav@1646
|
360 |
ctor.visitVarInsn(ALOAD, 0);
|
jaroslav@1646
|
361 |
Class<?> argType = invokedType.parameterType(i);
|
jaroslav@1646
|
362 |
ctor.visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
|
jaroslav@1646
|
363 |
lvIndex += getParameterSize(argType);
|
jaroslav@1646
|
364 |
ctor.visitFieldInsn(PUTFIELD, lambdaClassName, argNames[i], argDescs[i]);
|
jaroslav@1646
|
365 |
}
|
jaroslav@1646
|
366 |
ctor.visitInsn(RETURN);
|
jaroslav@1646
|
367 |
// Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
jaroslav@1646
|
368 |
ctor.visitMaxs(-1, -1);
|
jaroslav@1646
|
369 |
ctor.visitEnd();
|
jaroslav@1646
|
370 |
}
|
jaroslav@1646
|
371 |
|
jaroslav@1646
|
372 |
/**
|
jaroslav@1646
|
373 |
* Generate a writeReplace method that supports serialization
|
jaroslav@1646
|
374 |
*/
|
jaroslav@1646
|
375 |
private void generateSerializationFriendlyMethods() {
|
jaroslav@1646
|
376 |
TypeConvertingMethodAdapter mv
|
jaroslav@1646
|
377 |
= new TypeConvertingMethodAdapter(
|
jaroslav@1646
|
378 |
cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
|
jaroslav@1646
|
379 |
NAME_METHOD_WRITE_REPLACE, DESCR_METHOD_WRITE_REPLACE,
|
jaroslav@1646
|
380 |
null, null));
|
jaroslav@1646
|
381 |
|
jaroslav@1646
|
382 |
mv.visitCode();
|
jaroslav@1646
|
383 |
mv.visitTypeInsn(NEW, NAME_SERIALIZED_LAMBDA);
|
jaroslav@1646
|
384 |
mv.visitInsn(DUP);
|
jaroslav@1646
|
385 |
mv.visitLdcInsn(Type.getType(targetClass));
|
jaroslav@1646
|
386 |
mv.visitLdcInsn(invokedType.returnType().getName().replace('.', '/'));
|
jaroslav@1646
|
387 |
mv.visitLdcInsn(samMethodName);
|
jaroslav@1646
|
388 |
mv.visitLdcInsn(samMethodType.toMethodDescriptorString());
|
jaroslav@1646
|
389 |
mv.visitLdcInsn(implInfo.getReferenceKind());
|
jaroslav@1646
|
390 |
mv.visitLdcInsn(implInfo.getDeclaringClass().getName().replace('.', '/'));
|
jaroslav@1646
|
391 |
mv.visitLdcInsn(implInfo.getName());
|
jaroslav@1646
|
392 |
mv.visitLdcInsn(implInfo.getMethodType().toMethodDescriptorString());
|
jaroslav@1646
|
393 |
mv.visitLdcInsn(instantiatedMethodType.toMethodDescriptorString());
|
jaroslav@1646
|
394 |
mv.iconst(argDescs.length);
|
jaroslav@1646
|
395 |
mv.visitTypeInsn(ANEWARRAY, JAVA_LANG_OBJECT);
|
jaroslav@1646
|
396 |
for (int i = 0; i < argDescs.length; i++) {
|
jaroslav@1646
|
397 |
mv.visitInsn(DUP);
|
jaroslav@1646
|
398 |
mv.iconst(i);
|
jaroslav@1646
|
399 |
mv.visitVarInsn(ALOAD, 0);
|
jaroslav@1646
|
400 |
mv.visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
|
jaroslav@1646
|
401 |
mv.boxIfTypePrimitive(Type.getType(argDescs[i]));
|
jaroslav@1646
|
402 |
mv.visitInsn(AASTORE);
|
jaroslav@1646
|
403 |
}
|
jaroslav@1646
|
404 |
mv.visitMethodInsn(INVOKESPECIAL, NAME_SERIALIZED_LAMBDA, NAME_CTOR,
|
jaroslav@1646
|
405 |
DESCR_CTOR_SERIALIZED_LAMBDA);
|
jaroslav@1646
|
406 |
mv.visitInsn(ARETURN);
|
jaroslav@1646
|
407 |
// Maxs computed by ClassWriter.COMPUTE_MAXS, these arguments ignored
|
jaroslav@1646
|
408 |
mv.visitMaxs(-1, -1);
|
jaroslav@1646
|
409 |
mv.visitEnd();
|
jaroslav@1646
|
410 |
}
|
jaroslav@1646
|
411 |
|
jaroslav@1646
|
412 |
/**
|
jaroslav@1646
|
413 |
* Generate a readObject/writeObject method that is hostile to serialization
|
jaroslav@1646
|
414 |
*/
|
jaroslav@1646
|
415 |
private void generateSerializationHostileMethods() {
|
jaroslav@1646
|
416 |
MethodVisitor mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
|
jaroslav@1646
|
417 |
NAME_METHOD_WRITE_OBJECT, DESCR_METHOD_WRITE_OBJECT,
|
jaroslav@1646
|
418 |
null, SER_HOSTILE_EXCEPTIONS);
|
jaroslav@1646
|
419 |
mv.visitCode();
|
jaroslav@1646
|
420 |
mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
|
jaroslav@1646
|
421 |
mv.visitInsn(DUP);
|
jaroslav@1646
|
422 |
mv.visitLdcInsn("Non-serializable lambda");
|
jaroslav@1646
|
423 |
mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
|
jaroslav@1646
|
424 |
DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION);
|
jaroslav@1646
|
425 |
mv.visitInsn(ATHROW);
|
jaroslav@1646
|
426 |
mv.visitMaxs(-1, -1);
|
jaroslav@1646
|
427 |
mv.visitEnd();
|
jaroslav@1646
|
428 |
|
jaroslav@1646
|
429 |
mv = cw.visitMethod(ACC_PRIVATE + ACC_FINAL,
|
jaroslav@1646
|
430 |
NAME_METHOD_READ_OBJECT, DESCR_METHOD_READ_OBJECT,
|
jaroslav@1646
|
431 |
null, SER_HOSTILE_EXCEPTIONS);
|
jaroslav@1646
|
432 |
mv.visitCode();
|
jaroslav@1646
|
433 |
mv.visitTypeInsn(NEW, NAME_NOT_SERIALIZABLE_EXCEPTION);
|
jaroslav@1646
|
434 |
mv.visitInsn(DUP);
|
jaroslav@1646
|
435 |
mv.visitLdcInsn("Non-serializable lambda");
|
jaroslav@1646
|
436 |
mv.visitMethodInsn(INVOKESPECIAL, NAME_NOT_SERIALIZABLE_EXCEPTION, NAME_CTOR,
|
jaroslav@1646
|
437 |
DESCR_CTOR_NOT_SERIALIZABLE_EXCEPTION);
|
jaroslav@1646
|
438 |
mv.visitInsn(ATHROW);
|
jaroslav@1646
|
439 |
mv.visitMaxs(-1, -1);
|
jaroslav@1646
|
440 |
mv.visitEnd();
|
jaroslav@1646
|
441 |
}
|
jaroslav@1646
|
442 |
|
jaroslav@1646
|
443 |
/**
|
jaroslav@1646
|
444 |
* This class generates a method body which calls the lambda implementation
|
jaroslav@1646
|
445 |
* method, converting arguments, as needed.
|
jaroslav@1646
|
446 |
*/
|
jaroslav@1646
|
447 |
private class ForwardingMethodGenerator extends TypeConvertingMethodAdapter {
|
jaroslav@1646
|
448 |
|
jaroslav@1646
|
449 |
ForwardingMethodGenerator(MethodVisitor mv) {
|
jaroslav@1646
|
450 |
super(mv);
|
jaroslav@1646
|
451 |
}
|
jaroslav@1646
|
452 |
|
jaroslav@1646
|
453 |
void generate(MethodType methodType) {
|
jaroslav@1646
|
454 |
visitCode();
|
jaroslav@1646
|
455 |
|
jaroslav@1646
|
456 |
if (implKind == MethodHandleInfo.REF_newInvokeSpecial) {
|
jaroslav@1646
|
457 |
visitTypeInsn(NEW, implMethodClassName);
|
jaroslav@1646
|
458 |
visitInsn(DUP);
|
jaroslav@1646
|
459 |
}
|
jaroslav@1646
|
460 |
for (int i = 0; i < argNames.length; i++) {
|
jaroslav@1646
|
461 |
visitVarInsn(ALOAD, 0);
|
jaroslav@1646
|
462 |
visitFieldInsn(GETFIELD, lambdaClassName, argNames[i], argDescs[i]);
|
jaroslav@1646
|
463 |
}
|
jaroslav@1646
|
464 |
|
jaroslav@1646
|
465 |
convertArgumentTypes(methodType);
|
jaroslav@1646
|
466 |
|
jaroslav@1646
|
467 |
// Invoke the method we want to forward to
|
jaroslav@1646
|
468 |
visitMethodInsn(invocationOpcode(), implMethodClassName,
|
jaroslav@1646
|
469 |
implMethodName, implMethodDesc,
|
jaroslav@1646
|
470 |
implDefiningClass.isInterface());
|
jaroslav@1646
|
471 |
|
jaroslav@1646
|
472 |
// Convert the return value (if any) and return it
|
jaroslav@1646
|
473 |
// Note: if adapting from non-void to void, the 'return'
|
jaroslav@1646
|
474 |
// instruction will pop the unneeded result
|
jaroslav@1646
|
475 |
Class<?> samReturnClass = methodType.returnType();
|
jaroslav@1646
|
476 |
convertType(implMethodReturnClass, samReturnClass, samReturnClass);
|
jaroslav@1646
|
477 |
visitInsn(getReturnOpcode(samReturnClass));
|
jaroslav@1646
|
478 |
// Maxs computed by ClassWriter.COMPUTE_MAXS,these arguments ignored
|
jaroslav@1646
|
479 |
visitMaxs(-1, -1);
|
jaroslav@1646
|
480 |
visitEnd();
|
jaroslav@1646
|
481 |
}
|
jaroslav@1646
|
482 |
|
jaroslav@1646
|
483 |
private void convertArgumentTypes(MethodType samType) {
|
jaroslav@1646
|
484 |
int lvIndex = 0;
|
jaroslav@1646
|
485 |
boolean samIncludesReceiver = implIsInstanceMethod &&
|
jaroslav@1646
|
486 |
invokedType.parameterCount() == 0;
|
jaroslav@1646
|
487 |
int samReceiverLength = samIncludesReceiver ? 1 : 0;
|
jaroslav@1646
|
488 |
if (samIncludesReceiver) {
|
jaroslav@1646
|
489 |
// push receiver
|
jaroslav@1646
|
490 |
Class<?> rcvrType = samType.parameterType(0);
|
jaroslav@1646
|
491 |
visitVarInsn(getLoadOpcode(rcvrType), lvIndex + 1);
|
jaroslav@1646
|
492 |
lvIndex += getParameterSize(rcvrType);
|
jaroslav@1646
|
493 |
convertType(rcvrType, implDefiningClass, instantiatedMethodType.parameterType(0));
|
jaroslav@1646
|
494 |
}
|
jaroslav@1646
|
495 |
int samParametersLength = samType.parameterCount();
|
jaroslav@1646
|
496 |
int argOffset = implMethodType.parameterCount() - samParametersLength;
|
jaroslav@1646
|
497 |
for (int i = samReceiverLength; i < samParametersLength; i++) {
|
jaroslav@1646
|
498 |
Class<?> argType = samType.parameterType(i);
|
jaroslav@1646
|
499 |
visitVarInsn(getLoadOpcode(argType), lvIndex + 1);
|
jaroslav@1646
|
500 |
lvIndex += getParameterSize(argType);
|
jaroslav@1646
|
501 |
convertType(argType, implMethodType.parameterType(argOffset + i), instantiatedMethodType.parameterType(i));
|
jaroslav@1646
|
502 |
}
|
jaroslav@1646
|
503 |
}
|
jaroslav@1646
|
504 |
|
jaroslav@1646
|
505 |
private int invocationOpcode() throws InternalError {
|
jaroslav@1646
|
506 |
switch (implKind) {
|
jaroslav@1646
|
507 |
case MethodHandleInfo.REF_invokeStatic:
|
jaroslav@1646
|
508 |
return INVOKESTATIC;
|
jaroslav@1646
|
509 |
case MethodHandleInfo.REF_newInvokeSpecial:
|
jaroslav@1646
|
510 |
return INVOKESPECIAL;
|
jaroslav@1646
|
511 |
case MethodHandleInfo.REF_invokeVirtual:
|
jaroslav@1646
|
512 |
return INVOKEVIRTUAL;
|
jaroslav@1646
|
513 |
case MethodHandleInfo.REF_invokeInterface:
|
jaroslav@1646
|
514 |
return INVOKEINTERFACE;
|
jaroslav@1646
|
515 |
case MethodHandleInfo.REF_invokeSpecial:
|
jaroslav@1646
|
516 |
return INVOKESPECIAL;
|
jaroslav@1646
|
517 |
default:
|
jaroslav@1646
|
518 |
throw new InternalError("Unexpected invocation kind: " + implKind);
|
jaroslav@1646
|
519 |
}
|
jaroslav@1646
|
520 |
}
|
jaroslav@1646
|
521 |
}
|
jaroslav@1646
|
522 |
|
jaroslav@1646
|
523 |
static int getParameterSize(Class<?> c) {
|
jaroslav@1646
|
524 |
if (c == Void.TYPE) {
|
jaroslav@1646
|
525 |
return 0;
|
jaroslav@1646
|
526 |
} else if (c == Long.TYPE || c == Double.TYPE) {
|
jaroslav@1646
|
527 |
return 2;
|
jaroslav@1646
|
528 |
}
|
jaroslav@1646
|
529 |
return 1;
|
jaroslav@1646
|
530 |
}
|
jaroslav@1646
|
531 |
|
jaroslav@1646
|
532 |
static int getLoadOpcode(Class<?> c) {
|
jaroslav@1646
|
533 |
if(c == Void.TYPE) {
|
jaroslav@1646
|
534 |
throw new InternalError("Unexpected void type of load opcode");
|
jaroslav@1646
|
535 |
}
|
jaroslav@1646
|
536 |
return ILOAD + getOpcodeOffset(c);
|
jaroslav@1646
|
537 |
}
|
jaroslav@1646
|
538 |
|
jaroslav@1646
|
539 |
static int getReturnOpcode(Class<?> c) {
|
jaroslav@1646
|
540 |
if(c == Void.TYPE) {
|
jaroslav@1646
|
541 |
return RETURN;
|
jaroslav@1646
|
542 |
}
|
jaroslav@1646
|
543 |
return IRETURN + getOpcodeOffset(c);
|
jaroslav@1646
|
544 |
}
|
jaroslav@1646
|
545 |
|
jaroslav@1646
|
546 |
private static int getOpcodeOffset(Class<?> c) {
|
jaroslav@1646
|
547 |
if (c.isPrimitive()) {
|
jaroslav@1646
|
548 |
if (c == Long.TYPE) {
|
jaroslav@1646
|
549 |
return 1;
|
jaroslav@1646
|
550 |
} else if (c == Float.TYPE) {
|
jaroslav@1646
|
551 |
return 2;
|
jaroslav@1646
|
552 |
} else if (c == Double.TYPE) {
|
jaroslav@1646
|
553 |
return 3;
|
jaroslav@1646
|
554 |
}
|
jaroslav@1646
|
555 |
return 0;
|
jaroslav@1646
|
556 |
} else {
|
jaroslav@1646
|
557 |
return 4;
|
jaroslav@1646
|
558 |
}
|
jaroslav@1646
|
559 |
}
|
jaroslav@1646
|
560 |
|
jaroslav@1646
|
561 |
}
|