Skip to content
Closed
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 @@ -930,6 +930,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