Skip to content
This repository was archived by the owner on Aug 27, 2022. It is now read-only.
/ lanai Public archive

Commit 1b79326

Browse files
committedSep 25, 2020
8242451: ensure semantics of non-capturing lambdas are preserved independent of execution mode
Reviewed-by: mchung
1 parent dc1ef58 commit 1b79326

File tree

4 files changed

+165
-57
lines changed

4 files changed

+165
-57
lines changed
 

‎src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java

+63-22
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
private static final String METHOD_DESCRIPTOR_VOID = Type.getMethodDescriptor(Type.VOID_TYPE);
5858
private static final String JAVA_LANG_OBJECT = "java/lang/Object";
5959
private static final String NAME_CTOR = "<init>";
60+
private static final String LAMBDA_INSTANCE_FIELD = "LAMBDA_INSTANCE$";
6061

6162
//Serialization support
6263
private static final String NAME_SERIALIZED_LAMBDA = "java/lang/invoke/SerializedLambda";
@@ -206,32 +207,42 @@ private static String lambdaClassName(Class<?> targetClass) {
206207
@Override
207208
CallSite buildCallSite() throws LambdaConversionException {
208209
final Class<?> innerClass = spinInnerClass();
209-
if (invokedType.parameterCount() == 0 && !disableEagerInitialization) {
210+
if (invokedType.parameterCount() == 0) {
210211
// In the case of a non-capturing lambda, we optimize linkage by pre-computing a single instance,
211212
// unless we've suppressed eager initialization
212-
final Constructor<?>[] ctrs = AccessController.doPrivileged(
213-
new PrivilegedAction<>() {
214-
@Override
215-
public Constructor<?>[] run() {
216-
Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
217-
if (ctrs.length == 1) {
218-
// The lambda implementing inner class constructor is private, set
219-
// it accessible (by us) before creating the constant sole instance
220-
ctrs[0].setAccessible(true);
221-
}
222-
return ctrs;
213+
if (disableEagerInitialization) {
214+
try {
215+
return new ConstantCallSite(caller.findStaticGetter(innerClass, LAMBDA_INSTANCE_FIELD,
216+
invokedType.returnType()));
217+
} catch (ReflectiveOperationException e) {
218+
throw new LambdaConversionException(
219+
"Exception finding " + LAMBDA_INSTANCE_FIELD + " static field", e);
220+
}
221+
} else {
222+
final Constructor<?>[] ctrs = AccessController.doPrivileged(
223+
new PrivilegedAction<>() {
224+
@Override
225+
public Constructor<?>[] run() {
226+
Constructor<?>[] ctrs = innerClass.getDeclaredConstructors();
227+
if (ctrs.length == 1) {
228+
// The lambda implementing inner class constructor is private, set
229+
// it accessible (by us) before creating the constant sole instance
230+
ctrs[0].setAccessible(true);
231+
}
232+
return ctrs;
233+
}
234+
});
235+
if (ctrs.length != 1) {
236+
throw new LambdaConversionException("Expected one lambda constructor for "
237+
+ innerClass.getCanonicalName() + ", got " + ctrs.length);
223238
}
224-
});
225-
if (ctrs.length != 1) {
226-
throw new LambdaConversionException("Expected one lambda constructor for "
227-
+ innerClass.getCanonicalName() + ", got " + ctrs.length);
228-
}
229239

230-
try {
231-
Object inst = ctrs[0].newInstance();
232-
return new ConstantCallSite(MethodHandles.constant(samBase, inst));
233-
} catch (ReflectiveOperationException e) {
234-
throw new LambdaConversionException("Exception instantiating lambda object", e);
240+
try {
241+
Object inst = ctrs[0].newInstance();
242+
return new ConstantCallSite(MethodHandles.constant(samBase, inst));
243+
} catch (ReflectiveOperationException e) {
244+
throw new LambdaConversionException("Exception instantiating lambda object", e);
245+
}
235246
}
236247
} else {
237248
try {
@@ -331,6 +342,10 @@ private Class<?> generateInnerClass() throws LambdaConversionException {
331342

332343
generateConstructor();
333344

345+
if (invokedType.parameterCount() == 0 && disableEagerInitialization) {
346+
generateClassInitializer();
347+
}
348+
334349
// Forward the SAM method
335350
MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, samMethodName,
336351
samMethodType.toMethodDescriptorString(), null, null);
@@ -398,6 +413,32 @@ public Void run() {
398413
}
399414
}
400415

416+
/**
417+
* Generate a static field and a static initializer that sets this field to an instance of the lambda
418+
*/
419+
private void generateClassInitializer() {
420+
String lambdaTypeDescriptor = invokedType.returnType().descriptorString();
421+
422+
// Generate the static final field that holds the lambda singleton
423+
FieldVisitor fv = cw.visitField(ACC_PRIVATE | ACC_STATIC | ACC_FINAL,
424+
LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor, null, null);
425+
fv.visitEnd();
426+
427+
// Instantiate the lambda and store it to the static final field
428+
MethodVisitor clinit = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
429+
clinit.visitCode();
430+
431+
clinit.visitTypeInsn(NEW, lambdaClassName);
432+
clinit.visitInsn(Opcodes.DUP);
433+
assert invokedType.parameterCount() == 0;
434+
clinit.visitMethodInsn(INVOKESPECIAL, lambdaClassName, NAME_CTOR, constructorType.toMethodDescriptorString(), false);
435+
clinit.visitFieldInsn(PUTSTATIC, lambdaClassName, LAMBDA_INSTANCE_FIELD, lambdaTypeDescriptor);
436+
437+
clinit.visitInsn(RETURN);
438+
clinit.visitMaxs(-1, -1);
439+
clinit.visitEnd();
440+
}
441+
401442
/**
402443
* Generate the constructor for the class
403444
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @bug 8242451
27+
* @library /test/lib
28+
* @summary Test that the LAMBDA_INSTANCE$ field is present depending
29+
* on disableEagerInitialization
30+
* @run main LambdaEagerInitTest
31+
* @run main/othervm -Djdk.internal.lambda.disableEagerInitialization=true LambdaEagerInitTest
32+
*/
33+
34+
import java.lang.reflect.Field;
35+
import java.lang.reflect.Modifier;
36+
import java.util.Arrays;
37+
import java.util.HashSet;
38+
import java.util.Set;
39+
import java.util.stream.Collectors;
40+
41+
import static jdk.test.lib.Asserts.*;
42+
43+
public class LambdaEagerInitTest {
44+
45+
interface H {Object m(String s);}
46+
47+
private static Set<String> allowedStaticFields(boolean nonCapturing) {
48+
Set<String> s = new HashSet<>();
49+
if (Boolean.getBoolean("jdk.internal.lambda.disableEagerInitialization")) {
50+
if (nonCapturing) s.add("LAMBDA_INSTANCE$");
51+
}
52+
return s;
53+
}
54+
55+
private void nonCapturingLambda() {
56+
H la = s -> s;
57+
assertEquals("hi", la.m("hi"));
58+
Class<? extends H> c1 = la.getClass();
59+
verifyLambdaClass(la.getClass(), true);
60+
}
61+
62+
private void capturingLambda() {
63+
H la = s -> concat(s, "foo");
64+
assertEquals("hi foo", la.m("hi"));
65+
verifyLambdaClass(la.getClass(), false);
66+
}
67+
68+
private void verifyLambdaClass(Class<?> c, boolean nonCapturing) {
69+
Set<String> staticFields = new HashSet<>();
70+
Set<String> instanceFields = new HashSet<>();
71+
for (Field f : c.getDeclaredFields()) {
72+
if (Modifier.isStatic(f.getModifiers())) {
73+
staticFields.add(f.getName());
74+
} else {
75+
instanceFields.add(f.getName());
76+
}
77+
}
78+
assertEquals(instanceFields.size(), nonCapturing ? 0 : 1, "Unexpected instance fields");
79+
assertEquals(staticFields, allowedStaticFields(nonCapturing), "Unexpected static fields");
80+
}
81+
82+
private String concat(String... ss) {
83+
return Arrays.stream(ss).collect(Collectors.joining(" "));
84+
}
85+
86+
public static void main(String[] args) {
87+
LambdaEagerInitTest test = new LambdaEagerInitTest();
88+
test.nonCapturingLambda();
89+
test.capturingLambda();
90+
}
91+
}

‎test/langtools/tools/javac/lambda/lambdaExpression/LambdaTest6.java

+1-3
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@
2626
* @bug 8003280
2727
* @summary Add lambda tests
2828
* Test bridge methods for certain SAM conversions
29-
* Tests that jdk.internal.lambda.disableEagerInitialization=true creates a
30-
* get$Lambda method for non-capturing lambdas
29+
* Test the set of generated methods
3130
* @compile LambdaTest6.java
3231
* @run main LambdaTest6
33-
* @run main/othervm -Djdk.internal.lambda.disableEagerInitialization=true LambdaTest6
3432
*/
3533

3634
import java.lang.reflect.Method;

‎test/langtools/tools/javac/lambda/methodReference/BridgeMethod.java

+10-32
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,11 @@
2626
* @bug 8003280
2727
* @summary Add lambda tests
2828
* Test bridge methods in certain SAM conversion
29-
* Tests that jdk.internal.lambda.disableEagerInitialization=true creates a
30-
* get$Lambda method for non-capturing lambdas
3129
* @compile BridgeMethod.java
3230
* @run main BridgeMethod
33-
* @run main/othervm -Djdk.internal.lambda.disableEagerInitialization=true BridgeMethod
3431
*/
3532

3633
import java.lang.reflect.Method;
37-
import java.util.Arrays;
3834
import java.util.HashSet;
3935
import java.util.Set;
4036

@@ -72,52 +68,34 @@ private static Set<String> setOfStringObject() {
7268
return s;
7369
}
7470

75-
private static Set<String> allowedMethods() {
76-
Set<String> s = new HashSet<>();
77-
s.add("m");
78-
return s;
79-
}
80-
81-
private static boolean matchingMethodNames(Method[] methods) {
82-
Set<String> methodNames = new HashSet<>();
83-
for (Method m : methods) {
84-
methodNames.add(m.getName());
85-
}
86-
return methodNames.equals(allowedMethods());
87-
}
88-
8971
public static void main(String[] args) {
9072
L la = BridgeMethod::bar; //static reference
9173
la.m("hi");
9274
Class<? extends L> c1 = la.getClass();
9375
Method[] methods = c1.getDeclaredMethods();
94-
assertTrue(matchingMethodNames(methods));
9576
Set<String> types = setOfStringObject();
9677
System.out.println("methods in SAM conversion of L:");
9778
for(Method m : methods) {
98-
if (m.getName().equals("m")) {
99-
System.out.println(m.toGenericString());
100-
Class[] parameterTypes = m.getParameterTypes();
101-
assertTrue(parameterTypes.length == 1);
102-
assertTrue(types.remove(parameterTypes[0].getName()));
103-
}
79+
assertTrue(m.getName().equals("m"));
80+
System.out.println(m.toGenericString());
81+
Class[] parameterTypes = m.getParameterTypes();
82+
assertTrue(parameterTypes.length == 1);
83+
assertTrue(types.remove(parameterTypes[0].getName()));
10484
}
10585
assertTrue(types.isEmpty() || (types.size() == 1 && types.contains("java.lang.String")));
10686

10787
KM km = BridgeMethod::bar;
10888
//km.m("hi"); //will be uncommented when CR7028808 fixed
10989
Class<? extends KM> c2 = km.getClass();
11090
methods = c2.getDeclaredMethods();
111-
assertTrue(matchingMethodNames(methods));
11291
types = setOfStringObject();
11392
System.out.println("methods in SAM conversion of KM:");
11493
for(Method m : methods) {
115-
if (m.getName().equals("m")) {
116-
System.out.println(m.toGenericString());
117-
Class<?>[] parameterTypes = m.getParameterTypes();
118-
assertTrue(parameterTypes.length == 1);
119-
assertTrue(types.remove(parameterTypes[0].getName()));
120-
}
94+
assertTrue(m.getName().equals("m"));
95+
System.out.println(m.toGenericString());
96+
Class<?>[] parameterTypes = m.getParameterTypes();
97+
assertTrue(parameterTypes.length == 1);
98+
assertTrue(types.remove(parameterTypes[0].getName()));
12199
}
122100
assertTrue(types.isEmpty());
123101

0 commit comments

Comments
 (0)
This repository has been archived.