Skip to content

Commit

Permalink
Load lambda types in the application layer
Browse files Browse the repository at this point in the history
  • Loading branch information
Zeavee committed Oct 11, 2024
1 parent a5eb4a5 commit c5e63c8
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ public class ImageLayerSnapshotUtil {
public static final String INTERFACES_TAG = "interfaces";
public static final String WRAPPED_TYPE_TAG = "wrapped type";
public static final String GENERATED_SERIALIZATION_TAG = "generated serialization";
public static final String LAMBDA_TYPE_TAG = "lambda type";
public static final String HOLDER_CLASS_TAG = "holder class";
public static final String RAW_DECLARING_CLASS_TAG = "raw declaring class";
public static final String RAW_TARGET_CONSTRUCTOR_CLASS_TAG = "raw target constructor class";
public static final String CONSTANTS_TAG = "constants";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public record BootstrapMethodRecord(int bci, int cpi, ResolvedJavaMethod method)
private final ConcurrentMap<BootstrapMethodRecord, BootstrapMethodInfo> bootstrapMethodInfoCache = new ConcurrentHashMap<>();
private final Set<Executable> indyBuildTimeAllowList;
private final Set<Executable> condyBuildTimeAllowList;
private final Method metafactory;
private final Method altMetafactory;

public static BootstrapMethodConfiguration singleton() {
return ImageSingletons.lookup(BootstrapMethodConfiguration.class);
Expand All @@ -79,10 +81,10 @@ public BootstrapMethodConfiguration() {
* Bootstrap method used for Lambdas. Executing this method at run time implies defining
* hidden class at run time, which is unsupported.
*/
Method metafactory = ReflectionUtil.lookupMethod(LambdaMetafactory.class, "metafactory", MethodHandles.Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class,
metafactory = ReflectionUtil.lookupMethod(LambdaMetafactory.class, "metafactory", MethodHandles.Lookup.class, String.class, MethodType.class, MethodType.class, MethodHandle.class,
MethodType.class);
/* Alternate version of LambdaMetafactory.metafactory. */
Method altMetafactory = ReflectionUtil.lookupMethod(LambdaMetafactory.class, "altMetafactory", MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class);
altMetafactory = ReflectionUtil.lookupMethod(LambdaMetafactory.class, "altMetafactory", MethodHandles.Lookup.class, String.class, MethodType.class, Object[].class);

/*
* Bootstrap method used to optimize String concatenation. Executing it at run time
Expand Down Expand Up @@ -143,6 +145,10 @@ public boolean isIndyAllowedAtBuildTime(Executable method) {
return method != null && indyBuildTimeAllowList.contains(method);
}

public boolean isMetafactory(Executable method) {
return method != null && (method.equals(metafactory) || method.equals(altMetafactory));
}

/**
* Check if the provided method is allowed to be executed at build time.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.util.AnalysisError;
import com.oracle.graal.pointsto.util.AnalysisFuture;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.TypeResult;
import com.oracle.svm.core.classinitialization.ClassInitializationInfo;
Expand Down Expand Up @@ -235,10 +236,15 @@ protected void prepareConstantRelinking(EconomicMap<String, Object> constantData
@Override
protected boolean delegateProcessing(String constantType, Object constantValue, List<Object> constantData, Object[] values, int i) {
if (constantType.equals(METHOD_POINTER_TAG)) {
AnalysisType methodPointerType = metaAccess.lookupJavaType(MethodPointer.class);
int mid = (int) constantValue;
AnalysisMethod method = getAnalysisMethod(mid);
values[i] = new RelocatableConstant(new MethodPointer(method), methodPointerType);
AnalysisFuture<JavaConstant> task = new AnalysisFuture<>(() -> {
AnalysisType methodPointerType = metaAccess.lookupJavaType(MethodPointer.class);
int mid = (int) constantValue;
AnalysisMethod method = getAnalysisMethod(mid);
RelocatableConstant constant = new RelocatableConstant(new MethodPointer(method), methodPointerType);
values[i] = constant;
return constant;
});
values[i] = task;
return true;
} else if (constantType.equals(C_ENTRY_POINT_LITERAL_CODE_POINTER)) {
AnalysisType cEntryPointerLiteralPointerType = metaAccess.lookupJavaType(CEntryPointLiteralCodePointer.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import static com.oracle.graal.pointsto.heap.ImageLayerLoader.get;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FACTORY_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.GENERATED_SERIALIZATION_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.HOLDER_CLASS_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.LAMBDA_TYPE_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RAW_DECLARING_CLASS_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RAW_TARGET_CONSTRUCTOR_CLASS_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TARGET_CONSTRUCTOR_TAG;
Expand All @@ -35,19 +37,42 @@
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_TYPE_TAG;

import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;

import org.graalvm.collections.EconomicMap;
import org.graalvm.nativeimage.AnnotationAccess;

import com.oracle.graal.pointsto.heap.ImageLayerLoader;
import com.oracle.graal.pointsto.heap.ImageLayerLoaderHelper;
import com.oracle.graal.pointsto.infrastructure.OriginalMethodProvider;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
import com.oracle.graal.pointsto.meta.AnalysisType;
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.bootstrap.BootstrapMethodConfiguration;
import com.oracle.svm.core.reflect.serialize.SerializationSupport;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.code.FactoryMethodSupport;
import com.oracle.svm.hosted.reflect.serialize.SerializationFeature;
import com.oracle.svm.hosted.substitute.SubstitutionMethod;
import com.oracle.svm.util.ReflectionUtil;

import jdk.graal.compiler.serviceprovider.JavaVersionUtil;
import jdk.internal.reflect.ReflectionFactory;
import jdk.vm.ci.meta.ConstantPool;
import jdk.vm.ci.meta.JavaConstant;

public class SVMImageLayerLoaderHelper extends ImageLayerLoaderHelper {
private static final Class<?> DIRECT_METHOD_HANDLE_STATIC_ACCESSOR_CLASS = ReflectionUtil.lookupClass(false, "java.lang.invoke.DirectMethodHandle$StaticAccessor");
private static final Class<?> DIRECT_METHOD_HANDLE_CONSTRUCTOR_CLASS = ReflectionUtil.lookupClass(false, "java.lang.invoke.DirectMethodHandle$Constructor");
private static final String STATIC_BASE_FIELD_NAME = "staticBase";
private static final String INSTANCE_CLASS_FIELD_NAME = "instanceClass";
private static final int INVOKE_DYNAMIC_OPCODE = 186;

public SVMImageLayerLoaderHelper(ImageLayerLoader imageLayerLoader) {
super(imageLayerLoader);
}
Expand All @@ -70,11 +95,77 @@ protected boolean loadType(EconomicMap<String, Object> typeData, int tid) {
Class<?> constructorAccessor = serializationSupport.getSerializationConstructorAccessor(rawDeclaringClass, rawTargetConstructorClass).getClass();
imageLayerLoader.getMetaAccess().lookupJavaType(constructorAccessor);
return true;
} else if (wrappedType.equals(LAMBDA_TYPE_TAG)) {
String holderClassName = get(typeData, HOLDER_CLASS_TAG);
Class<?> holderClass = imageLayerLoader.lookupClass(false, holderClassName);
loadLambdaTypes(holderClass);
return true;
}

return super.loadType(typeData, tid);
}

/**
* The constant pool index of bootstrap method is not stable in different JVM instances, so the
* only solution is to load all lambda types of the given holder class.
*/
private void loadLambdaTypes(Class<?> holderClass) {
AnalysisUniverse universe = imageLayerLoader.getUniverse();
AnalysisType type = universe.getBigbang().getMetaAccess().lookupJavaType(holderClass);
boolean isSubstitution = AnnotationAccess.isAnnotationPresent(holderClass, TargetClass.class);
ConstantPool constantPool = getConstantPool(type, isSubstitution);
int index = JavaVersionUtil.JAVA_SPEC > 21 ? 0 : -1;
ConstantPool.BootstrapMethodInvocation bootstrap;
while ((bootstrap = getBootstrap(constantPool, index)) != null) {
if (BootstrapMethodConfiguration.singleton().isMetafactory(OriginalMethodProvider.getJavaMethod(bootstrap.getMethod()))) {
constantPool.loadReferencedType(index, INVOKE_DYNAMIC_OPCODE);
JavaConstant test = constantPool.lookupAppendix(index, INVOKE_DYNAMIC_OPCODE);
Object appendix = universe.getSnippetReflection().asObject(Object.class, test);

Class<?> potentialLambdaClass;
if (DIRECT_METHOD_HANDLE_STATIC_ACCESSOR_CLASS.isInstance(appendix)) {
potentialLambdaClass = ReflectionUtil.readField(DIRECT_METHOD_HANDLE_STATIC_ACCESSOR_CLASS, STATIC_BASE_FIELD_NAME, appendix);
} else if (DIRECT_METHOD_HANDLE_CONSTRUCTOR_CLASS.isInstance(appendix)) {
potentialLambdaClass = ReflectionUtil.readField(DIRECT_METHOD_HANDLE_CONSTRUCTOR_CLASS, INSTANCE_CLASS_FIELD_NAME, appendix);
} else {
throw VMError.shouldNotReachHere("Unexpected appendix %s", appendix);
}
universe.getBigbang().getMetaAccess().lookupJavaType(potentialLambdaClass);
}
if (JavaVersionUtil.JAVA_SPEC > 21) {
index++;
} else {
index--;
}
}
}

/**
* A default and substitution class have two different constant pools. The constant pool can
* only be fetched through the methods of the class, so we iterate over the methods and the
* constructors and take the first constant pool that matches the current class.
*/
private static ConstantPool getConstantPool(AnalysisType type, boolean isSubstitution) {
Stream<AnalysisMethod> candidates = Stream.concat(Arrays.stream(type.getDeclaredMethods(false)), Arrays.stream(type.getDeclaredConstructors(false)));
Optional<ConstantPool> cp = candidates.map(method -> {
Executable javaMethod = method.getJavaMethod();
if (((javaMethod != null && AnnotationAccess.isAnnotationPresent(javaMethod.getDeclaringClass(), TargetClass.class)) || (method.wrapped instanceof SubstitutionMethod)) == isSubstitution) {
return method.getConstantPool();
}
return null;
}).filter(Objects::nonNull).findAny();
assert cp.isPresent() : String.format("No constant pool was found in the %s class.", isSubstitution ? "substitution" : "default");
return cp.get();
}

private static ConstantPool.BootstrapMethodInvocation getBootstrap(ConstantPool constantPool, int index) {
try {
return constantPool.lookupBootstrapMethodInvocation(index, INVOKE_DYNAMIC_OPCODE);
} catch (IndexOutOfBoundsException e) {
return null;
}
}

@Override
protected boolean loadMethod(EconomicMap<String, Object> methodData, int mid) {
String wrappedMethod = get(methodData, WRAPPED_METHOD_TAG);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@
import jdk.graal.compiler.nodes.EncodedGraph;
import jdk.graal.compiler.util.ObjectCopier;
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaMethod;

public class SVMImageLayerSnapshotUtil extends ImageLayerSnapshotUtil {
public static final String GENERATED_SERIALIZATION = "jdk.internal.reflect.GeneratedSerializationConstructorAccessor";
Expand Down Expand Up @@ -169,7 +168,7 @@ public String getMethodIdentifier(AnalysisMethod method) {
return getGeneratedSerializationName(declaringClass) + ":" + method.getName();
}
if (method.wrapped instanceof FactoryMethod factoryMethod) {
ResolvedJavaMethod targetConstructor = factoryMethod.getTargetConstructor();
AnalysisMethod targetConstructor = method.getUniverse().lookup(factoryMethod.getTargetConstructor());
return addModuleName(targetConstructor.getDeclaringClass().toJavaName(true) + getQualifiedName(method), moduleName);
}
if (method.wrapped instanceof IncompatibleClassChangeFallbackMethod) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@

import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_ID_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.C_ENTRY_POINT_LITERAL_CODE_POINTER;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.HOLDER_CLASS_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.HUB_IDENTITY_HASH_CODE_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_KEYS;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_OBJECTS;
Expand All @@ -40,11 +41,13 @@
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INITIALIZED_AT_BUILD_TIME_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INITIALIZED_NO_TRACKING_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_NO_INITIALIZER_NO_TRACKING_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.LAMBDA_TYPE_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.LOCATION_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.METHOD_POINTER_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.OBJECT_OFFSET_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STATIC_OBJECT_FIELDS_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STATIC_PRIMITIVE_FIELDS_TAG;
import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.WRAPPED_TYPE_TAG;

import java.util.ArrayList;
import java.util.Arrays;
Expand Down Expand Up @@ -92,6 +95,7 @@
import com.oracle.svm.hosted.reflect.proxy.ProxySubstitutionType;
import com.oracle.svm.util.LogUtils;

import jdk.graal.compiler.java.LambdaUtils;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.ResolvedJavaMethod;
import jdk.vm.ci.meta.ResolvedJavaType;
Expand Down Expand Up @@ -149,6 +153,11 @@ protected void persistType(AnalysisType type, EconomicMap<String, Object> typeMa
}
}

if (LambdaUtils.isLambdaType(type)) {
typeMap.put(WRAPPED_TYPE_TAG, LAMBDA_TYPE_TAG);
typeMap.put(HOLDER_CLASS_TAG, LambdaUtils.capturingClass(type.toJavaName()));
}

super.persistType(type, typeMap);
}

Expand Down

0 comments on commit c5e63c8

Please sign in to comment.