Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,6 @@
import java.util.Optional;
import java.util.StringJoiner;

import com.oracle.svm.core.reflect.fieldaccessor.UnsafeFieldAccessorFactory;
import jdk.internal.access.JavaLangReflectAccess;
import jdk.internal.reflect.FieldAccessor;
import org.graalvm.compiler.core.common.NumUtil;
import org.graalvm.compiler.core.common.SuppressFBWarnings;
import org.graalvm.nativeimage.AnnotationAccess;
Expand Down Expand Up @@ -114,13 +111,16 @@
import com.oracle.svm.core.reflect.ReflectionMetadataDecoder.MethodDescriptor;
import com.oracle.svm.core.reflect.Target_java_lang_reflect_RecordComponent;
import com.oracle.svm.core.reflect.Target_jdk_internal_reflect_ConstantPool;
import com.oracle.svm.core.reflect.fieldaccessor.UnsafeFieldAccessorFactory;
import com.oracle.svm.core.util.LazyFinalReference;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ReflectionUtil;
import com.oracle.svm.util.ReflectionUtil.ReflectionUtilError;

import jdk.internal.access.JavaLangReflectAccess;
import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.FieldAccessor;
import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory;
import sun.reflect.annotation.AnnotationType;
Expand Down Expand Up @@ -246,13 +246,16 @@ public final class DynamicHub implements AnnotatedElement, java.lang.reflect.Typ
*/
private static final int IS_LAMBDA_FORM_HIDDEN_BIT = 9;

/** Similar to {@link #flags}, but non-final because {@link #setData} sets the value. */
@UnknownPrimitiveField(availability = AfterHostedUniverse.class)//
private byte instantiationFlags;
private byte additionalFlags;

/** Has the type been discovered as instantiated by the static analysis? */
private static final int IS_INSTANTIATED_BIT = 0;
/** Can this class be instantiated as an instance. */
private static final int CAN_INSTANTIATE_AS_INSTANCE_BIT = 1;
/** Is the class a proxy class according to {@link java.lang.reflect.Proxy#isProxyClass}? */
private static final int IS_PROXY_CLASS_BIT = 2;

/**
* The {@link Modifier modifiers} of this class.
Expand Down Expand Up @@ -427,7 +430,7 @@ public void setClassInitializationInfo(ClassInitializationInfo classInitializati

@Platforms(Platform.HOSTED_ONLY.class)
public void setData(int layoutEncoding, int typeID, int monitorOffset, int optionalIdentityHashOffset, short typeCheckStart, short typeCheckRange, short typeCheckSlot,
short[] typeCheckSlots, CFunctionPointer[] vtable, long referenceMapIndex, boolean isInstantiated, boolean canInstantiateAsInstance) {
short[] typeCheckSlots, CFunctionPointer[] vtable, long referenceMapIndex, boolean isInstantiated, boolean canInstantiateAsInstance, boolean isProxyClass) {
assert this.vtable == null : "Initialization must be called only once";
assert !(!isInstantiated && canInstantiateAsInstance);
if (LayoutEncoding.isPureInstance(layoutEncoding)) {
Expand All @@ -451,7 +454,9 @@ public void setData(int layoutEncoding, int typeID, int monitorOffset, int optio
throw VMError.shouldNotReachHere("Reference map index not within integer range, need to switch field from int to long");
}
this.referenceMapIndex = (int) referenceMapIndex;
this.instantiationFlags = NumUtil.safeToUByte(makeFlag(IS_INSTANTIATED_BIT, isInstantiated) | makeFlag(CAN_INSTANTIATE_AS_INSTANCE_BIT, canInstantiateAsInstance));
this.additionalFlags = NumUtil.safeToUByte(makeFlag(IS_INSTANTIATED_BIT, isInstantiated) |
makeFlag(CAN_INSTANTIATE_AS_INSTANCE_BIT, canInstantiateAsInstance) |
makeFlag(IS_PROXY_CLASS_BIT, isProxyClass));
}

@Platforms(Platform.HOSTED_ONLY.class)
Expand Down Expand Up @@ -641,11 +646,15 @@ public int getReferenceMapIndex() {
}

public boolean isInstantiated() {
return isFlagSet(instantiationFlags, IS_INSTANTIATED_BIT);
return isFlagSet(additionalFlags, IS_INSTANTIATED_BIT);
}

public boolean canInstantiateAsInstance() {
return isFlagSet(instantiationFlags, CAN_INSTANTIATE_AS_INSTANCE_BIT);
return isFlagSet(additionalFlags, CAN_INSTANTIATE_AS_INSTANCE_BIT);
}

public boolean isProxyClass() {
return isFlagSet(additionalFlags, IS_PROXY_CLASS_BIT);
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,24 @@

import java.lang.reflect.InvocationHandler;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import org.graalvm.collections.EconomicMap;
import org.graalvm.collections.EconomicSet;
import org.graalvm.collections.Equivalence;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
import org.graalvm.nativeimage.hosted.RuntimeReflection;

import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.configure.ConfigurationFiles;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.hub.PredefinedClassesSupport;
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
import com.oracle.svm.core.option.SubstrateOptionsParser;
import com.oracle.svm.core.util.ImageHeapMap;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ClassUtil;
import com.oracle.svm.util.LogUtils;
Expand Down Expand Up @@ -82,71 +85,78 @@ public String toString() {
}
}

private final Map<ProxyCacheKey, Object> proxyCache;
private final EconomicSet<Class<?>> hostedProxyClasses = EconomicSet.create(Equivalence.IDENTITY);
private final EconomicMap<ProxyCacheKey, Object> proxyCache = ImageHeapMap.create();

@Platforms(Platform.HOSTED_ONLY.class)
public DynamicProxySupport() {
this.proxyCache = new ConcurrentHashMap<>();
}

@Platforms(Platform.HOSTED_ONLY.class)
@Override
public void addProxyClass(Class<?>... interfaces) {
@Platforms(Platform.HOSTED_ONLY.class)
public synchronized void addProxyClass(Class<?>... interfaces) {
/*
* Make a defensive copy of the interfaces array to protect against the caller modifying the
* array.
*/
final Class<?>[] intfs = interfaces.clone();
Class<?>[] intfs = interfaces.clone();
ProxyCacheKey key = new ProxyCacheKey(intfs);

proxyCache.computeIfAbsent(key, k -> {
try {
Class<?> clazz = createProxyClassFromImplementedInterfaces(intfs);

boolean isPredefinedProxy = Arrays.stream(interfaces).anyMatch(PredefinedClassesSupport::isPredefined);
if (!proxyCache.containsKey(key)) {
proxyCache.put(key, createProxyClass(intfs));
}
}

if (isPredefinedProxy) {
/*
* Treat the proxy as a predefined class so that we can set its class loader to
* the loader passed at runtime. If one of the interfaces is a predefined class,
* this can be required so that the classes can actually see each other
* according to the runtime class loader hierarchy.
*/
PredefinedClassesSupport.registerClass(clazz);
RuntimeClassInitialization.initializeAtRunTime(clazz);
}
@Platforms(Platform.HOSTED_ONLY.class)
private Object createProxyClass(Class<?>[] interfaces) {
try {
Class<?> clazz = createProxyClassFromImplementedInterfaces(interfaces);

boolean isPredefinedProxy = Arrays.stream(interfaces).anyMatch(PredefinedClassesSupport::isPredefined);
if (isPredefinedProxy) {
/*
* The constructor of the generated dynamic proxy class that takes a
* `java.lang.reflect.InvocationHandler` argument, i.e., the one reflectively
* invoked by `java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[],
* InvocationHandler)`, is registered for reflection so that dynamic proxy instances
* can be allocated at run time.
* Treat the proxy as a predefined class so that we can set its class loader to the
* loader passed at runtime. If one of the interfaces is a predefined class, this
* can be required so that the classes can actually see each other according to the
* runtime class loader hierarchy.
*/
RuntimeReflection.register(ReflectionUtil.lookupConstructor(clazz, InvocationHandler.class));
PredefinedClassesSupport.registerClass(clazz);
RuntimeClassInitialization.initializeAtRunTime(clazz);
}

/*
* The proxy class reflectively looks up the methods of the interfaces it implements
* to pass a Method object to InvocationHandler.
*/
for (Class<?> intf : intfs) {
RuntimeReflection.register(intf.getMethods());
}

return clazz;
} catch (Throwable t) {
LogUtils.warning("Could not create a proxy class from list of interfaces: %s. Reason: %s", Arrays.toString(interfaces), t.getMessage());
return t;
/*
* The constructor of the generated dynamic proxy class that takes a
* `java.lang.reflect.InvocationHandler` argument, i.e., the one reflectively invoked by
* `java.lang.reflect.Proxy.newProxyInstance(ClassLoader, Class<?>[],
* InvocationHandler)`, is registered for reflection so that dynamic proxy instances can
* be allocated at run time.
*/
RuntimeReflection.register(ReflectionUtil.lookupConstructor(clazz, InvocationHandler.class));

/*
* The proxy class reflectively looks up the methods of the interfaces it implements to
* pass a Method object to InvocationHandler.
*/
for (Class<?> intf : interfaces) {
RuntimeReflection.register(intf.getMethods());
}
});

hostedProxyClasses.add(clazz);
return clazz;
} catch (Throwable t) {
LogUtils.warning("Could not create a proxy class from list of interfaces: %s. Reason: %s", Arrays.toString(interfaces), t.getMessage());
return t;
}
}

@Override
@Platforms(Platform.HOSTED_ONLY.class)
public Class<?> createProxyClassForSerialization(Class<?>... interfaces) {
final Class<?>[] intfs = interfaces.clone();

return createProxyClassFromImplementedInterfaces(intfs);
}

@Platforms(Platform.HOSTED_ONLY.class)
private static Class<?> createProxyClassFromImplementedInterfaces(Class<?>[] interfaces) {
return getJdkProxyClass(getCommonClassLoaderOrFail(null, interfaces), interfaces);
}
Expand Down Expand Up @@ -228,7 +238,15 @@ private static void describeLoaderChain(StringBuilder b, ClassLoader loader) {

@Override
public boolean isProxyClass(Class<?> clazz) {
return proxyCache.containsValue(clazz);
if (SubstrateUtil.HOSTED) {
return isHostedProxyClass(clazz);
}
return DynamicHub.fromClass(clazz).isProxyClass();
}

@Platforms(Platform.HOSTED_ONLY.class)
private synchronized boolean isHostedProxyClass(Class<?> clazz) {
return hostedProxyClasses.contains(clazz);
}

@SuppressWarnings("deprecation")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.hub.DynamicHubSupport;
import com.oracle.svm.core.hub.LayoutEncoding;
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
import com.oracle.svm.core.meta.MethodPointer;
import com.oracle.svm.core.reflect.SubstrateConstructorAccessor;
import com.oracle.svm.core.reflect.SubstrateMethodAccessor;
Expand Down Expand Up @@ -1065,9 +1066,11 @@ private void buildHubs() {
assert ((SubstrateReferenceMap) referenceMap).hasNoDerivedOffsets();
long referenceMapIndex = referenceMapEncoder.lookupEncoding(referenceMap);

boolean isProxyClass = ImageSingletons.lookup(DynamicProxyRegistry.class).isProxyClass(type.getJavaClass());

DynamicHub hub = type.getHub();
hub.setData(layoutHelper, type.getTypeID(), monitorOffset, optionalIdHashOffset, type.getTypeCheckStart(), type.getTypeCheckRange(),
type.getTypeCheckSlot(), type.getTypeCheckSlots(), vtable, referenceMapIndex, type.isInstantiated(), canInstantiateAsInstance);
type.getTypeCheckSlot(), type.getTypeCheckSlots(), vtable, referenceMapIndex, type.isInstantiated(), canInstantiateAsInstance, isProxyClass);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,10 @@
import com.oracle.svm.core.configure.ConfigurationFile;
import com.oracle.svm.core.configure.ConfigurationFiles;
import com.oracle.svm.core.configure.ProxyConfigurationParser;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
import com.oracle.svm.core.reflect.proxy.DynamicProxySupport;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.hosted.ConfigurationTypeResolver;
import com.oracle.svm.hosted.FallbackFeature;
import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl;
Expand Down