Skip to content

Commit

Permalink
[GR-42114] Integrate missing resource metadata
Browse files Browse the repository at this point in the history
PullRequest: graal/14478
  • Loading branch information
Zeavee committed Aug 11, 2023
2 parents 6cea685 + c125fae commit f4f5f2e
Show file tree
Hide file tree
Showing 35 changed files with 1,031 additions and 318 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -593,78 +593,48 @@ private static boolean newArrayInstance0(JNIEnvironment jni, Breakpoint bp, JNIV

private static boolean findResource(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIObjectHandle self = getReceiver(thread);
JNIObjectHandle module = getObjectArgument(thread, 1);
JNIObjectHandle name = getObjectArgument(thread, 2);
JNIObjectHandle result = Support.callObjectMethodLL(jni, self, bp.method, module, name);
if (clearException(jni)) {
result = nullHandle();
}
traceReflectBreakpoint(jni, nullHandle(), nullHandle(), callerClass, bp.specification.methodName, result.notEqual(nullHandle()), state.getFullStackTraceOrNull(),
traceReflectBreakpoint(jni, nullHandle(), nullHandle(), callerClass, bp.specification.methodName, true, state.getFullStackTraceOrNull(),
fromJniString(jni, module), fromJniString(jni, name));
return true;
}

private static boolean getResource(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleGetResources(jni, thread, bp, false, state);
return handleGetResources(jni, thread, bp, state);
}

private static boolean getResources(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleGetResources(jni, thread, bp, true, state);
return handleGetResources(jni, thread, bp, state);
}

private static boolean handleGetResources(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, boolean returnsEnumeration, InterceptedState state) {
private static boolean handleGetResources(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIObjectHandle self = getReceiver(thread);
JNIObjectHandle name = getObjectArgument(thread, 1);
boolean result;
JNIObjectHandle returnValue = Support.callObjectMethodL(jni, self, bp.method, name);
result = returnValue.notEqual(nullHandle());
if (clearException(jni)) {
result = false;
}
if (result && returnsEnumeration) {
result = hasEnumerationElements(jni, returnValue);
}
JNIObjectHandle selfClazz = nullHandle(); // self is java.lang.ClassLoader, get its class
if (self.notEqual(nullHandle())) {
selfClazz = jniFunctions().getGetObjectClass().invoke(jni, self);
if (clearException(jni)) {
selfClazz = nullHandle();
}
}
traceReflectBreakpoint(jni, selfClazz, nullHandle(), callerClass, bp.specification.methodName, result, state.getFullStackTraceOrNull(), fromJniString(jni, name));
traceReflectBreakpoint(jni, selfClazz, nullHandle(), callerClass, bp.specification.methodName, true, state.getFullStackTraceOrNull(), fromJniString(jni, name));
return true;
}

private static boolean hasEnumerationElements(JNIEnvironment jni, JNIObjectHandle obj) {
boolean hasElements = Support.callBooleanMethod(jni, obj, agent.handles().javaUtilEnumerationHasMoreElements);
if (clearException(jni)) {
hasElements = false;
}
return hasElements;
}

private static boolean getSystemResource(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleGetSystemResources(jni, thread, bp, false, state);
return handleGetSystemResources(jni, thread, bp, state);
}

private static boolean getSystemResources(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
return handleGetSystemResources(jni, thread, bp, true, state);
return handleGetSystemResources(jni, thread, bp, state);
}

private static boolean handleGetSystemResources(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, boolean returnsEnumeration, InterceptedState state) {
private static boolean handleGetSystemResources(JNIEnvironment jni, JNIObjectHandle thread, Breakpoint bp, InterceptedState state) {
JNIObjectHandle callerClass = state.getDirectCallerClass();
JNIObjectHandle name = getReceiver(thread);
JNIObjectHandle returnValue = Support.callStaticObjectMethodL(jni, bp.clazz, bp.method, name);
boolean result = returnValue.notEqual(nullHandle());
if (clearException(jni)) {
result = false;
}
if (result && returnsEnumeration) {
result = hasEnumerationElements(jni, returnValue);
}
traceReflectBreakpoint(jni, nullHandle(), nullHandle(), callerClass, bp.specification.methodName, result, state.getFullStackTraceOrNull(), fromJniString(jni, name));
traceReflectBreakpoint(jni, nullHandle(), nullHandle(), callerClass, bp.specification.methodName, true, state.getFullStackTraceOrNull(), fromJniString(jni, name));
return true;
}

Expand Down Expand Up @@ -734,14 +704,11 @@ private static boolean getBundleImpl(JNIEnvironment jni, JNIObjectHandle thread,
JNIObjectHandle control = getObjectArgument(thread, 4);
JNIObjectHandle result = Support.callStaticObjectMethodLLLLL(jni, bp.clazz, bp.method, callerModule, module, baseName, locale, control);
BundleInfo bundleInfo = BundleInfo.NONE;
if (clearException(jni)) {
result = nullHandle();
} else {
if (!clearException(jni)) {
bundleInfo = extractBundleInfo(jni, result);
}
traceReflectBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImpl", result.notEqual(nullHandle()),
state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo.classNames,
bundleInfo.locales);
traceReflectBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImpl", true, state.getFullStackTraceOrNull(),
Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo.classNames, bundleInfo.locales);
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,9 @@ public void processEntry(EconomicMap<String, ?> entry, ConfigurationSet configur
List<String> classNames = (List<String>) args.get(5);
@SuppressWarnings("unchecked")
List<String> locales = (List<String>) args.get(6);
resourceConfiguration.addBundle(condition, classNames, locales, baseName);
if (baseName != null) {
resourceConfiguration.addBundle(condition, classNames, locales, baseName);
}
break;
}
case "allocateInstance": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
*/
package com.oracle.svm.core;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.List;
Expand Down Expand Up @@ -56,6 +57,10 @@ public interface ResourceCollector {
void addResource(Module module, String resourceName, InputStream resourceStream, boolean fromJar);

void addDirectoryResource(Module module, String dir, String content, boolean fromJar);

void registerNegativeQuery(Module module, String resourceName);

void registerIOException(Module module, String resourceName, IOException e, boolean linkAtBuildTime);
}

public abstract void collectResources(ResourceCollector resourceCollector);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2023, 2023, 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. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* 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.
*/
package com.oracle.svm.core;

import static com.oracle.svm.core.SubstrateOptions.ReportingMode.Warn;

import java.io.Serial;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.oracle.svm.core.util.ExitStatus;

public final class MissingRegistrationUtils {

public static boolean throwMissingRegistrationErrors() {
return SubstrateOptions.ThrowMissingRegistrationErrors.hasBeenSet();
}

public static SubstrateOptions.ReportingMode missingRegistrationReportingMode() {
return SubstrateOptions.MissingRegistrationReportingMode.getValue();
}

private static final int CONTEXT_LINES = 4;

private static final Set<String> seenOutputs = SubstrateOptions.MissingRegistrationReportingMode.getValue() == Warn ? ConcurrentHashMap.newKeySet() : null;

public static void report(Error exception, StackTraceElement responsibleClass) {
if (responsibleClass != null && !MissingRegistrationSupport.singleton().reportMissingRegistrationErrors(responsibleClass)) {
return;
}
switch (missingRegistrationReportingMode()) {
case Throw -> {
throw exception;
}
case Exit -> {
exception.printStackTrace(System.out);
System.exit(ExitStatus.MISSING_METADATA.getValue());
}
case ExitTest -> {
throw new ExitException(exception);
}
case Warn -> {
StackTraceElement[] stackTrace = exception.getStackTrace();
int printed = 0;
StackTraceElement entryPoint = null;
StringBuilder sb = new StringBuilder(exception.toString());
sb.append("\n");
for (StackTraceElement stackTraceElement : stackTrace) {
if (printed == 0) {
String moduleName = stackTraceElement.getModuleName();
/*
* Skip internal stack trace entries to include only the relevant part of
* the trace in the output. The heuristic used is that any JDK and Graal
* code is excluded except the first element, so that the rest of the trace
* consists of meaningful application code entries.
*/
if (moduleName != null && (moduleName.equals("java.base") || moduleName.startsWith("org.graalvm"))) {
entryPoint = stackTraceElement;
} else {
printLine(sb, entryPoint);
printed++;
}
}
if (printed > 0) {
printLine(sb, stackTraceElement);
printed++;
}
if (printed >= CONTEXT_LINES) {
break;
}
}
if (seenOutputs.isEmpty()) {
/* First output, we print an explanation message */
System.out.println("Note: this run will print partial stack traces of the locations where a " + exception.getClass().toString() + " would be thrown " +
"when the -H:+ThrowMissingRegistrationErrors option is set. The trace stops at the first entry of JDK code and provides " + CONTEXT_LINES + " lines of context.");
}
String output = sb.toString();
if (seenOutputs.add(output)) {
System.out.print(output);
}
}
}
}

private static void printLine(StringBuilder sb, Object object) {
sb.append(" ").append(object).append(System.lineSeparator());
}

public static final class ExitException extends Error {
@Serial//
private static final long serialVersionUID = -3638940737396726143L;

public ExitException(Throwable cause) {
super(cause);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,24 @@ public Boolean getValueOrDefault(UnmodifiableEconomicMap<OptionKey<?>, Object> v
@Option(help = "file:doc-files/MissingRegistrationPathsHelp.txt")//
public static final HostedOptionKey<LocatableMultiOptionValue.Strings> ThrowMissingRegistrationErrorsPaths = new HostedOptionKey<>(LocatableMultiOptionValue.Strings.build());

public enum ReportingMode {
Warn,
Throw,
ExitTest,
Exit
}

@Option(help = {"Select the mode in which the missing reflection registrations will be reported.",
"Possible values are:",
"\"Throw\" (default): Throw a MissingReflectionRegistrationError;",
"\"Exit\": Call System.exit() to avoid accidentally catching the error;",
"\"Warn\": Print a message to stdout, including a stack trace to see what caused the issue."})//
public static final HostedOptionKey<ReportingMode> MissingRegistrationReportingMode = new HostedOptionKey<>(
ReportingMode.Throw);

@Option(help = "Instead of warning, throw IOExceptions for link-at-build-time resources at build time")//
public static final HostedOptionKey<Boolean> ThrowLinkAtBuildTimeIOExceptions = new HostedOptionKey<>(false);

@Option(help = "Allows the addresses of pinned objects to be passed to other code.", type = OptionType.Expert) //
public static final HostedOptionKey<Boolean> PinnedObjectAddressing = new HostedOptionKey<>(true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.MissingRegistrationUtils;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.allocationprofile.AllocationCounter;
import com.oracle.svm.core.allocationprofile.AllocationSite;
Expand Down Expand Up @@ -307,7 +308,7 @@ private static void instanceHubErrorStub(DynamicHub hub) throws InstantiationExc
} else if (!hub.isInstanceClass() || LayoutEncoding.isSpecial(hub.getLayoutEncoding())) {
throw new InstantiationException("Can only allocate instance objects for concrete classes.");
} else if (!hub.isInstantiated()) {
if (MissingReflectionRegistrationUtils.throwMissingRegistrationErrors()) {
if (MissingRegistrationUtils.throwMissingRegistrationErrors()) {
MissingReflectionRegistrationUtils.forClass(hub.getTypeName());
}
throw new IllegalArgumentException("Type " + DynamicHub.toClass(hub).getTypeName() + " is instantiated reflectively but was never registered." +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*/
package com.oracle.svm.core.hub;

import static com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils.throwMissingRegistrationErrors;
import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors;

import org.graalvm.collections.EconomicMap;
import org.graalvm.nativeimage.ImageSingletons;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
*/
package com.oracle.svm.core.hub;

import static com.oracle.svm.core.reflect.MissingReflectionRegistrationUtils.throwMissingRegistrationErrors;
import static com.oracle.svm.core.MissingRegistrationUtils.throwMissingRegistrationErrors;
import static com.oracle.svm.core.reflect.ReflectionMetadataDecoder.NO_DATA;
import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_CLASSES_FLAG;
import static com.oracle.svm.core.reflect.target.ReflectionMetadataDecoderImpl.ALL_CONSTRUCTORS_FLAG;
Expand Down Expand Up @@ -805,7 +805,7 @@ public Enum<?>[] getEnumConstantsShared() {
@Substitute
public InputStream getResourceAsStream(String resourceName) {
String resolvedName = resolveName(resourceName);
return Resources.createInputStream(module, resolvedName);
return Resources.singleton().createInputStream(module, resolvedName);
}

@KeepOriginal
Expand Down
Loading

0 comments on commit f4f5f2e

Please sign in to comment.