diff --git a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java index 1ce3199d1f6..593c66fab70 100644 --- a/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java +++ b/src/java.base/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java @@ -32,14 +32,11 @@ import sun.invoke.util.VerifyAccess; import sun.security.action.GetBooleanAction; -import java.nio.charset.StandardCharsets; import java.io.Serializable; import java.lang.constant.ConstantDescs; import java.lang.reflect.Modifier; import java.util.LinkedHashSet; import java.util.Set; -import java.util.StringJoiner; -import java.util.zip.CRC32; import static java.lang.invoke.MethodHandleStatics.CLASSFILE_VERSION; import static java.lang.invoke.MethodHandles.Lookup.ClassOption.NESTMATE; @@ -87,11 +84,6 @@ private static final boolean disableEagerInitialization; - private static final boolean generateStableLambdaNames; - - private static final int mask1 = 0b10101010; - private static final int mask2 = 0b01010101; - // condy to load implMethod from class data private static final ConstantDynamic implMethodCondy; @@ -105,9 +97,6 @@ final String disableEagerInitializationKey = "jdk.internal.lambda.disableEagerInitialization"; disableEagerInitialization = GetBooleanAction.privilegedGetProperty(disableEagerInitializationKey); - final String generateStableLambdaNamesKey = "jdk.internal.lambda.generateStableLambdaNames"; - generateStableLambdaNames = GetBooleanAction.privilegedGetProperty(generateStableLambdaNamesKey); - // condy to load implMethod from class data MethodType classDataMType = methodType(Object.class, MethodHandles.Lookup.class, String.class, Class.class); Handle classDataBsm = new Handle(H_INVOKESTATIC, Type.getInternalName(MethodHandles.class), "classData", @@ -183,7 +172,7 @@ public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, implMethodName = implInfo.getName(); implMethodDesc = implInfo.getMethodType().toMethodDescriptorString(); constructorType = factoryType.changeReturnType(Void.TYPE); - lambdaClassName = generateStableLambdaNames ? stableLambdaClassName(targetClass) : lambdaClassName(targetClass); + lambdaClassName = lambdaClassName(targetClass); // If the target class invokes a protected method inherited from a // superclass in a different package, or does 'invokespecial', the // lambda class has no access to the resolved method. Instead, we need @@ -208,10 +197,6 @@ public InnerClassLambdaMetafactory(MethodHandles.Lookup caller, } private static String lambdaClassName(Class targetClass) { - return createNameFromTargetClass(targetClass); - } - - private static String createNameFromTargetClass(Class targetClass) { String name = targetClass.getName(); if (targetClass.isHidden()) { // use the original class name @@ -220,78 +205,6 @@ private static String createNameFromTargetClass(Class targetClass) { return name.replace('.', '/') + "$$Lambda"; } - /** - * Create a stable name for the lambda class. - * When CDS archiving is enabled, lambda classes - * are stored in the archive using some parameters from - * the InnerClassLambdaMetafactory. To distinguish between - * two lambdas, even when CDS archiving is disabled, - * use a superset of those parameters to create a stable name. - * - * Concatenate all the parameters chosen for the stable name, - * and hash them into 64-bit hash value. - * Any additional changes to this method will result in unstable - * hash values across different versions. Thus, every change - * to this method should be regarded as a backward incompatible change. - * - * No matter what hash function we use, there is a possibility of - * collisions in names. We expect a relatively low number of lambdas - * per class. Thus, we don't expect to have collisions using the described - * hash function. Every tool that uses this feature should handle potential - * collisions on its own. There is no guarantee that names will be unique, - * only that they will be stable (identical in every run). - * - * @return a stable name for the created lambda class. - */ - private String stableLambdaClassName(Class targetClass) { - String name = createNameFromTargetClass(targetClass); - - StringBuilder hashData1 = new StringBuilder(), hashData2 = new StringBuilder(); - appendData(hashData1, hashData2, interfaceMethodName); - appendData(hashData1, hashData2, getQualifiedSignature(factoryType)); - appendData(hashData1, hashData2, getQualifiedSignature(interfaceMethodType)); - appendData(hashData1, hashData2, implementation.internalMemberName().toString()); - appendData(hashData1, hashData2, getQualifiedSignature(dynamicMethodType)); - - for (Class clazz : altInterfaces) { - appendData(hashData1, hashData2, clazz.getName()); - } - - for (MethodType method : altMethods) { - appendData(hashData1, hashData2, getQualifiedSignature(method)); - } - - return name + hashToHexString(hashData1.toString(), hashData2.toString()); - } - - private void appendData(StringBuilder hashData1, StringBuilder hashData2, String data) { - for (int i = 0; i < data.length(); i++) { - hashData1.append((char)(data.charAt(i) & mask1)); - hashData2.append((char)(data.charAt(i) & mask2)); - } - } - - private long hashStringToLong(String hashData) { - CRC32 crc32 = new CRC32(); - crc32.update(hashData.getBytes(StandardCharsets.UTF_8)); - return crc32.getValue(); - } - - private String hashToHexString(String hashData1, String hashData2) { - long hashValueData1 = hashStringToLong(hashData1); - long hashValueData2 = hashStringToLong(hashData2); - return Long.toHexString(hashValueData1 | (hashValueData2 << 32)); - } - - private String getQualifiedSignature(MethodType type) { - StringJoiner sj = new StringJoiner(",", "(", ")" + type.returnType().getName()); - Class[] ptypes = type.ptypes(); - for (int i = 0; i < ptypes.length; i++) { - sj.add(ptypes[i].getName()); - } - return sj.toString(); - } - /** * Build the CallSite. Generate a class file which implements the functional * interface, define the class, if there are no parameters create an instance diff --git a/test/jdk/java/lang/invoke/lambda/TestStableLambdaNames.java b/test/jdk/java/lang/invoke/lambda/TestStableLambdaNames.java deleted file mode 100644 index 26c8e665d15..00000000000 --- a/test/jdk/java/lang/invoke/lambda/TestStableLambdaNames.java +++ /dev/null @@ -1,297 +0,0 @@ -/* -* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -* -* This code is free software; you can redistribute it and/or modify it -* under the terms of the GNU General Public License version 2 only, as -* published by the Free Software Foundation. -* -* This code is distributed in the hope that it will be useful, but WITHOUT -* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -* version 2 for more details (a copy is included in the LICENSE file that -* accompanied this code). -* -* You should have received a copy of the GNU General Public License version -* 2 along with this work; if not, write to the Free Software Foundation, -* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -* -* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -* or visit www.oracle.com if you need additional information or have any -* questions. -*/ - -/** - * @test - * @summary Test if the names of the lambda classes are stable when {@code -Djdk.internal.lambda.generateStableLambdaNames} - * flag is set to true. This test directly calls java.lang.invoke.LambdaMetafactory#altMetafactory - * method to create multilple lambda instances and then checks their names stability. We created a - * multidimensional space of possible values for each parameter that - * {@link java.lang.invoke.LambdaMetafactory#altMetafactory} takes and then search that space by combining - * different values of those parameters. There is a rule we have to follow: - * Alternative methods of the specific method must have the same signature with difference in parameter types - * as long as the parameter of the alternative method is the superclass type of the type of corresponding parameter in - * original method - * @run main/othervm -Djdk.internal.lambda.generateStableLambdaNames=true TestStableLambdaNames - */ - -import java.lang.invoke.LambdaMetafactory; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; -import java.rmi.Remote; -import java.util.HashSet; -import java.util.Set; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.BooleanSupplier; -import java.util.function.Supplier; - -public class TestStableLambdaNames { - private static final MethodHandles.Lookup lookup = MethodHandles.lookup(); - - /** - * Different types of lambda classes based on value of flags parameter in the - * {@link java.lang.invoke.LambdaMetafactory#altMetafactory}. - * {@link java.lang.invoke.LambdaMetafactory#altMetafactory} uses bitwise and with this - * parameter and predefined values to determine if lambda is serializable, has - * altMethods and altInterfaces etc. - */ - private enum lambdaType { - NOT_SERIALIZABLE_NO_ALT_METHODS_NO_ALT_INTERFACES (0), - SERIALIZABLE_ONLY (1), - NOT_SERIALIZABLE_HAS_ALT_INTERFACES(2), - SERIALIZABLE_HAS_ALT_INTERFACES(3), - NOT_SERIALIZABLE_HAS_ALT_METHODS(4), - SERIALIZABLE_HAS_ALT_METHODS(5), - NOT_SERIALIZABLE_HAS_ALT_METHODS_HAS_ALT_INTERFACES(6), - SERIALIZABLE_HAS_ALT_METHODS_HAS_ALT_INTERFACES(7); - - private final int index; - lambdaType(int i) { - index = i; - } - } - - private static final String[] interfaceMethods = {"accept", "consume", "apply", "supply", "get", "test", "getAsBoolean"}; - private static final Class[] interfaces = {Consumer.class, Function.class, Predicate.class, Supplier.class, BooleanSupplier.class}; - /** List of method types for defined methods */ - private static final MethodType[] methodTypes = {MethodType.methodType(String.class, Integer.class), MethodType.methodType(Throwable.class, AssertionError.class)}; - private static final Class[] altInterfaces = {Cloneable.class, Remote.class}; - /** Alternative methods that corresponds to method1 */ - private static final MethodType[] altMethodsMethod1 = {MethodType.methodType(String.class, Number.class)}; - /** Alternative methods that corresponds to method2 */ - private static final MethodType[] altMethodsMethod2 = {MethodType.methodType(Throwable.class, Error.class), MethodType.methodType(Throwable.class, Throwable.class)}; - - private static String method1(Number number) { - return String.valueOf(number); - } - - private static String method1(Integer number) { return String.valueOf(number); } - - private static Throwable method2(AssertionError error) { - return error; - } - - private static Throwable method2(Error error) { - return error; - } - - private static Throwable method2(Throwable throwable) { - return throwable; - } - - private static String removeHashFromLambdaName(String name) { - return name.substring(0, name.indexOf("/0x0")); - } - - private static void createPlainLambdas(Set lambdaNames, int flags, MethodHandle[] methodHandles) throws Throwable { - for (String interfaceMethod : interfaceMethods) { - for (Class interfaceClass : interfaces) { - for (int i = 0; i < methodTypes.length; i++) { - Object lambda = LambdaMetafactory.altMetafactory(lookup, interfaceMethod, MethodType.methodType(interfaceClass), - methodTypes[i], methodHandles[i], methodTypes[i], flags).getTarget().invoke(); - lambdaNames.add(removeHashFromLambdaName(lambda.getClass().getName())); - } - } - } - } - - private static Object lambdaWithOneAltInterface(String interfaceMethod, Class interfaceClass, MethodType methodType, MethodHandle methodHandle, int flags, Class altInterface) throws Throwable { - int numOfAltInterfaces = 1; - return LambdaMetafactory.altMetafactory(lookup, interfaceMethod, MethodType.methodType(interfaceClass), - methodType, methodHandle, methodType, flags, numOfAltInterfaces, altInterface).getTarget().invoke(); - } - - private static Object lambdaWithMultipleAltInterfaces(String interfaceMethod, Class interfaceClass, MethodType methodType, MethodHandle methodHandle, int flags) throws Throwable { - int numOfAltInterfaces = 2; - int altInterfacesIndex = 0; - return LambdaMetafactory.altMetafactory(lookup, interfaceMethod, MethodType.methodType(interfaceClass), - methodType, methodHandle, methodType, flags, numOfAltInterfaces, altInterfaces[altInterfacesIndex++], altInterfaces[altInterfacesIndex]).getTarget().invoke(); - } - - private static void createLambdasWithAltInterfaces(Set lambdaNames, int flags, MethodHandle[] methodHandles) throws Throwable { - Object lambda; - for (String interfaceMethod : interfaceMethods) { - for (Class interfaceClass : interfaces) { - for (int i = 0; i < methodTypes.length; i++) { - for (Class altInterface : altInterfaces) { - lambda = lambdaWithOneAltInterface(interfaceMethod, interfaceClass, methodTypes[i], methodHandles[i], flags, altInterface); - lambdaNames.add(removeHashFromLambdaName(lambda.getClass().getName())); - } - - lambda = lambdaWithMultipleAltInterfaces(interfaceMethod, interfaceClass, methodTypes[i], methodHandles[i], flags); - lambdaNames.add(removeHashFromLambdaName(lambda.getClass().getName())); - } - } - } - } - - private static Object lambdaWithOneAltMethod(String interfaceMethod, Class interfaceClass, MethodType methodType, MethodHandle methodHandle, - int flags, MethodType altMethod, MethodHandle[] methodHandles) throws Throwable { - int numOfAltMethods = 1; - return LambdaMetafactory.altMetafactory(lookup, interfaceMethod, MethodType.methodType(interfaceClass), - methodType, methodHandle, methodType, flags, numOfAltMethods, altMethod).getTarget().invoke(); - } - - private static Object lambdaWithMultipleAltMethods(String interfaceMethod, Class interfaceClass, int flags, MethodHandle[] methodHandles) throws Throwable { - int numOfAltMethods = 2; - int indexOfAltMethod = 0; - MethodType methodTypeMethod2 = methodTypes[1]; - MethodHandle methodHandleMethod2 = methodHandles[1]; - return LambdaMetafactory.altMetafactory(lookup, interfaceMethod, MethodType.methodType(interfaceClass), - methodTypeMethod2, methodHandleMethod2, methodTypeMethod2, flags, numOfAltMethods, altMethodsMethod2[indexOfAltMethod++], - altMethodsMethod2[indexOfAltMethod]).getTarget().invoke(); - } - - private static void createLambdasWithAltMethods(Set lambdaNames, int flags, MethodHandle[] methodHandles) throws Throwable { - int indexOfMethodWithOneAltMethod = 0; - int indexOfMethodWithTwoAltMethods = 1; - int altMethodIndex = 0; - Object lambda; - for (String interfaceMethod : interfaceMethods) { - for (Class interfaceClass : interfaces) { - lambda = lambdaWithOneAltMethod(interfaceMethod, interfaceClass, methodTypes[indexOfMethodWithOneAltMethod], methodHandles[indexOfMethodWithOneAltMethod], - flags, altMethodsMethod1[altMethodIndex], methodHandles); - lambdaNames.add(removeHashFromLambdaName(lambda.getClass().getName())); - - for (MethodType altMethod : altMethodsMethod2) { - lambda = lambdaWithOneAltMethod(interfaceMethod, interfaceClass, methodTypes[indexOfMethodWithTwoAltMethods], methodHandles[indexOfMethodWithTwoAltMethods], - flags, altMethod, methodHandles); - lambdaNames.add(removeHashFromLambdaName(lambda.getClass().getName())); - } - - lambda = lambdaWithMultipleAltMethods(interfaceMethod, interfaceClass, flags, methodHandles); - } - } - } - - private static Object lambdaWithOneAltInterfaceAndOneAltMethod(String interfaceMethod, Class interfaceClass, MethodType methodType, MethodHandle methodHandle, int flags, - Class altInterface, MethodType altMethod) throws Throwable { - int numOfAltInterfaces = 1; - int numOfAltMethods = 1; - return LambdaMetafactory.altMetafactory(lookup, interfaceMethod, MethodType.methodType(interfaceClass), - methodType, methodHandle, methodType, flags, numOfAltInterfaces, altInterface, numOfAltMethods, altMethod).getTarget().invoke(); - } - - private static Object lambdaWithOneAltInterfaceAndMultipleAltMethods(String interfaceMethod, Class interfaceClass, int flags, Class altInterface, - MethodHandle[] methodHandles) throws Throwable { - int numOfAltInterfaces = 1; - int numOfAltMethods = 2; - int indexOfAltMethod = 0; - MethodType methodTypeMethod2 = methodTypes[1]; - MethodHandle methodHandleMethod2 = methodHandles[1]; - - return LambdaMetafactory.altMetafactory(lookup, interfaceMethod, MethodType.methodType(interfaceClass), - methodTypeMethod2, methodHandleMethod2, methodTypeMethod2, flags, numOfAltInterfaces, altInterface, numOfAltMethods, altMethodsMethod2[indexOfAltMethod++], - altMethodsMethod2[indexOfAltMethod]).getTarget().invoke(); - } - - private static Object lambdaWithMultipleAltInterfaceAndMultipleAltMethods(String interfaceMethod, Class interfaceClass, int flags, MethodHandle[] methodHandles) throws Throwable { - int numOfAltInterfaces = 2; - int numOfAltMethods = 2; - int indexOfAltInterface = 0; - int indexOfAltMethod = 0; - MethodType methodTypeMethod2 = methodTypes[1]; - MethodHandle methodHandleMethod2 = methodHandles[1]; - - return LambdaMetafactory.altMetafactory(lookup, interfaceMethod, MethodType.methodType(interfaceClass), methodTypeMethod2, methodHandleMethod2, methodTypeMethod2, - flags, numOfAltInterfaces, altInterfaces[indexOfAltInterface++], altInterfaces[indexOfAltInterface], numOfAltMethods, altMethodsMethod2[indexOfAltMethod++], - altMethodsMethod2[indexOfAltMethod]).getTarget().invoke(); - } - private static void createLambdasWithAltInterfacesAndAltMethods(Set lambdaNames, int flags, MethodHandle[] methodHandles) throws Throwable { - int indexOfMethodWithOneAltMethod = 0; - int indexOfMethodWithTwoAltMethods = 1; - int altMethodIndex = 0; - Object lambda; - - for (String interfaceMethod : interfaceMethods) { - for (Class interfaceClass : interfaces) { - for (Class altInterface : altInterfaces) { - lambda = lambdaWithOneAltInterfaceAndOneAltMethod(interfaceMethod, interfaceClass, methodTypes[indexOfMethodWithOneAltMethod], methodHandles[indexOfMethodWithOneAltMethod], flags, - altInterface, altMethodsMethod1[altMethodIndex]); - lambdaNames.add(removeHashFromLambdaName(lambda.getClass().getName())); - lambda = lambdaWithOneAltInterfaceAndMultipleAltMethods(interfaceMethod, interfaceClass, flags, altInterface, methodHandles); - lambdaNames.add(removeHashFromLambdaName(lambda.getClass().getName())); - - for (MethodType altMethod : altMethodsMethod2) { - lambda = lambdaWithOneAltInterfaceAndOneAltMethod(interfaceMethod, interfaceClass, methodTypes[indexOfMethodWithTwoAltMethods], methodHandles[indexOfMethodWithTwoAltMethods], flags, - altInterface, altMethod); - lambdaNames.add(removeHashFromLambdaName(lambda.getClass().getName())); - } - } - lambda = lambdaWithMultipleAltInterfaceAndMultipleAltMethods(interfaceMethod, interfaceClass, flags, methodHandles); - lambdaNames.add(removeHashFromLambdaName(lambda.getClass().getName())); - } - } - } - - private static void createLambdasWithDifferentParameters(Set lambdaNames, MethodHandle[] methodHandles) throws Throwable { - // All lambdas with flags 0 - createPlainLambdas(lambdaNames, lambdaType.NOT_SERIALIZABLE_NO_ALT_METHODS_NO_ALT_INTERFACES.index, methodHandles); - - // All lambdas with flags 1 - createPlainLambdas(lambdaNames, lambdaType.SERIALIZABLE_ONLY.index, methodHandles); - - // All lambdas with flags 2 - createLambdasWithAltInterfaces(lambdaNames, lambdaType.NOT_SERIALIZABLE_HAS_ALT_INTERFACES.index, methodHandles); - - // All lambdas with flags 3 - createLambdasWithAltInterfaces(lambdaNames, lambdaType.SERIALIZABLE_HAS_ALT_INTERFACES.index, methodHandles); - - // All lambdas with flags 4 - createLambdasWithAltMethods(lambdaNames, lambdaType.NOT_SERIALIZABLE_HAS_ALT_METHODS.index, methodHandles); - - // All lambdas with flags 5 - createLambdasWithAltMethods(lambdaNames, lambdaType.SERIALIZABLE_HAS_ALT_METHODS.index, methodHandles); - - // All lambdas with flags 6 - createLambdasWithAltInterfacesAndAltMethods(lambdaNames, lambdaType.NOT_SERIALIZABLE_HAS_ALT_METHODS_HAS_ALT_INTERFACES.index, methodHandles); - - // All lambdas with flags 7 - createLambdasWithAltInterfacesAndAltMethods(lambdaNames, lambdaType.SERIALIZABLE_HAS_ALT_METHODS_HAS_ALT_INTERFACES.index, methodHandles); - } - - public static void main(String[] args) throws Throwable { - MethodType methodTypeForMethod1 = methodTypes[0]; - MethodType methodTypeForMethod2 = methodTypes[1]; - MethodHandle[] methodHandles = {lookup.findStatic(TestStableLambdaNames.class, "method1", methodTypeForMethod1), - lookup.findStatic(TestStableLambdaNames.class, "method2", methodTypeForMethod2)}; - - Set lambdaClassStableNames = new HashSet<>(); - createLambdasWithDifferentParameters(lambdaClassStableNames, methodHandles); - - Set lambdaClassStableNamesTest = new HashSet<>(); - createLambdasWithDifferentParameters(lambdaClassStableNamesTest, methodHandles); - - if (lambdaClassStableNames.size() != lambdaClassStableNamesTest.size()) { - throw new RuntimeException(lambdaClassStableNames.size() + " names was created during name creation run, but " + lambdaClassStableNamesTest.size() + " names were created during test run. " + - "Number of created names must be the same."); - } - - if (!lambdaClassStableNamesTest.containsAll(lambdaClassStableNames)) { - throw new RuntimeException("Different names for lambda classes were created during name creation run and test run. All the created names in both runs must be the same."); - } - } -} \ No newline at end of file