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 java.lang.invoke;
27
28 import jdk.internal.org.objectweb.asm.*;
29 import sun.misc.Unsafe;
30
31 import java.lang.reflect.Constructor;
32 import java.security.AccessController;
33 import java.security.PrivilegedAction;
34 import java.security.ProtectionDomain;
35 import java.util.concurrent.atomic.AtomicInteger;
36
37 import static jdk.internal.org.objectweb.asm.Opcodes.*;
38
39 /**
40 * Lambda metafactory implementation which dynamically creates an
41 * inner-class-like class per lambda callsite.
42 *
43 * @see LambdaMetafactory
44 */
45 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
46 private static final Unsafe UNSAFE = Unsafe.getUnsafe();
47
48 private static final int CLASSFILE_VERSION = 51;
49 private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
50 private static final String NAME_MAGIC_ACCESSOR_IMPL = "java/lang/invoke/MagicLambdaImpl";
51 private static final String NAME_CTOR = "<init>";
52
53 //Serialization support
54 private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
55 private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
56 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
57 private static final String NAME_OBJECT = "java/lang/Object";
58 private static final String DESCR_CTOR_SERIALIZED_LAMBDA
59 = MethodType.methodType(void.class,
60 Class.class,
61 String.class, String.class, String.class,
62 int.class, String.class, String.class, String.class,
63 String.class,
64 Object[].class).toMethodDescriptorString();
65
66 // Used to ensure that each spun class name is unique
67 private static final AtomicInteger counter = new AtomicInteger(0);
68
69 // See context values in AbstractValidatingLambdaMetafactory
70 private final String implMethodClassName; // Name of type containing implementation "CC"
71 private final String implMethodName; // Name of implementation method "impl"
72 private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
73 private final Type[] implMethodArgumentTypes; // ASM types for implementaion method parameters
74 private final Type implMethodReturnType; // ASM type for implementaion method return type "Ljava/lang/String;"
75 private final MethodType constructorType; // Generated class constructor type "(CC)void"
76 private final String constructorDesc; // Type descriptor for constructor "(LCC;)V"
77 private final ClassWriter cw; // ASM class writer
78 private final Type[] argTypes; // ASM types for the constructor arguments
79 private final String[] argNames; // Generated names for the constructor arguments
80 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
81 private final Type[] instantiatedArgumentTypes; // ASM types for the functional interface arguments
82
83 /**
84 * General meta-factory constructor, supporting both standard cases and
85 * allowing for uncommon options such as serialization or bridging.
86 *
87 * @param caller Stacked automatically by VM; represents a lookup context
88 * with the accessibility privileges of the caller.
181 }
182 // The lambda implementing inner class constructor is private, set
183 // it accessible (by us) before creating the constant sole instance
184 AccessController.doPrivileged(new PrivilegedAction<Void>() {
185 @Override
186 public Void run() {
187 ctrs[0].setAccessible(true);
188 return null;
189 }
190 });
191 Object inst = ctrs[0].newInstance();
192 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
193 } else {
194 return new ConstantCallSite(
195 MethodHandles.Lookup.IMPL_LOOKUP
196 .findConstructor(innerClass, constructorType)
197 .asType(constructorType.changeReturnType(samBase)));
198 }
199 }
200
201 /**
202 * Generate a class file which implements the functional
203 * interface, define and return the class.
204 *
205 * @implNote The class that is generated does not include signature
206 * information for exceptions that may be present on the SAM method.
207 * This is to reduce classfile size, and is harmless as checked exceptions
208 * are erased anyway, no one will ever compile against this classfile,
209 * and we make no guarantees about the reflective properties of lambda
210 * objects.
211 *
212 * @return a Class which implements the functional interface
213 * @throws LambdaConversionException If properly formed functional interface
214 * is not found
215 */
216 private Class<?> spinInnerClass() throws LambdaConversionException {
217 String[] interfaces = new String[markerInterfaces.length + 1];
218 interfaces[0] = samBase.getName().replace('.', '/');
219 for (int i=0; i<markerInterfaces.length; i++) {
220 interfaces[i+1] = markerInterfaces[i].getName().replace('.', '/');
242
243 // Forward the bridges
244 if (additionalBridges != null) {
245 for (MethodType mt : additionalBridges) {
246 methodDescriptor = mt.toMethodDescriptorString();
247 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
248 methodDescriptor, null, null);
249 new ForwardingMethodGenerator(mv).generate(methodDescriptor);
250 }
251 }
252
253 if (isSerializable)
254 generateWriteReplace();
255
256 cw.visitEnd();
257
258 // Define the generated class in this VM.
259
260 final byte[] classBytes = cw.toByteArray();
261
262 /*** Uncomment to dump the generated file
263 System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName,
264 classBytes.length);
265 try (FileOutputStream fos = new FileOutputStream(lambdaClassName
266 .replace('/', '.') + ".class")) {
267 fos.write(classBytes);
268 } catch (IOException ex) {
269 PlatformLogger.getLogger(InnerClassLambdaMetafactory.class
270 .getName()).severe(ex.getMessage(), ex);
271 }
272 ***/
273
274 ClassLoader loader = targetClass.getClassLoader();
275 ProtectionDomain pd = (loader == null)
276 ? null
277 : AccessController.doPrivileged(
278 new PrivilegedAction<ProtectionDomain>() {
279 @Override
280 public ProtectionDomain run() {
281 return targetClass.getProtectionDomain();
282 }
283 }
284 );
285
286 return UNSAFE.defineClass(lambdaClassName,
287 classBytes, 0, classBytes.length,
288 loader, pd);
289 }
290
291 /**
292 * Generate the constructor for the class
|
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 java.lang.invoke;
27
28 import jdk.internal.org.objectweb.asm.*;
29 import sun.misc.Unsafe;
30 import sun.util.logging.PlatformLogger;
31
32 import java.io.File;
33 import java.io.FileOutputStream;
34 import java.io.IOException;
35 import java.lang.reflect.Constructor;
36 import java.security.AccessController;
37 import java.security.PrivilegedAction;
38 import java.security.ProtectionDomain;
39 import java.util.concurrent.atomic.AtomicBoolean;
40 import java.util.concurrent.atomic.AtomicInteger;
41
42 import static jdk.internal.org.objectweb.asm.Opcodes.*;
43
44 /**
45 * Lambda metafactory implementation which dynamically creates an
46 * inner-class-like class per lambda callsite.
47 *
48 * @see LambdaMetafactory
49 */
50 /* package */ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
51 private static final Unsafe UNSAFE = Unsafe.getUnsafe();
52
53 private static final int CLASSFILE_VERSION = 51;
54 private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
55 private static final String NAME_MAGIC_ACCESSOR_IMPL = "java/lang/invoke/MagicLambdaImpl";
56 private static final String NAME_CTOR = "<init>";
57
58 //Serialization support
59 private static final char[] HEX = {
60 '0', '1', '2', '3', '4', '5', '6', '7',
61 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
62 };
63 private static final char[] BAD_CHARS = {
64 '\\', '/', ':', '*', '?', '"', '<', '>', '|'
65 };
66 private static final String[] REPLACEMENT = {
67 "%5C", ".", "%3A", "%2A", "%3F", "%22", "%3C", "%3E", "%7C"
68 };
69
70 private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
71 private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
72 private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
73 private static final String NAME_OBJECT = "java/lang/Object";
74 private static final String DESCR_CTOR_SERIALIZED_LAMBDA
75 = MethodType.methodType(void.class,
76 Class.class,
77 String.class, String.class, String.class,
78 int.class, String.class, String.class, String.class,
79 String.class,
80 Object[].class).toMethodDescriptorString();
81
82 // Used to ensure that each spun class name is unique
83 private static final AtomicInteger counter = new AtomicInteger(0);
84
85 // For dumping generated classes to disk, for debugging purposes
86 private static final String DUMP_PATH_PROPERTY = "jdk.internal.lambda.dumpProxyClasses";
87 private static final String dumpDir;
88 private static AtomicBoolean invalidDir = new AtomicBoolean(false);
89 static {
90 dumpDir = AccessController.doPrivileged(new PrivilegedAction<String>() {
91 @Override
92 public String run() {
93 String path = System.getProperty(DUMP_PATH_PROPERTY);
94 if (path != null) {
95 validateDumpDir(path);
96 }
97 return path;
98 }
99 });
100 }
101
102 // See context values in AbstractValidatingLambdaMetafactory
103 private final String implMethodClassName; // Name of type containing implementation "CC"
104 private final String implMethodName; // Name of implementation method "impl"
105 private final String implMethodDesc; // Type descriptor for implementation methods "(I)Ljava/lang/String;"
106 private final Type[] implMethodArgumentTypes; // ASM types for implementaion method parameters
107 private final Type implMethodReturnType; // ASM type for implementaion method return type "Ljava/lang/String;"
108 private final MethodType constructorType; // Generated class constructor type "(CC)void"
109 private final String constructorDesc; // Type descriptor for constructor "(LCC;)V"
110 private final ClassWriter cw; // ASM class writer
111 private final Type[] argTypes; // ASM types for the constructor arguments
112 private final String[] argNames; // Generated names for the constructor arguments
113 private final String lambdaClassName; // Generated name for the generated class "X$$Lambda$1"
114 private final Type[] instantiatedArgumentTypes; // ASM types for the functional interface arguments
115
116 /**
117 * General meta-factory constructor, supporting both standard cases and
118 * allowing for uncommon options such as serialization or bridging.
119 *
120 * @param caller Stacked automatically by VM; represents a lookup context
121 * with the accessibility privileges of the caller.
214 }
215 // The lambda implementing inner class constructor is private, set
216 // it accessible (by us) before creating the constant sole instance
217 AccessController.doPrivileged(new PrivilegedAction<Void>() {
218 @Override
219 public Void run() {
220 ctrs[0].setAccessible(true);
221 return null;
222 }
223 });
224 Object inst = ctrs[0].newInstance();
225 return new ConstantCallSite(MethodHandles.constant(samBase, inst));
226 } else {
227 return new ConstantCallSite(
228 MethodHandles.Lookup.IMPL_LOOKUP
229 .findConstructor(innerClass, constructorType)
230 .asType(constructorType.changeReturnType(samBase)));
231 }
232 }
233
234 private static File validateDumpDir(String path) {
235 File dirPath = new File(path);
236 String errMsg = null;
237 if (!dirPath.exists()) {
238 errMsg = "Directory at jdk.internal.lambda.dumpProxyClasses does not exist";
239 } else if (!dirPath.isDirectory()) {
240 errMsg = "Path at jdk.internal.lambda.dumpProxyClasses is not a directory";
241 } else if (!dirPath.canWrite()) {
242 errMsg = "Directory at jdk.internal.lambda.dumpProxyClasses is not writable";
243 } else {
244 // validate dump directory, ready to go
245 return dirPath;
246 }
247
248 // show error message about invalid directory once
249 if (! invalidDir.getAndSet(true)) {
250 PlatformLogger.getLogger(InnerClassLambdaMetafactory.class.getName())
251 .warning(errMsg);
252 }
253 return null;
254 }
255
256 private static String encodeForFilename(String className) {
257 final int len = className.length();
258 StringBuilder sb = new StringBuilder(len);
259
260 for (int i = 0; i < len; i++) {
261 char c = className.charAt(i);
262 // control characters
263 if (c <= 31) {
264 sb.append('%');
265 sb.append(HEX[c >> 4 & 0x0F]);
266 sb.append(HEX[c & 0x0F]);
267 } else {
268 int j = 0;
269 for (; j < BAD_CHARS.length; j++) {
270 if (c == BAD_CHARS[j]) {
271 sb.append(REPLACEMENT[j]);
272 break;
273 }
274 }
275 if (j >= BAD_CHARS.length) {
276 sb.append(c);
277 }
278 }
279 }
280
281 return sb.toString();
282 }
283
284 /**
285 * Generate a class file which implements the functional
286 * interface, define and return the class.
287 *
288 * @implNote The class that is generated does not include signature
289 * information for exceptions that may be present on the SAM method.
290 * This is to reduce classfile size, and is harmless as checked exceptions
291 * are erased anyway, no one will ever compile against this classfile,
292 * and we make no guarantees about the reflective properties of lambda
293 * objects.
294 *
295 * @return a Class which implements the functional interface
296 * @throws LambdaConversionException If properly formed functional interface
297 * is not found
298 */
299 private Class<?> spinInnerClass() throws LambdaConversionException {
300 String[] interfaces = new String[markerInterfaces.length + 1];
301 interfaces[0] = samBase.getName().replace('.', '/');
302 for (int i=0; i<markerInterfaces.length; i++) {
303 interfaces[i+1] = markerInterfaces[i].getName().replace('.', '/');
325
326 // Forward the bridges
327 if (additionalBridges != null) {
328 for (MethodType mt : additionalBridges) {
329 methodDescriptor = mt.toMethodDescriptorString();
330 mv = cw.visitMethod(ACC_PUBLIC|ACC_BRIDGE, samMethodName,
331 methodDescriptor, null, null);
332 new ForwardingMethodGenerator(mv).generate(methodDescriptor);
333 }
334 }
335
336 if (isSerializable)
337 generateWriteReplace();
338
339 cw.visitEnd();
340
341 // Define the generated class in this VM.
342
343 final byte[] classBytes = cw.toByteArray();
344
345 // If requested, dump out to a file for debugging purposes
346 if (dumpDir != null) {
347 AccessController.doPrivileged(new PrivilegedAction<Void>() {
348 @Override
349 public Void run() {
350 File dirPath = validateDumpDir(dumpDir);
351 if (dirPath != null) {
352 File out = new File(dirPath, encodeForFilename(lambdaClassName) + ".class");
353 try (FileOutputStream fos = new FileOutputStream(out)) {
354 fos.write(classBytes);
355 } catch (IOException ex) {
356 PlatformLogger.getLogger(InnerClassLambdaMetafactory.class.getName())
357 .warning("Exception writing to path at jdk.internal.lambda.dumpProxyClasses");
358 }
359 }
360 return null;
361 }
362 });
363 }
364
365 ClassLoader loader = targetClass.getClassLoader();
366 ProtectionDomain pd = (loader == null)
367 ? null
368 : AccessController.doPrivileged(
369 new PrivilegedAction<ProtectionDomain>() {
370 @Override
371 public ProtectionDomain run() {
372 return targetClass.getProtectionDomain();
373 }
374 }
375 );
376
377 return UNSAFE.defineClass(lambdaClassName,
378 classBytes, 0, classBytes.length,
379 loader, pd);
380 }
381
382 /**
383 * Generate the constructor for the class
|