Skip to content

Commit

Permalink
Bugfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
loicottet committed Mar 8, 2022
1 parent fa190bc commit 812e5a3
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 122 deletions.
10 changes: 5 additions & 5 deletions docs/reference-manual/native-image/BuildOutput.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ To reduce overhead, please ensure that the classpath only contains entries that
#### <a name="glossary-reflection-registrations"></a>Reflection Registrations
The number of classes, fields, and methods that are registered for reflection.
Large numbers can cause significant reflection overheads, slow down the build process, and increase the size of the native image (see [method metadata](#glossary-method-metadata)).
Large numbers can cause significant reflection overheads, slow down the build process, and increase the size of the native image (see [reflection metadata](#glossary-reflection-metadata)).
#### <a name="glossary-jni-access-registrations"></a>JNI Access Registrations
The number of classes, fields, and methods that are registered for [JNI][doc_jni] access.
Expand Down Expand Up @@ -136,16 +136,16 @@ Therefore, reducing the number of [reachable methods](#glossary-reachability) al
The image heap contains reachable objects such as static application data, metadata, and `byte[]` for different purposes.
##### <a name="glossary-general-heap-data"></a>General Heap Data Stored in `byte[]`
The total size of all `byte[]` objects that are neither used for `java.lang.String`, nor [code metadata](#glossary-code-metadata), nor [method metadata](#glossary-method-metadata), nor [graph encodings](#glossary-graph-encodings).
The total size of all `byte[]` objects that are neither used for `java.lang.String`, nor [code metadata](#glossary-code-metadata), nor [reflection metadata](#glossary-reflection-metadata), nor [graph encodings](#glossary-graph-encodings).
Therefore, this can also include `byte[]` objects from application code.
##### <a name="glossary-code-metadata"></a>Code Metadata Stored in `byte[]`
The total size of all `byte[]` objects used for metadata for the [code area](#glossary-code-area).
Therefore, reducing the number of [reachable methods](#glossary-reachability) also reduces the size of this metadata.
##### <a name="glossary-method-metadata"></a>Method Metadata Stored in `byte[]`
The total size of all `byte[]` objects used for method metadata, a type of reflection metadata.
To reduce the amount of method metadata, reduce the number of [classes registered for reflection](#glossary-reflection-classes).
##### <a name="glossary-reflection-metadata"></a>Reflection Metadata Stored in `byte[]`
The total size of all `byte[]` objects used for reflection metadata, including class, field, method and constructor data.
To reduce the amount of reflection metadata, reduce the number of [elements registered for reflection](#glossary-reflection-registrations).
##### <a name="glossary-graph-encodings"></a>Graph Encodings Stored in `byte[]`
The total size of all `byte[]` objects used for graph encodings.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,11 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, String ol
}
};

@SuppressWarnings("unused")//
@APIOption(name = "configure-reflection-metadata")//
@Option(help = "Enable runtime instantiation of reflection objects for non-invoked methods.", type = OptionType.Expert, deprecated = true)//
public static final HostedOptionKey<Boolean> ConfigureReflectionMetadata = new HostedOptionKey<>(true);

@Option(help = "Include a list of methods included in the image for runtime inspection.", type = OptionType.Expert)//
public static final HostedOptionKey<Boolean> IncludeMethodData = new HostedOptionKey<>(true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,9 @@ private static ReflectionFactory getReflectionFactory() {
@KeepOriginal
private static native Field searchFields(Field[] fields, String name);

/**
* @see #filterHidingMethods(Method...)
*/
@Substitute
private static Method searchMethods(Method[] allMethods, String name, Class<?>[] parameterTypes) {
Method[] methods = filterHidingMethods(allMethods);
Expand All @@ -1057,6 +1060,9 @@ private static Method searchMethods(Method[] allMethods, String name, Class<?>[]
@KeepOriginal
private static native Field[] copyFields(Field[] arg);

/**
* @see #filterHidingMethods(Method...)
*/
@Substitute
private static Method[] copyMethods(Method[] original) {
Method[] arg = filterHidingMethods(original);
Expand Down Expand Up @@ -1235,6 +1241,9 @@ private String getSimpleBinaryName0() {
/* See open/src/hotspot/share/prims/jvm.cpp#1522. */
}

/**
* @see #filterHidingMethods(Method...)
*/
@Substitute //
@SuppressWarnings({"unused"})
List<Method> getDeclaredPublicMethods(String methodName, Class<?>... parameterTypes) {
Expand Down Expand Up @@ -1476,6 +1485,11 @@ private Class<?>[] getPermittedSubclasses0() {
@KeepOriginal
native AnnotationType getAnnotationType();

/*
* We need to filter out hiding methods at the last moment. This ensures that the JDK internals
* see them as regular methods and ensure the visibility of methods is correct, but they should
* not be returned to application code.
*/
private static Method[] filterHidingMethods(Method... methods) {
List<Method> filtered = new ArrayList<>();
for (Method method : methods) {
Expand Down Expand Up @@ -1510,6 +1524,13 @@ private static AnnotationType getAnnotationType(DynamicHub that) {
private static class CachedConstructorAccessors {
@SuppressWarnings("unused")
private static Constructor<?> getCachedConstructor(DynamicHub that) {
/*
* The JavaDoc for the Class.newInstance method states that "The class is initialized if
* it has not already been initialized". However, it doesn't specify if the absence of a
* nullary constructor will result in an InstantiationException before the class is
* initialized. We eagerly initialize the class to conform with JCK tests.
*/
that.ensureInitialized();
return that.companion.getCachedConstructor();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ private Map<String, Long> calculateHeapBreakdown(Collection<ObjectInfo> heapObje
}
long metadataByteLength = ImageSingletons.lookup(ReflectionMetadataDecoder.class).getMetadataByteLength();
if (metadataByteLength > 0) {
classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("reflection metadata", "#glossary-method-metadata"), metadataByteLength);
classNameToSize.put(BREAKDOWN_BYTE_ARRAY_PREFIX + linkStrategy.asDocLink("reflection metadata", "#glossary-reflection-metadata"), metadataByteLength);
remainingBytes -= metadataByteLength;
}
if (graphEncodingByteLength > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
package com.oracle.svm.hosted.heap;

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
import java.util.function.Consumer;

Expand Down Expand Up @@ -61,6 +62,7 @@ public class SVMImageHeapScanner extends ImageHeapScanner {
private final Class<?> economicMapImpl;
private final Field economicMapImplEntriesField;
private final Field economicMapImplHashArrayField;
private final RuntimeReflectionSupport reflectionSupport;

public SVMImageHeapScanner(ImageHeap imageHeap, ImageClassLoader loader, AnalysisMetaAccess metaAccess,
SnippetReflectionProvider snippetReflection, ConstantReflectionProvider aConstantReflection, ObjectScanningObserver aScanningObserver) {
Expand All @@ -70,6 +72,7 @@ public SVMImageHeapScanner(ImageHeap imageHeap, ImageClassLoader loader, Analysi
economicMapImplEntriesField = ReflectionUtil.lookupField(economicMapImpl, "entries");
economicMapImplHashArrayField = ReflectionUtil.lookupField(economicMapImpl, "hashArray");
ImageSingletons.add(ImageHeapScanner.class, this);
reflectionSupport = ImageSingletons.lookup(RuntimeReflectionSupport.class);
}

public static ImageHeapScanner instance() {
Expand Down Expand Up @@ -144,8 +147,8 @@ protected void onObjectReachable(ImageHeapObject imageHeapObject) {
super.onObjectReachable(imageHeapObject);

Object object = SubstrateObjectConstant.asObject(imageHeapObject.getObject());
if (object instanceof AccessibleObject) {
ImageSingletons.lookup(RuntimeReflectionSupport.class).registerHeapReflectionObject((AccessibleObject) object);
if (object instanceof Field || object instanceof Executable) {
reflectionSupport.registerHeapReflectionObject((AccessibleObject) object);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,7 @@ private static void registerTypesForReachableMethod(DuringAnalysisAccessImpl acc
for (JavaType paramType : analysisMethod.toParameterTypes()) {
makeAnalysisTypeReachable(access, (AnalysisType) paramType);
}
makeAnalysisTypeReachable(access, (AnalysisType) analysisMethod.getSignature().getReturnType(null));
}

private void makeTypeReachable(DuringAnalysisAccessImpl access, Type type) {
Expand Down Expand Up @@ -524,11 +525,8 @@ private static void registerTypesForRecordComponent(DuringAnalysisAccessImpl acc
}
}

private static void registerTypesForAnnotation(DuringAnalysisAccessImpl accessImpl, Annotation annotation) {
/*
* Don't make annotation types reachable unless they have a chance of being queried.
*/
accessImpl.registerReachabilityHandler((access) -> registerTypesForAnnotationValue((DuringAnalysisAccessImpl) access, annotation.annotationType(), annotation), annotation.annotationType());
private static void registerTypesForAnnotation(DuringAnalysisAccessImpl access, Annotation annotation) {
registerTypesForAnnotationValue(access, annotation.annotationType(), annotation);
}

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
import java.lang.reflect.Proxy;
import java.util.Collections;
Expand All @@ -62,6 +63,7 @@
import com.oracle.svm.core.annotate.Delete;
import com.oracle.svm.core.code.CodeInfoEncoder;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.meta.DirectSubstrateObjectConstant;
import com.oracle.svm.core.meta.SharedField;
import com.oracle.svm.core.meta.SubstrateObjectConstant;
import com.oracle.svm.core.reflect.ReflectionMetadataDecoder;
Expand Down Expand Up @@ -157,7 +159,31 @@ private void registerField(HostedType declaringType, FieldMetadata metadata) {

private void registerMethod(HostedType declaringType, MethodMetadata metadata) {
sortedTypes.add(declaringType);
methodData.computeIfAbsent(declaringType, t -> new HashSet<>()).add(metadata);
methodData.computeIfAbsent(declaringType, t -> new TreeSet<>(ReflectionMetadataEncoderImpl::compareMethods)).add(metadata);
}

/*
* Force bridge methods at the end of the methods list so JCK tests see the real method first
* when iterating through the list.
*/
private static int compareMethods(MethodMetadata left, MethodMetadata right) {
boolean isLeftBridge = isBridgeMethod(left);
boolean isRightBridge = isBridgeMethod(right);
if (isLeftBridge != isRightBridge) {
return isLeftBridge ? 1 : -1;
} else {
/* Ensure consistent ordering */
return Integer.compare(left.hashCode(), right.hashCode());
}
}

private static boolean isBridgeMethod(MethodMetadata metadata) {
if (metadata.heapObject != null) {
return ((Method) ((DirectSubstrateObjectConstant) metadata.heapObject).getObject()).isBridge();
} else {
/* BRIDGE and VOLATILE use the same modifier bit, but only VOLATILE is public */
return Modifier.isVolatile(metadata.modifiers);
}
}

private void registerConstructor(HostedType declaringType, ConstructorMetadata metadata) {
Expand Down Expand Up @@ -548,6 +574,7 @@ public void addReachableMethodMetadata(HostedMethod method) {
HostedType declaringType = method.getDeclaringClass();
String name = isMethod ? method.getName() : null;
HostedType[] parameterTypes = getParameterTypes(method);
HostedType returnType = (HostedType) method.getSignature().getReturnType(null);
int modifiers = method.getModifiers();

/* Fill encoders with the necessary values. */
Expand All @@ -557,9 +584,10 @@ public void addReachableMethodMetadata(HostedMethod method) {
for (HostedType parameterType : parameterTypes) {
encoders.sourceClasses.addObject(parameterType.getJavaClass());
}
encoders.sourceClasses.addObject(returnType.getJavaClass());

if (isMethod) {
registerMethod(declaringType, new MethodMetadata(false, declaringType, name, parameterTypes, modifiers, null));
registerMethod(declaringType, new MethodMetadata(false, declaringType, name, parameterTypes, modifiers, returnType));
} else {
registerConstructor(declaringType, new ConstructorMetadata(declaringType, parameterTypes, modifiers));
}
Expand Down Expand Up @@ -799,7 +827,7 @@ private void encodeExecutable(UnsafeArrayTypeWriter buf, ExecutableMetadata exec
encodeName(buf, ((MethodMetadata) executable).name);
}
encodeArray(buf, executable.parameterTypes, parameterType -> encodeType(buf, parameterType));
if (isMethod && (executable.complete || ((MethodMetadata) executable).hiding)) {
if (isMethod) {
encodeType(buf, ((MethodMetadata) executable).returnType);
}
if (executable.complete) {
Expand Down Expand Up @@ -1154,6 +1182,8 @@ private static void encodeTargetInfo(UnsafeArrayTypeWriter buf, TypeAnnotation.T
buf.putU1(THROWS);
buf.putU2(targetInfo.getCount());
break;
default:
throw GraalError.shouldNotReachHere("Unknown type annotation target: " + targetInfo.getTarget());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ public String toString() {
}
}

private final ClassLoader classLoader;
private final Map<ProxyCacheKey, Object> proxyCache;

public DynamicProxySupport() {
public DynamicProxySupport(ClassLoader classLoader) {
this.classLoader = classLoader;
this.proxyCache = new ConcurrentHashMap<>();
}

Expand All @@ -96,12 +98,16 @@ public void addProxyClass(Class<?>... interfaces) {
proxyCache.computeIfAbsent(key, k -> {
Class<?> clazz;
try {
clazz = getJdkProxyClass(getCommonClassLoader(intfs), intfs);
clazz = getJdkProxyClass(classLoader, intfs);
} catch (Throwable e) {
if (NativeImageOptions.AllowIncompleteClasspath.getValue()) {
return e;
} else {
throw e;
try {
clazz = getJdkProxyClass(getCommonClassLoader(intfs), intfs);
} catch (Throwable e2) {
if (NativeImageOptions.AllowIncompleteClasspath.getValue()) {
return e;
} else {
throw e;
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void duringSetup(DuringSetupAccess a) {
DuringSetupAccessImpl access = (DuringSetupAccessImpl) a;

ImageClassLoader imageClassLoader = access.getImageClassLoader();
DynamicProxySupport dynamicProxySupport = new DynamicProxySupport();
DynamicProxySupport dynamicProxySupport = new DynamicProxySupport(imageClassLoader.getClassLoader());
ImageSingletons.add(DynamicProxyRegistry.class, dynamicProxySupport);
ConfigurationTypeResolver typeResolver = new ConfigurationTypeResolver("resource configuration", imageClassLoader, NativeImageOptions.AllowIncompleteClasspath.getValue());
ProxyRegistry proxyRegistry = new ProxyRegistry(typeResolver, dynamicProxySupport, imageClassLoader);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,11 +258,12 @@ private static Executable decodeExecutable(UnsafeArrayTypeReader buf, CodeInfo i
}
boolean complete = (modifiers & COMPLETE_FLAG_MASK) != 0;
boolean hiding = (modifiers & HIDING_FLAG_MASK) != 0;
assert !(complete && hiding);
modifiers &= ~COMPLETE_FLAG_MASK;

String name = isMethod ? decodeName(buf, info) : null;
Class<?>[] parameterTypes = decodeArray(buf, Class.class, (i) -> decodeType(buf, info));
Class<?> returnType = isMethod && hiding ? decodeType(buf, info) : null;
Class<?> returnType = isMethod ? decodeType(buf, info) : null;
if (!complete) {
if (reflectOnly != hiding) {
/*
Expand All @@ -282,8 +283,6 @@ private static Executable decodeExecutable(UnsafeArrayTypeReader buf, CodeInfo i
return SubstrateUtil.cast(constructor, Executable.class);
}
}
assert !hiding;
returnType = isMethod ? decodeType(buf, info) : null;
Class<?>[] exceptionTypes = decodeArray(buf, Class.class, (i) -> decodeType(buf, info));
String signature = decodeName(buf, info);
byte[] annotations = decodeByteArray(buf);
Expand Down
Loading

0 comments on commit 812e5a3

Please sign in to comment.