Skip to content

Commit

Permalink
Throw exceptions for missing resource bundle registrations
Browse files Browse the repository at this point in the history
  • Loading branch information
loicottet committed Dec 21, 2023
1 parent 8a010c6 commit 9624ce5
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ public final class AccessAdvisor {
internalCallerFilter.addOrGetChildren("java.util.**", ConfigurationFilter.Inclusion.Exclude);
internalCallerFilter.addOrGetChildren("java.util.concurrent.atomic.*", ConfigurationFilter.Inclusion.Include); // Atomic*FieldUpdater
internalCallerFilter.addOrGetChildren("java.util.Collections", ConfigurationFilter.Inclusion.Include); // java.util.Collections.zeroLengthArray
// LogRecord.readObject looks up resource bundles
internalCallerFilter.addOrGetChildren("java.util.logging.LogRecord", ConfigurationFilter.Inclusion.Include);
internalCallerFilter.addOrGetChildren("java.util.random.*", ConfigurationFilter.Inclusion.Include); // RandomGeneratorFactory$$Lambda
/*
* ForkJoinTask.getThrowableException calls Class.getConstructors and
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IllformedLocaleException;
import java.util.Locale;
import java.util.Map;
Expand All @@ -43,6 +44,7 @@
import java.util.function.Function;
import java.util.stream.Collectors;

import org.graalvm.collections.EconomicMap;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
Expand Down Expand Up @@ -79,6 +81,8 @@ public class LocalizationSupport {

public final Charset defaultCharset;

private final EconomicMap<String, Set<Locale>> registeredBundles = EconomicMap.create();

public LocalizationSupport(Locale defaultLocale, Set<Locale> locales, Charset defaultCharset) {
this.defaultLocale = defaultLocale;
this.allLocales = locales.toArray(new Locale[0]);
Expand Down Expand Up @@ -273,4 +277,18 @@ private void registerNullaryConstructor(Class<?> bundleClass) {
}
RuntimeReflection.register(nullaryConstructor);
}

@Platforms(Platform.HOSTED_ONLY.class)
public void registerBundleLookup(String baseName, Locale locale) {
registeredBundles.putIfAbsent(baseName, new HashSet<>());
registeredBundles.get(baseName).add(locale);
}

public boolean isRegisteredBundleLookup(String baseName, Locale locale, Object controlOrStrategy) {
if (baseName == null || locale == null || controlOrStrategy == null) {
/* Those cases will throw a NullPointerException before any lookup */
return true;
}
return registeredBundles.get(baseName, Collections.emptySet()).contains(locale);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
*/
package com.oracle.svm.core.jdk.localization.substitutions;

import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION;

import java.util.Locale;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
Expand All @@ -38,6 +41,9 @@
import com.oracle.svm.core.annotate.TargetElement;
import com.oracle.svm.core.jdk.localization.LocalizationSupport;
import com.oracle.svm.core.jdk.localization.substitutions.modes.OptimizedLocaleMode;
import com.oracle.svm.core.jdk.resources.MissingResourceRegistrationUtils;

import jdk.internal.loader.BootLoader;

@TargetClass(java.util.ResourceBundle.class)
@SuppressWarnings({"unused"})
Expand Down Expand Up @@ -98,4 +104,64 @@ private static ResourceBundle getBundle(String baseName, @SuppressWarnings("unus
private static ResourceBundle getBundle(String baseName, Locale targetLocale, @SuppressWarnings("unused") Module module) {
return ImageSingletons.lookup(LocalizationSupport.class).asOptimizedSupport().getCached(baseName, targetLocale);
}

@Substitute
private static ResourceBundle getBundleImpl(String baseName,
Locale locale,
Class<?> caller,
ClassLoader loader,
ResourceBundle.Control control) {
Module callerModule = getCallerModule(caller);

// get resource bundles for a named module only if loader is the module's class loader
if (callerModule.isNamed() && loader == getLoader(callerModule)) {
if (!ImageSingletons.lookup(LocalizationSupport.class).isRegisteredBundleLookup(baseName, locale, control)) {
MissingResourceRegistrationUtils.missingResourceBundle(baseName, locale);
}
return getBundleImpl(callerModule, callerModule, baseName, locale, control);
}

// find resource bundles from unnamed module of given class loader
// Java agent can add to the bootclasspath e.g. via
// java.lang.instrument.Instrumentation and load classes in unnamed module.
// It may call RB::getBundle that will end up here with loader == null.
Module unnamedModule = loader != null
? loader.getUnnamedModule()
: BootLoader.getUnnamedModule();

if (!ImageSingletons.lookup(LocalizationSupport.class).isRegisteredBundleLookup(baseName, locale, control)) {
MissingResourceRegistrationUtils.missingResourceBundle(baseName, locale);
}
return getBundleImpl(callerModule, unnamedModule, baseName, locale, control);
}

@Substitute
private static ResourceBundle getBundleFromModule(Class<?> caller,
Module module,
String baseName,
Locale locale,
ResourceBundle.Control control) {
Objects.requireNonNull(module);
Module callerModule = getCallerModule(caller);
if (callerModule != module) {
@SuppressWarnings("removal")
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(GET_CLASSLOADER_PERMISSION);
}
}
if (!ImageSingletons.lookup(LocalizationSupport.class).isRegisteredBundleLookup(baseName, locale, control)) {
MissingResourceRegistrationUtils.missingResourceBundle(baseName, locale);
}
return getBundleImpl(callerModule, module, baseName, locale, control);
}

@Alias
private static native Module getCallerModule(Class<?> caller);

@Alias
private static native ClassLoader getLoader(Module module);

@Alias
private static native ResourceBundle getBundleImpl(Module callerModule, Module module, String baseName, Locale locale, ResourceBundle.Control control);
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;
import com.oracle.svm.core.jdk.localization.LocalizationSupport;
import com.oracle.svm.core.jdk.localization.substitutions.modes.JvmLocaleMode;
import com.oracle.svm.core.jdk.localization.substitutions.modes.OptimizedLocaleMode;
import com.oracle.svm.core.jdk.resources.MissingResourceRegistrationUtils;

import sun.util.resources.Bundles.Strategy;

Expand All @@ -48,9 +50,21 @@ final class Target_sun_util_resources_Bundles {
@Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
private static ConcurrentMap<?, ?> cacheList = new ConcurrentHashMap<>();

@TargetElement(onlyWith = OptimizedLocaleMode.class)
@TargetElement(name = "loadBundleOf", onlyWith = OptimizedLocaleMode.class)
@Substitute
private static ResourceBundle loadBundleOf(String baseName, Locale targetLocale, Strategy strategy) {
private static ResourceBundle loadBundleOf_optimized(String baseName, Locale targetLocale, Strategy strategy) {
return ImageSingletons.lookup(LocalizationSupport.class).asOptimizedSupport().getCached(baseName, targetLocale);
}

@TargetElement(onlyWith = JvmLocaleMode.class)
@Alias
private static native ResourceBundle loadBundleOf(String baseName, Locale targetLocale, Strategy strategy);

@Substitute
public static ResourceBundle of(String baseName, Locale locale, Strategy strategy) {
if (!ImageSingletons.lookup(LocalizationSupport.class).isRegisteredBundleLookup(baseName, locale, strategy)) {
MissingResourceRegistrationUtils.missingResourceBundle(baseName, locale);
}
return ImageSingletons.lookup(LocalizationSupport.class).jvmMode() ? loadBundleOf(baseName, locale, strategy) : loadBundleOf_optimized(baseName, locale, strategy);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

import java.nio.file.Files;
import java.nio.file.spi.FileSystemProvider;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
Expand All @@ -40,13 +41,22 @@
public final class MissingResourceRegistrationUtils {

public static void missingResource(String resourcePath) {
MissingResourceRegistrationError exception = new MissingResourceRegistrationError(errorMessage(resourcePath), resourcePath);
MissingResourceRegistrationError exception = new MissingResourceRegistrationError(
errorMessage("resource at path", resourcePath),
resourcePath);
report(exception);
}

private static String errorMessage(String resourcePath) {
public static void missingResourceBundle(String baseName, Locale locale) {
MissingResourceRegistrationError exception = new MissingResourceRegistrationError(
errorMessage("resource bundle with name and locale", baseName + ", " + locale),
baseName + "_" + locale);
report(exception);
}

private static String errorMessage(String type, String resourcePath) {
/* Can't use multi-line strings as they pull in format and bloat "Hello, World!" */
return "The program tried to access the resource at path " +
return "The program tried to access the " + type +
System.lineSeparator() +
System.lineSeparator() +
ERROR_EMPHASIS_INDENT + resourcePath +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -578,10 +578,6 @@ public void prepareBundle(String baseName) {

@Platforms(Platform.HOSTED_ONLY.class)
public void prepareBundle(String baseName, Collection<Locale> wantedLocales) {
if (baseName.isEmpty()) {
return;
}

prepareBundleInternal(baseName, wantedLocales);

String alternativeBundleName = null;
Expand All @@ -599,6 +595,7 @@ public void prepareBundle(String baseName, Collection<Locale> wantedLocales) {
private void prepareBundleInternal(String baseName, Collection<Locale> wantedLocales) {
boolean somethingFound = false;
for (Locale locale : wantedLocales) {
support.registerBundleLookup(baseName, locale);
List<ResourceBundle> resourceBundle;
try {
resourceBundle = ImageSingletons.lookup(ClassLoaderSupport.class).getResourceBundle(baseName, locale);
Expand Down Expand Up @@ -664,7 +661,9 @@ protected void prepareNegativeBundle(String baseName, Locale locale) {

@Platforms(Platform.HOSTED_ONLY.class)
protected void prepareBundle(ResourceBundle bundle, Locale locale) {
prepareBundle(bundle.getBaseBundleName(), bundle, locale);
String baseName = bundle.getBaseBundleName();
support.registerBundleLookup(baseName, locale);
prepareBundle(baseName, bundle, locale);
}

@Platforms(Platform.HOSTED_ONLY.class)
Expand Down

0 comments on commit 9624ce5

Please sign in to comment.