From 6b41ba201dcbfdbfa86109cdb0eff125416bed52 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Wed, 7 Jul 2021 17:07:23 +0200 Subject: [PATCH 01/26] extend the agent to collect the locale and classname of resource bundles --- .../svm/agent/BreakpointInterceptor.java | 23 +++++++++- .../agent/NativeImageAgentJNIHandleSet.java | 5 +++ .../config/ResourceConfiguration.java | 44 +++++++++++++++---- .../configure/trace/ReflectionProcessor.java | 14 ++++-- 4 files changed, 72 insertions(+), 14 deletions(-) diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index 504635073a32..56dab7fedaf7 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -647,11 +647,15 @@ private static boolean getBundleImplJDK8OrEarlier(JNIEnvironment jni, Breakpoint JNIObjectHandle loader = getObjectArgument(2); JNIObjectHandle control = getObjectArgument(3); JNIObjectHandle result = Support.callStaticObjectMethodLLLL(jni, bp.clazz, bp.method, baseName, locale, loader, control); + String className = Tracer.UNKNOWN_VALUE; if (clearException(jni)) { result = nullHandle(); + } else { + className = extractClassName(jni, result); } + String languageTag = fromJniString(jni, callObjectMethod(jni, locale, agent.handles().javaUtilLocaleToLanguageTag)); traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK8OrEarlier", result.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE); + state.getFullStackTraceOrNull(), fromJniString(jni, baseName), languageTag, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, className); return true; } @@ -671,14 +675,29 @@ private static boolean getBundleImplJDK11OrLater(JNIEnvironment jni, Breakpoint JNIObjectHandle locale = getObjectArgument(3); JNIObjectHandle control = getObjectArgument(4); JNIObjectHandle result = Support.callStaticObjectMethodLLLLL(jni, bp.clazz, bp.method, callerModule, module, baseName, locale, control); + String className = Tracer.UNKNOWN_VALUE; if (clearException(jni)) { result = nullHandle(); + } else { + className = extractClassName(jni, result); } + String languageTag = fromJniString(jni, callObjectMethod(jni, locale, agent.handles().javaUtilLocaleToLanguageTag)); traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK11OrLater", result.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE); + state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), languageTag, Tracer.UNKNOWN_VALUE, className); return true; } + private static String extractClassName(JNIEnvironment jni, JNIObjectHandle result) { + JNIObjectHandle clazz = callObjectMethod(jni, result, agent.handles().javaLangObjectGetClass); + if (!clearException(jni)) { + JNIObjectHandle classNameHandle = callObjectMethod(jni, clazz, agent.handles().javaLangClassGetName); + if (!clearException(jni)) { + return fromJniString(jni, classNameHandle); + } + } + return Tracer.UNKNOWN_VALUE; + } + private static boolean loadClass(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { assert experimentalClassLoaderSupport; /* diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java index f4b8e7801fef..a881172d3d05 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java @@ -72,6 +72,8 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet { private JNIMethodId javaLangReflectConstructorDeclaringClassName; + final JNIMethodId javaUtilLocaleToLanguageTag; + NativeImageAgentJNIHandleSet(JNIEnvironment env) { super(env); javaLangClass = newClassGlobalRef(env, "java/lang/Class"); @@ -102,6 +104,9 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet { javaLangIllegalAccessException = newClassGlobalRef(env, "java/lang/IllegalAccessException"); javaLangInvokeWrongMethodTypeException = newClassGlobalRef(env, "java/lang/invoke/WrongMethodTypeException"); javaLangIllegalArgumentException = newClassGlobalRef(env, "java/lang/IllegalArgumentException"); + + JNIObjectHandle javaUtilLocale = findClass(env, "java/util/Locale"); + javaUtilLocaleToLanguageTag = getMethodId(env, javaUtilLocale, "toLanguageTag", "()Ljava/lang/String;", false); } JNIMethodId getJavaLangReflectExecutableGetParameterTypes(JNIEnvironment env) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index 5fb2b8aeb146..c93871e0ddc7 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.util.Set; +import java.util.Comparator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; @@ -60,14 +61,24 @@ public void ignoreResources(ConfigurationCondition condition, String pattern) { @Override public void addResourceBundles(ConfigurationCondition condition, String name) { - configuration.addBundle(condition, name); + // TODO extent the API + configuration.addBundle(condition, "todo", name, "TODO"); } + } + + public static class BundleConfiguration { + public final String baseName; + public final Set locales = ConcurrentHashMap.newKeySet(); + public final Set classNames = ConcurrentHashMap.newKeySet(); + public BundleConfiguration(String baseName) { + this.baseName = baseName; + } } private final ConcurrentMap, Pattern> addedResources = new ConcurrentHashMap<>(); private final ConcurrentMap, Pattern> ignoredResources = new ConcurrentHashMap<>(); - private final Set> bundles = ConcurrentHashMap.newKeySet(); + private final ConcurrentHashMap, BundleConfiguration> bundles = new ConcurrentHashMap<>(); public ResourceConfiguration() { } @@ -75,13 +86,15 @@ public ResourceConfiguration() { public ResourceConfiguration(ResourceConfiguration other) { addedResources.putAll(other.addedResources); ignoredResources.putAll(other.ignoredResources); - bundles.addAll(other.bundles); + bundles.putAll(other.bundles); } public void removeAll(ResourceConfiguration other) { addedResources.keySet().removeAll(other.addedResources.keySet()); ignoredResources.keySet().removeAll(other.ignoredResources.keySet()); - bundles.removeAll(other.bundles); + for (ConditionalElement key : other.bundles.keySet()) { + bundles.remove(key); + } } public void addResourcePattern(ConfigurationCondition condition, String pattern) { @@ -92,8 +105,15 @@ public void ignoreResourcePattern(ConfigurationCondition condition, String patte ignoredResources.computeIfAbsent(new ConditionalElement<>(condition, pattern), p -> Pattern.compile(p.getElement())); } - public void addBundle(ConfigurationCondition condition, String bundle) { - bundles.add(new ConditionalElement<>(condition, bundle)); + public void addBundle(ConfigurationCondition condition, String className, String baseName, String localeTag) { + ConditionalElement key = new ConditionalElement<>(condition, baseName); + BundleConfiguration config = bundles.get(key); + if (config == null) { + config = new BundleConfiguration(baseName); + bundles.put(key, config); + } + config.locales.add(localeTag); + config.classNames.add(className); } public boolean anyResourceMatches(String s) { @@ -115,7 +135,7 @@ public boolean anyResourceMatches(String s) { } public boolean anyBundleMatches(ConfigurationCondition condition, String bundleName) { - return bundles.contains(new ConditionalElement<>(condition, bundleName)); + return bundles.containsKey(new ConditionalElement<>(condition, bundleName)); } @Override @@ -131,10 +151,18 @@ public void printJson(JsonWriter writer) throws IOException { } writer.append('}').append(',').newline(); writer.quote("bundles").append(':'); - JsonPrinter.printCollection(writer, bundles, ConditionalElement.comparator(), (p, w) -> conditionalElementJson(p, w, "name")); + printResourceBundle(writer, bundles.keySet(), Comparator.naturalOrder(), (p, w) -> printResourceBundle(bundles.get(p), w)); writer.unindent().newline().append('}'); } + private void printResourceBundle(BundleConfiguration config, JsonWriter writer) throws IOException { + writer.append('{').quote("name").append(':').quote(config.baseName).append(',').quote("localeTags").append(":"); + JsonPrinter.printCollection(writer, config.locales, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); + writer.append(',').quote("classNames").append(":"); + JsonPrinter.printCollection(writer, config.classNames, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); + writer.append('}'); + } + @Override public boolean isEmpty() { return addedResources.isEmpty() && bundles.isEmpty(); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java index 107919386380..504b2de752b9 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java @@ -246,13 +246,19 @@ public void processEntry(Map entry) { } case "getBundleImplJDK8OrEarlier": { - expectSize(args, 4); - resourceConfiguration.addBundle(condition, (String) args.get(0)); + expectSize(args, 5); + String baseName = (String) args.get(0); + String localeTag = (String) args.get(1); + String className = (String) args.get(4); + resourceConfiguration.addBundle(condition, className, baseName, localeTag); break; } case "getBundleImplJDK11OrLater": { - expectSize(args, 5); - resourceConfiguration.addBundle(condition, (String) args.get(2)); + expectSize(args, 6); + String baseName = (String) args.get(2); + String localeTag = (String) args.get(3); + String className = (String) args.get(5); + resourceConfiguration.addBundle(condition, className, baseName, localeTag); break; } default: From 3c28aa5dec7bbefbded3589dfb2f5369f8ca1943 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Fri, 16 Jul 2021 14:17:01 +0200 Subject: [PATCH 02/26] read & use the whole resource bundle parent chain in native image agent --- .../svm/agent/BreakpointInterceptor.java | 52 ++++++++++++++----- .../agent/NativeImageAgentJNIHandleSet.java | 5 ++ .../config/ResourceConfiguration.java | 21 ++++++-- .../configure/trace/ReflectionProcessor.java | 11 ++-- .../oracle/svm/jvmtiagentbase/Support.java | 4 ++ 5 files changed, 70 insertions(+), 23 deletions(-) diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index 56dab7fedaf7..4de96036a578 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -41,6 +41,7 @@ import static com.oracle.svm.jvmtiagentbase.Support.jvmtiEnv; import static com.oracle.svm.jvmtiagentbase.Support.jvmtiFunctions; import static com.oracle.svm.jvmtiagentbase.Support.newObjectL; +import static com.oracle.svm.jvmtiagentbase.Support.readObjectField; import static com.oracle.svm.jvmtiagentbase.Support.testException; import static com.oracle.svm.jvmtiagentbase.Support.toCString; import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_BREAKPOINT; @@ -59,6 +60,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; +import org.graalvm.collections.Pair; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.UnmanagedMemory; @@ -647,15 +649,15 @@ private static boolean getBundleImplJDK8OrEarlier(JNIEnvironment jni, Breakpoint JNIObjectHandle loader = getObjectArgument(2); JNIObjectHandle control = getObjectArgument(3); JNIObjectHandle result = Support.callStaticObjectMethodLLLL(jni, bp.clazz, bp.method, baseName, locale, loader, control); - String className = Tracer.UNKNOWN_VALUE; + List> bundleInfo = null; if (clearException(jni)) { result = nullHandle(); } else { - className = extractClassName(jni, result); + bundleInfo = extractBundleInfo(jni, result); } - String languageTag = fromJniString(jni, callObjectMethod(jni, locale, agent.handles().javaUtilLocaleToLanguageTag)); + String languageTag = readLocaleTag(jni, locale); traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK8OrEarlier", result.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), fromJniString(jni, baseName), languageTag, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, className); + state.getFullStackTraceOrNull(), fromJniString(jni, baseName), languageTag, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo); return true; } @@ -675,27 +677,49 @@ private static boolean getBundleImplJDK11OrLater(JNIEnvironment jni, Breakpoint JNIObjectHandle locale = getObjectArgument(3); JNIObjectHandle control = getObjectArgument(4); JNIObjectHandle result = Support.callStaticObjectMethodLLLLL(jni, bp.clazz, bp.method, callerModule, module, baseName, locale, control); - String className = Tracer.UNKNOWN_VALUE; + List> bundleInfo = null; if (clearException(jni)) { result = nullHandle(); } else { - className = extractClassName(jni, result); + bundleInfo = extractBundleInfo(jni, result); } - String languageTag = fromJniString(jni, callObjectMethod(jni, locale, agent.handles().javaUtilLocaleToLanguageTag)); + String languageTag = readLocaleTag(jni, locale); traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK11OrLater", result.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), languageTag, Tracer.UNKNOWN_VALUE, className); + state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), languageTag, Tracer.UNKNOWN_VALUE, bundleInfo); return true; } - private static String extractClassName(JNIEnvironment jni, JNIObjectHandle result) { - JNIObjectHandle clazz = callObjectMethod(jni, result, agent.handles().javaLangObjectGetClass); - if (!clearException(jni)) { - JNIObjectHandle classNameHandle = callObjectMethod(jni, clazz, agent.handles().javaLangClassGetName); + private static String readLocaleTag(JNIEnvironment jni, JNIObjectHandle locale) { + return fromJniString(jni, callObjectMethod(jni, locale, agent.handles().javaUtilLocaleToLanguageTag)); + } + + private static List> extractBundleInfo(JNIEnvironment jni, JNIObjectHandle result) { + List> res = new ArrayList<>(); + JNIObjectHandle curr = result; + while (!nullHandle().equal(curr)) { + JNIObjectHandle locale = callObjectMethod(jni, curr, agent.handles().javaUtilResourceBundleGetLocale); + if (clearException(jni)) { + return null; + } + String localeTag = readLocaleTag(jni, locale); + JNIObjectHandle clazz = callObjectMethod(jni, result, agent.handles().javaLangObjectGetClass); if (!clearException(jni)) { - return fromJniString(jni, classNameHandle); + JNIObjectHandle classNameHandle = callObjectMethod(jni, clazz, agent.handles().javaLangClassGetName); + if (!clearException(jni)) { + res.add(Pair.create(fromJniString(jni, classNameHandle), localeTag)); + } } + curr = getParent(jni, curr); + } + return res; + } + + private static JNIObjectHandle getParent(JNIEnvironment jni, JNIObjectHandle curr) { + JNIObjectHandle parent = readObjectField(jni, curr, agent.handles().javaUtilResourceBundleParentField); + if (!clearException(jni)) { + return parent; } - return Tracer.UNKNOWN_VALUE; + return nullHandle(); } private static boolean loadClass(JNIEnvironment jni, Breakpoint bp, InterceptedState state) { diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java index a881172d3d05..7001e6ba4114 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java @@ -73,6 +73,8 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet { private JNIMethodId javaLangReflectConstructorDeclaringClassName; final JNIMethodId javaUtilLocaleToLanguageTag; + final JNIFieldId javaUtilResourceBundleParentField; + final JNIMethodId javaUtilResourceBundleGetLocale; NativeImageAgentJNIHandleSet(JNIEnvironment env) { super(env); @@ -107,6 +109,9 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet { JNIObjectHandle javaUtilLocale = findClass(env, "java/util/Locale"); javaUtilLocaleToLanguageTag = getMethodId(env, javaUtilLocale, "toLanguageTag", "()Ljava/lang/String;", false); + JNIObjectHandle javaUtilResourceBundle = findClass(env, "java/util/ResourceBundle"); + javaUtilResourceBundleParentField = getFieldId(env, javaUtilResourceBundle, "parent", "Ljava/util/ResourceBundle;", false); + javaUtilResourceBundleGetLocale = getMethodId(env, javaUtilResourceBundle, "getLocale", "()Ljava/util/Locale;", false); } JNIMethodId getJavaLangReflectExecutableGetParameterTypes(JNIEnvironment env) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index c93871e0ddc7..7eabe45708c0 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -25,6 +25,7 @@ package com.oracle.svm.configure.config; import java.io.IOException; +import java.util.List; import java.util.Set; import java.util.Comparator; import java.util.concurrent.ConcurrentHashMap; @@ -38,9 +39,12 @@ import com.oracle.svm.configure.json.JsonWriter; import com.oracle.svm.core.configure.ConditionalElement; import com.oracle.svm.core.configure.ResourcesRegistry; +import org.graalvm.collections.Pair; public class ResourceConfiguration implements ConfigurationBase { + private static final String PROPERTY_BUNDLE = "java.util.PropertyResourceBundle"; + public static class ParserAdapter implements ResourcesRegistry { private final ResourceConfiguration configuration; @@ -62,7 +66,7 @@ public void ignoreResources(ConfigurationCondition condition, String pattern) { @Override public void addResourceBundles(ConfigurationCondition condition, String name) { // TODO extent the API - configuration.addBundle(condition, "todo", name, "TODO"); + configuration.addBundle(condition, Collections.emptyList(), name, "TODO"); } } @@ -105,15 +109,22 @@ public void ignoreResourcePattern(ConfigurationCondition condition, String patte ignoredResources.computeIfAbsent(new ConditionalElement<>(condition, pattern), p -> Pattern.compile(p.getElement())); } - public void addBundle(ConfigurationCondition condition, String className, String baseName, String localeTag) { + public void addBundle(ConfigurationCondition condition, List> bundleInfo, String baseName, String queriedLocaleTag) { ConditionalElement key = new ConditionalElement<>(condition, baseName); BundleConfiguration config = bundles.get(key); if (config == null) { config = new BundleConfiguration(baseName); bundles.put(key, config); } - config.locales.add(localeTag); - config.classNames.add(className); + for (Pair pair : bundleInfo) { + String className = pair.getLeft(); + String localeTag = pair.getRight(); + if (!className.equals(PROPERTY_BUNDLE)) { + config.classNames.add(className); + } else { + config.locales.add(localeTag); + } + } } public boolean anyResourceMatches(String s) { @@ -156,7 +167,7 @@ public void printJson(JsonWriter writer) throws IOException { } private void printResourceBundle(BundleConfiguration config, JsonWriter writer) throws IOException { - writer.append('{').quote("name").append(':').quote(config.baseName).append(',').quote("localeTags").append(":"); + writer.append('{').quote("name").append(':').quote(config.baseName).append(',').quote("locales").append(":"); JsonPrinter.printCollection(writer, config.locales, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); writer.append(',').quote("classNames").append(":"); JsonPrinter.printCollection(writer, config.classNames, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java index 504b2de752b9..3a7a7196d576 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java @@ -32,6 +32,7 @@ import java.util.Map; import java.util.regex.Pattern; +import org.graalvm.collections.Pair; import org.graalvm.compiler.phases.common.LazyValue; import org.graalvm.nativeimage.impl.ConfigurationCondition; @@ -249,16 +250,18 @@ public void processEntry(Map entry) { expectSize(args, 5); String baseName = (String) args.get(0); String localeTag = (String) args.get(1); - String className = (String) args.get(4); - resourceConfiguration.addBundle(condition, className, baseName, localeTag); + @SuppressWarnings("unchecked") + List> bundleInfo = (List>) args.get(4); + resourceConfiguration.addBundle(condition, bundleInfo, baseName, localeTag); break; } case "getBundleImplJDK11OrLater": { expectSize(args, 6); String baseName = (String) args.get(2); String localeTag = (String) args.get(3); - String className = (String) args.get(5); - resourceConfiguration.addBundle(condition, className, baseName, localeTag); + @SuppressWarnings("unchecked") + List> bundleInfo = (List>) args.get(5); + resourceConfiguration.addBundle(condition, bundleInfo, baseName, localeTag); break; } default: diff --git a/substratevm/src/com.oracle.svm.jvmtiagentbase/src/com/oracle/svm/jvmtiagentbase/Support.java b/substratevm/src/com.oracle.svm.jvmtiagentbase/src/com/oracle/svm/jvmtiagentbase/Support.java index 1d743a41d801..82f0aa7a9715 100644 --- a/substratevm/src/com.oracle.svm.jvmtiagentbase/src/com/oracle/svm/jvmtiagentbase/Support.java +++ b/substratevm/src/com.oracle.svm.jvmtiagentbase/src/com/oracle/svm/jvmtiagentbase/Support.java @@ -264,6 +264,10 @@ public static JNIObjectHandle handleException(JNIEnvironment localEnv, boolean c return nullHandle(); } + public static JNIObjectHandle readObjectField(JNIEnvironment env, JNIObjectHandle obj, JNIFieldId fieldId) { + return jniFunctions().getGetObjectField().invoke(env, obj, fieldId); + } + /* * We use the Call*A functions that take a jvalue* for the Java arguments because that doesn't * require that calling conventions for a varargs call are the same as those for a regular call From 0d2d414dc8140fdfa331a780ef86624c165344ba Mon Sep 17 00:00:00 2001 From: David Kozak Date: Mon, 19 Jul 2021 11:57:35 +0200 Subject: [PATCH 03/26] use new locale configuration in the image --- .../config/ResourceConfigurationTest.java | 14 +++++- .../config/ResourceConfiguration.java | 43 +++++++++++++++++- .../configure/trace/ReflectionProcessor.java | 8 ++-- .../ResourceConfigurationParser.java | 45 +++++++++++++++++-- .../svm/core/configure/ResourcesRegistry.java | 9 +++- .../jdk/localization/LocalizationSupport.java | 23 ++++++++++ .../oracle/svm/hosted/ResourcesFeature.java | 18 +++++++- 7 files changed, 147 insertions(+), 13 deletions(-) diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java index 808f84764b44..155c7e435146 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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 @@ -28,8 +28,10 @@ import java.io.IOException; import java.io.PipedReader; import java.io.PipedWriter; +import java.util.Collection; import java.util.LinkedList; import java.util.List; +import java.util.Locale; import org.graalvm.nativeimage.impl.ConfigurationCondition; import org.junit.Assert; @@ -99,6 +101,16 @@ public void ignoreResources(ConfigurationCondition condition, String pattern) { @Override public void addResourceBundles(ConfigurationCondition condition, String name) { } + + @Override + public void addClassBasedResourceBundle(String className) { + + } + + @Override + public void addResourceBundles(String basename, Collection locales) { + + } }; ResourceConfigurationParser rcp = new ResourceConfigurationParser(registry, true); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index 7eabe45708c0..5cf7d0468afa 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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 @@ -25,7 +25,10 @@ package com.oracle.svm.configure.config; import java.io.IOException; +import java.util.Collection; +import java.util.Comparator; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.Comparator; import java.util.concurrent.ConcurrentHashMap; @@ -68,6 +71,29 @@ public void addResourceBundles(ConfigurationCondition condition, String name) { // TODO extent the API configuration.addBundle(condition, Collections.emptyList(), name, "TODO"); } + + public void addResourceBundles(String baseName) { + configuration.addBundle(baseName); + } + + @Override + public void addClassBasedResourceBundle(String className) { + configuration.addClassBasedResourceBundle(className); + } + + @Override + public void addResourceBundles(String basename, Collection locales) { + configuration.addBundle(basename, locales); + } + } + + private void addClassBasedResourceBundle(String className) { + String baseName = className; + int split = baseName.lastIndexOf('_'); + if (split != -1) { + baseName = baseName.substring(0, split); + } + getOrCreateBundleConfig(baseName).classNames.add(className); } public static class BundleConfiguration { @@ -109,6 +135,17 @@ public void ignoreResourcePattern(ConfigurationCondition condition, String patte ignoredResources.computeIfAbsent(new ConditionalElement<>(condition, pattern), p -> Pattern.compile(p.getElement())); } + private void addBundle(String basename, Collection locales) { + BundleConfiguration config = getOrCreateBundleConfig(basename); + for (Locale locale : locales) { + config.locales.add(locale.toLanguageTag()); + } + } + + private void addBundle(String baseName) { + getOrCreateBundleConfig(baseName); + } + public void addBundle(ConfigurationCondition condition, List> bundleInfo, String baseName, String queriedLocaleTag) { ConditionalElement key = new ConditionalElement<>(condition, baseName); BundleConfiguration config = bundles.get(key); @@ -127,6 +164,10 @@ public void addBundle(ConfigurationCondition condition, List new BundleConfiguration(baseName)); + } + public boolean anyResourceMatches(String s) { /* * Naive -- if the need arises, we could match in the order of most frequently matched diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java index 3a7a7196d576..03180d57b9c1 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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 @@ -249,19 +249,17 @@ public void processEntry(Map entry) { case "getBundleImplJDK8OrEarlier": { expectSize(args, 5); String baseName = (String) args.get(0); - String localeTag = (String) args.get(1); @SuppressWarnings("unchecked") List> bundleInfo = (List>) args.get(4); - resourceConfiguration.addBundle(condition, bundleInfo, baseName, localeTag); + resourceConfiguration.addBundle(condition, bundleInfo, baseName); break; } case "getBundleImplJDK11OrLater": { expectSize(args, 6); String baseName = (String) args.get(2); - String localeTag = (String) args.get(3); @SuppressWarnings("unchecked") List> bundleInfo = (List>) args.get(5); - resourceConfiguration.addBundle(condition, bundleInfo, baseName, localeTag); + resourceConfiguration.addBundle(condition, bundleInfo, baseName); break; } default: diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index 628bd0de1c32..4b023f52702b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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 @@ -27,12 +27,18 @@ import java.io.IOException; import java.io.Reader; import java.util.Collections; +import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Collectors; import java.util.function.BiConsumer; import org.graalvm.nativeimage.impl.ConfigurationCondition; +import com.oracle.svm.core.jdk.localization.LocalizationSupport; import com.oracle.svm.core.util.json.JSONParser; public class ResourceConfigurationParser extends ConfigurationParser { @@ -88,12 +94,45 @@ private void parseTopLevelObject(Map obj) { } if (bundlesObject != null) { List bundles = asList(bundlesObject, "Attribute 'bundles' must be a list of bundles"); - for (Object object : bundles) { - parseStringEntry(object, "name", registry::addResourceBundles, "bundle descriptor object", "'bundles' list"); + for (Object bundle : bundles) { + parseBundle(bundle); } } } + private void parseBundle(Object bundle) { + Map resource = asMap(bundle, "Elements of 'bundles' list must be a bundle descriptor object"); + checkAttributes(resource, "bundle descriptor object", Collections.singletonList("name"), Arrays.asList("locales", "classNames")); + String basename = asString(resource.get("name"), "Missing attribute 'name' in bundle descriptor object"); + Object locales = resource.get("locales"); + if (locales != null) { + List asList = asList(locales, "Attribute 'locales' must be a list of locales") + .stream() + .map(ResourceConfigurationParser::parseLocale) + .collect(Collectors.toList()); + registry.addResourceBundles(basename, asList); + } else { + registry.addResourceBundles(basename); + } + Object classNames = resource.get("classNames"); + if (classNames != null) { + List asList = asList(classNames, "Attribute 'classNames' must be a list of classes"); + for (Object o : asList) { + String className = asString(o, "Elements of 'classNames' must of strings."); + registry.addClassBasedResourceBundle(className); + } + } + } + + private static Locale parseLocale(Object input) { + String localeTag = asString(input, "Elements of 'locales' must be strings."); + Locale locale = LocalizationSupport.parseLocaleFromTag(localeTag); + if (locale == null) { + throw new JSONParserException(localeTag + " is not a valid locale tag"); + } + return locale; + } + private void parseStringEntry(Object data, String valueKey, BiConsumer resourceRegistry, String expectedType, String parentType) { Map resource = asMap(data, "Elements of " + parentType + " must be a " + expectedType); checkAttributes(resource, "resource and resource bundle descriptor object", Collections.singletonList(valueKey), Collections.singletonList(CONDITIONAL_KEY)); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java index 4fdb94d82f29..2d2084fb9239 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2021, 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 @@ -24,7 +24,8 @@ */ package com.oracle.svm.core.configure; -import org.graalvm.nativeimage.impl.ConfigurationCondition; +import java.util.Collection; +import java.util.Locale; public interface ResourcesRegistry { @@ -63,4 +64,8 @@ default void addResourceBundles(String name) { void ignoreResources(ConfigurationCondition condition, String pattern); void addResourceBundles(ConfigurationCondition condition, String name); + + void addClassBasedResourceBundle(String className); + + void addResourceBundles(String basename, Collection locales); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index cca533de556c..e9f5df7ace67 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -27,6 +27,7 @@ import java.nio.charset.Charset; import java.util.HashMap; +import java.util.IllformedLocaleException; import java.util.Locale; import java.util.Map; import java.util.PropertyResourceBundle; @@ -122,4 +123,26 @@ public boolean shouldSubstituteLoadLookup(String className) { public void prepareNonCompliant(Class clazz) { /*- By default, there is nothing to do */ } + + /** + * @return locale for given tag or null for invalid ones + */ + public static Locale parseLocaleFromTag(String tag) { + try { + return new Locale.Builder().setLanguageTag(tag).build(); + } catch (IllformedLocaleException ex) { + /*- Custom made locales consisting of at most three parts separated by '-' are also supported */ + String[] parts = tag.split("-"); + switch (parts.length) { + case 1: + return new Locale(parts[0]); + case 2: + return new Locale(parts[0], parts[1]); + case 3: + return new Locale(parts[0], parts[1], parts[2]); + default: + return null; + } + } + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index 767adf4bba53..600841ba4230 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2021, 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 @@ -35,12 +35,16 @@ import java.nio.file.Path; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -50,10 +54,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import com.oracle.svm.core.util.ClasspathUtils; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionType; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.home.HomeFinder; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.impl.ConfigurationCondition; @@ -160,6 +166,16 @@ public void addResourceBundles(ConfigurationCondition condition, String name) { } registerConditionalConfiguration(condition, () -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(name)); } + + @Override + public void addClassBasedResourceBundle(String className) { + ImageSingletons.lookup(LocalizationFeature.class).addClassBasedResourceBundle(className); + } + + @Override + public void addResourceBundles(String basename, Collection locales) { + ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(basename, locales); + } } @Override From 7203f73a6220145c898134541717c5b0d8090fbc Mon Sep 17 00:00:00 2001 From: David Kozak Date: Mon, 26 Jul 2021 18:53:26 +0200 Subject: [PATCH 04/26] only include 'locales' and 'classnames' field of a resource bundle config entry if they are non-empty --- .../svm/configure/config/ResourceConfiguration.java | 13 +++++++++---- .../core/configure/ResourceConfigurationParser.java | 3 +++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index 5cf7d0468afa..36ca91c0b6a1 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -208,10 +208,15 @@ public void printJson(JsonWriter writer) throws IOException { } private void printResourceBundle(BundleConfiguration config, JsonWriter writer) throws IOException { - writer.append('{').quote("name").append(':').quote(config.baseName).append(',').quote("locales").append(":"); - JsonPrinter.printCollection(writer, config.locales, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); - writer.append(',').quote("classNames").append(":"); - JsonPrinter.printCollection(writer, config.classNames, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); + writer.append('{').quote("name").append(':').quote(config.baseName); + if (!config.locales.isEmpty()) { + writer.append(',').quote("locales").append(":"); + JsonPrinter.printCollection(writer, config.locales, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); + } + if (!config.classNames.isEmpty()) { + writer.append(',').quote("classNames").append(":"); + JsonPrinter.printCollection(writer, config.classNames, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); + } writer.append('}'); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index 4b023f52702b..38f1d065dfbc 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -39,6 +39,7 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.jdk.localization.LocalizationSupport; +import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.json.JSONParser; public class ResourceConfigurationParser extends ConfigurationParser { @@ -110,6 +111,7 @@ private void parseBundle(Object bundle) { .stream() .map(ResourceConfigurationParser::parseLocale) .collect(Collectors.toList()); + UserError.guarantee(!asList.isEmpty(), "List of locales for %s is empty", basename); registry.addResourceBundles(basename, asList); } else { registry.addResourceBundles(basename); @@ -117,6 +119,7 @@ private void parseBundle(Object bundle) { Object classNames = resource.get("classNames"); if (classNames != null) { List asList = asList(classNames, "Attribute 'classNames' must be a list of classes"); + UserError.guarantee(!asList.isEmpty(), "List of classnames for %s is empty", basename); for (Object o : asList) { String className = asString(o, "Elements of 'classNames' must of strings."); registry.addClassBasedResourceBundle(className); From b6b7daf72479171710c91baee5ba0fcc3ff06f64 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Tue, 27 Jul 2021 16:12:39 +0200 Subject: [PATCH 05/26] enable tracing to get more debug hints from the gates --- .../oracle/svm/core/configure/ResourceConfigurationParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index 38f1d065dfbc..bbd16716b443 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -119,7 +119,7 @@ private void parseBundle(Object bundle) { Object classNames = resource.get("classNames"); if (classNames != null) { List asList = asList(classNames, "Attribute 'classNames' must be a list of classes"); - UserError.guarantee(!asList.isEmpty(), "List of classnames for %s is empty", basename); + UserError.guarantee(!asList.isEmpty(), "List of classNames for %s is empty", basename); for (Object o : asList) { String className = asString(o, "Elements of 'classNames' must of strings."); registry.addClassBasedResourceBundle(className); From e75297652e59226dcff97bd177caf536d769fcae Mon Sep 17 00:00:00 2001 From: David Kozak Date: Tue, 27 Jul 2021 18:16:46 +0200 Subject: [PATCH 06/26] fix style issue - make ResourceConfiguration.printResourceBundle static --- .../com/oracle/svm/configure/config/ResourceConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index 36ca91c0b6a1..e0ee5aa02b9f 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -207,7 +207,7 @@ public void printJson(JsonWriter writer) throws IOException { writer.unindent().newline().append('}'); } - private void printResourceBundle(BundleConfiguration config, JsonWriter writer) throws IOException { + private static void printResourceBundle(BundleConfiguration config, JsonWriter writer) throws IOException { writer.append('{').quote("name").append(':').quote(config.baseName); if (!config.locales.isEmpty()) { writer.append(',').quote("locales").append(":"); From 62302f9c9a0331a63dc15615ea99901e59377994 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Thu, 12 Aug 2021 10:45:55 +0200 Subject: [PATCH 07/26] fix registering class based bundles in Java 8 --- .../svm/core/jdk/localization/LocalizationSupport.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index e9f5df7ace67..0847bd9fd792 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -145,4 +145,9 @@ public static Locale parseLocaleFromTag(String tag) { } } } + + public void addClassBasedResourceBundle(Class bundleClass) { + RuntimeReflection.register(bundleClass); + RuntimeReflection.registerForReflectiveInstantiation(bundleClass); + } } From b24db311d974b5259feb7a0bfc36583759adbb65 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Thu, 12 Aug 2021 15:33:31 +0200 Subject: [PATCH 08/26] only register bundle in all locales if no specific locales or classnames are provided --- .../svm/core/configure/ResourceConfigurationParser.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index bbd16716b443..4661f33a96f5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -113,8 +113,6 @@ private void parseBundle(Object bundle) { .collect(Collectors.toList()); UserError.guarantee(!asList.isEmpty(), "List of locales for %s is empty", basename); registry.addResourceBundles(basename, asList); - } else { - registry.addResourceBundles(basename); } Object classNames = resource.get("classNames"); if (classNames != null) { @@ -125,6 +123,10 @@ private void parseBundle(Object bundle) { registry.addClassBasedResourceBundle(className); } } + if (locales == null && classNames == null) { + // register in every locale + registry.addResourceBundles(basename); + } } private static Locale parseLocale(Object input) { From ef98bc8cf54fef133d240092466a75359043814d Mon Sep 17 00:00:00 2001 From: David Kozak Date: Thu, 12 Aug 2021 16:16:15 +0200 Subject: [PATCH 09/26] remember the basename of the class based bundle, so that it can be looked up by it --- .../test/config/ResourceConfigurationTest.java | 2 +- .../svm/configure/config/ResourceConfiguration.java | 13 ++++--------- .../core/configure/ResourceConfigurationParser.java | 2 +- .../svm/core/configure/ResourcesRegistry.java | 2 +- .../core/jdk/localization/LocalizationSupport.java | 2 +- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 4 ++-- 6 files changed, 10 insertions(+), 15 deletions(-) diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java index 155c7e435146..df0863b629f0 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java @@ -103,7 +103,7 @@ public void addResourceBundles(ConfigurationCondition condition, String name) { } @Override - public void addClassBasedResourceBundle(String className) { + public void addClassBasedResourceBundle(String basename, String className) { } diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index e0ee5aa02b9f..71c2005dbb47 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -77,8 +77,8 @@ public void addResourceBundles(String baseName) { } @Override - public void addClassBasedResourceBundle(String className) { - configuration.addClassBasedResourceBundle(className); + public void addClassBasedResourceBundle(String basename, String className) { + configuration.addClassBasedResourceBundle(basename, className); } @Override @@ -87,13 +87,8 @@ public void addResourceBundles(String basename, Collection locales) { } } - private void addClassBasedResourceBundle(String className) { - String baseName = className; - int split = baseName.lastIndexOf('_'); - if (split != -1) { - baseName = baseName.substring(0, split); - } - getOrCreateBundleConfig(baseName).classNames.add(className); + private void addClassBasedResourceBundle(String basename, String className) { + getOrCreateBundleConfig(basename).classNames.add(className); } public static class BundleConfiguration { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index 4661f33a96f5..18ec6361564d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -120,7 +120,7 @@ private void parseBundle(Object bundle) { UserError.guarantee(!asList.isEmpty(), "List of classNames for %s is empty", basename); for (Object o : asList) { String className = asString(o, "Elements of 'classNames' must of strings."); - registry.addClassBasedResourceBundle(className); + registry.addClassBasedResourceBundle(basename, className); } } if (locales == null && classNames == null) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java index 2d2084fb9239..e6b30e36ce5d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java @@ -65,7 +65,7 @@ default void addResourceBundles(String name) { void addResourceBundles(ConfigurationCondition condition, String name); - void addClassBasedResourceBundle(String className); + void addClassBasedResourceBundle(String basename, String className); void addResourceBundles(String basename, Collection locales); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index 0847bd9fd792..94ace13050b9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -146,7 +146,7 @@ public static Locale parseLocaleFromTag(String tag) { } } - public void addClassBasedResourceBundle(Class bundleClass) { + public void addClassBasedResourceBundle(String basename, Class bundleClass) { RuntimeReflection.register(bundleClass); RuntimeReflection.registerForReflectiveInstantiation(bundleClass); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index 600841ba4230..f3e54f7cf8c8 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -168,8 +168,8 @@ public void addResourceBundles(ConfigurationCondition condition, String name) { } @Override - public void addClassBasedResourceBundle(String className) { - ImageSingletons.lookup(LocalizationFeature.class).addClassBasedResourceBundle(className); + public void addClassBasedResourceBundle(String basename, String className) { + ImageSingletons.lookup(LocalizationFeature.class).addClassBasedResourceBundle(basename, className); } @Override From faf9026e85c05c59afbe1c652d20488e71f430e7 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Fri, 13 Aug 2021 11:23:43 +0200 Subject: [PATCH 10/26] fix another style issue bundle basename is only needed for the OptimizedLocalizationSupport support --- .../oracle/svm/core/jdk/localization/LocalizationSupport.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index 94ace13050b9..b639ccbdfb88 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -146,7 +146,7 @@ public static Locale parseLocaleFromTag(String tag) { } } - public void addClassBasedResourceBundle(String basename, Class bundleClass) { + public void addClassBasedResourceBundle(@SuppressWarnings("unused") String basename, Class bundleClass) { RuntimeReflection.register(bundleClass); RuntimeReflection.registerForReflectiveInstantiation(bundleClass); } From 30715d3aaa6e50facda38c1528ef92796271f824 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Fri, 13 Aug 2021 12:05:02 +0200 Subject: [PATCH 11/26] refactoring: changing the naming patterns for localization --- .../test/config/ResourceConfigurationTest.java | 4 ++-- .../svm/configure/config/ResourceConfiguration.java | 10 +++++----- .../core/configure/ResourceConfigurationParser.java | 8 ++++---- .../oracle/svm/core/configure/ResourcesRegistry.java | 4 ++-- .../svm/core/jdk/localization/LocalizationSupport.java | 2 +- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 7 ++++++- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java index df0863b629f0..852bd6039696 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java @@ -103,12 +103,12 @@ public void addResourceBundles(ConfigurationCondition condition, String name) { } @Override - public void addClassBasedResourceBundle(String basename, String className) { + public void addResourceBundle(String basename, Collection locales) { } @Override - public void addResourceBundles(String basename, Collection locales) { + public void addClassResourceBundle(String basename, String className) { } }; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index 71c2005dbb47..c3ae3ba3436f 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -77,17 +77,17 @@ public void addResourceBundles(String baseName) { } @Override - public void addClassBasedResourceBundle(String basename, String className) { - configuration.addClassBasedResourceBundle(basename, className); + public void addResourceBundle(String basename, Collection locales) { + configuration.addBundle(basename, locales); } @Override - public void addResourceBundles(String basename, Collection locales) { - configuration.addBundle(basename, locales); + public void addClassResourceBundle(String basename, String className) { + configuration.addClassResourceBundle(basename, className); } } - private void addClassBasedResourceBundle(String basename, String className) { + private void addClassResourceBundle(String basename, String className) { getOrCreateBundleConfig(basename).classNames.add(className); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index 18ec6361564d..72c7dfd59064 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -112,7 +112,7 @@ private void parseBundle(Object bundle) { .map(ResourceConfigurationParser::parseLocale) .collect(Collectors.toList()); UserError.guarantee(!asList.isEmpty(), "List of locales for %s is empty", basename); - registry.addResourceBundles(basename, asList); + registry.addResourceBundle(basename, asList); } Object classNames = resource.get("classNames"); if (classNames != null) { @@ -120,12 +120,12 @@ private void parseBundle(Object bundle) { UserError.guarantee(!asList.isEmpty(), "List of classNames for %s is empty", basename); for (Object o : asList) { String className = asString(o, "Elements of 'classNames' must of strings."); - registry.addClassBasedResourceBundle(basename, className); + registry.addClassResourceBundle(basename, className); } } if (locales == null && classNames == null) { - // register in every locale - registry.addResourceBundles(basename); + /*- If nothing more precise is specified, register in every included locale */ + registry.addResourceBundle(basename); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java index e6b30e36ce5d..fc0c3802ba81 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java @@ -65,7 +65,7 @@ default void addResourceBundles(String name) { void addResourceBundles(ConfigurationCondition condition, String name); - void addClassBasedResourceBundle(String basename, String className); + void addResourceBundle(String basename, Collection locales); - void addResourceBundles(String basename, Collection locales); + void addClassResourceBundle(String basename, String className); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index b639ccbdfb88..0678a5f1819f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -146,7 +146,7 @@ public static Locale parseLocaleFromTag(String tag) { } } - public void addClassBasedResourceBundle(@SuppressWarnings("unused") String basename, Class bundleClass) { + public void prepareClassResourceBundle(@SuppressWarnings("unused") String basename, Class bundleClass) { RuntimeReflection.register(bundleClass); RuntimeReflection.registerForReflectiveInstantiation(bundleClass); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index f3e54f7cf8c8..edd16bffa9c7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -173,9 +173,14 @@ public void addClassBasedResourceBundle(String basename, String className) { } @Override - public void addResourceBundles(String basename, Collection locales) { + public void addResourceBundle(String basename, Collection locales) { ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(basename, locales); } + + @Override + public void addClassResourceBundle(String basename, String className) { + ImageSingletons.lookup(LocalizationFeature.class).prepareClassResourceBundle(basename, className); + } } @Override From 165cce34a7c64201647c5bbe16ef313320eb6e5d Mon Sep 17 00:00:00 2001 From: David Kozak Date: Fri, 13 Aug 2021 15:36:39 +0200 Subject: [PATCH 12/26] preprocess class based bundles correctly for OptimizedLocalizationSupport --- .../BundleContentSubstitutedLocalizationSupport.java | 6 ++++++ .../svm/core/jdk/localization/LocalizationSupport.java | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java index b46692dd9afd..4637aaba3638 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java @@ -88,6 +88,12 @@ protected void onBundlePrepared(ResourceBundle bundle) { } } + @Override + @Platforms(Platform.HOSTED_ONLY.class) + protected void onClassBundlePrepared(Class bundleClass) { + prepareNonCompliant(bundleClass); + } + @Platforms(Platform.HOSTED_ONLY.class) private void storeBundleContentOf(ResourceBundle bundle) { GraalError.guarantee(isBundleSupported(bundle), "Unsupported bundle %s of type %s", bundle, bundle.getClass()); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java index 0678a5f1819f..01fdd24b8087 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/LocalizationSupport.java @@ -113,6 +113,12 @@ protected void onBundlePrepared(@SuppressWarnings("unused") ResourceBundle bundl } + @Platforms(Platform.HOSTED_ONLY.class) + @SuppressWarnings("unused") + protected void onClassBundlePrepared(Class bundleClass) { + + } + @SuppressWarnings("unused") public boolean shouldSubstituteLoadLookup(String className) { /*- By default, keep the original code */ @@ -149,5 +155,6 @@ public static Locale parseLocaleFromTag(String tag) { public void prepareClassResourceBundle(@SuppressWarnings("unused") String basename, Class bundleClass) { RuntimeReflection.register(bundleClass); RuntimeReflection.registerForReflectiveInstantiation(bundleClass); + onClassBundlePrepared(bundleClass); } } From 7ce690bc977a0f4ff9b2f800030d0135db83183a Mon Sep 17 00:00:00 2001 From: David Kozak Date: Mon, 16 Aug 2021 14:52:45 +0200 Subject: [PATCH 13/26] store the bundles as delayed in BundleContentSubstitutedLocalizationSupport only if they have the right type --- .../BundleContentSubstitutedLocalizationSupport.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java index 4637aaba3638..e78c806685c2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java @@ -91,7 +91,9 @@ protected void onBundlePrepared(ResourceBundle bundle) { @Override @Platforms(Platform.HOSTED_ONLY.class) protected void onClassBundlePrepared(Class bundleClass) { - prepareNonCompliant(bundleClass); + if (isBundleSupported(bundleClass)) { + prepareNonCompliant(bundleClass); + } } @Platforms(Platform.HOSTED_ONLY.class) @@ -121,7 +123,12 @@ public Map getBundleContentOf(Object bundle) { @Platforms(Platform.HOSTED_ONLY.class) public boolean isBundleSupported(ResourceBundle bundle) { - return bundle instanceof ListResourceBundle || bundle instanceof OpenListResourceBundle || bundle instanceof ParallelListResourceBundle; + return isBundleSupported(bundle.getClass()); + } + + @Platforms(Platform.HOSTED_ONLY.class) + private boolean isBundleSupported(Class bundleClass) { + return ListResourceBundle.class.isAssignableFrom(bundleClass) || OpenListResourceBundle.class.isAssignableFrom(bundleClass) || ParallelListResourceBundle.class.isAssignableFrom(bundleClass); } @Platforms(Platform.HOSTED_ONLY.class) From c6b4c086f2191d669c36e02fc2f64d52a63b305e Mon Sep 17 00:00:00 2001 From: David Kozak Date: Mon, 16 Aug 2021 18:55:58 +0200 Subject: [PATCH 14/26] fix style issues --- .../BundleContentSubstitutedLocalizationSupport.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java index e78c806685c2..c0cadd4bbfa2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/BundleContentSubstitutedLocalizationSupport.java @@ -122,12 +122,12 @@ public Map getBundleContentOf(Object bundle) { } @Platforms(Platform.HOSTED_ONLY.class) - public boolean isBundleSupported(ResourceBundle bundle) { + private static boolean isBundleSupported(ResourceBundle bundle) { return isBundleSupported(bundle.getClass()); } @Platforms(Platform.HOSTED_ONLY.class) - private boolean isBundleSupported(Class bundleClass) { + private static boolean isBundleSupported(Class bundleClass) { return ListResourceBundle.class.isAssignableFrom(bundleClass) || OpenListResourceBundle.class.isAssignableFrom(bundleClass) || ParallelListResourceBundle.class.isAssignableFrom(bundleClass); } From 2fd2a55867fd52c6c493b4a93ea5c62637b965b9 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Tue, 24 Aug 2021 14:19:55 +0200 Subject: [PATCH 15/26] fixes after rebase --- .../oracle/svm/core/configure/ResourceConfigurationParser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index 72c7dfd59064..b16185a16082 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -28,7 +28,6 @@ import java.io.Reader; import java.util.Collections; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.Map; @@ -41,6 +40,7 @@ import com.oracle.svm.core.jdk.localization.LocalizationSupport; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.json.JSONParser; +import com.oracle.svm.core.util.json.JSONParserException; public class ResourceConfigurationParser extends ConfigurationParser { private final ResourcesRegistry registry; From 35dfaff45842f6028a9e57303aca2c0bdc4f58b3 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Wed, 25 Aug 2021 14:57:57 +0200 Subject: [PATCH 16/26] make the localization related handles in NativeImageAgentJNIHandleSet lazy --- .../svm/agent/BreakpointInterceptor.java | 6 ++-- .../agent/NativeImageAgentJNIHandleSet.java | 35 ++++++++++++++----- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index 4de96036a578..5609a1453f19 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -690,14 +690,14 @@ private static boolean getBundleImplJDK11OrLater(JNIEnvironment jni, Breakpoint } private static String readLocaleTag(JNIEnvironment jni, JNIObjectHandle locale) { - return fromJniString(jni, callObjectMethod(jni, locale, agent.handles().javaUtilLocaleToLanguageTag)); + return fromJniString(jni, callObjectMethod(jni, locale, agent.handles().getJavaUtilLocaleToLanguageTag(jni))); } private static List> extractBundleInfo(JNIEnvironment jni, JNIObjectHandle result) { List> res = new ArrayList<>(); JNIObjectHandle curr = result; while (!nullHandle().equal(curr)) { - JNIObjectHandle locale = callObjectMethod(jni, curr, agent.handles().javaUtilResourceBundleGetLocale); + JNIObjectHandle locale = callObjectMethod(jni, curr, agent.handles().getJavaUtilResourceBundleGetLocale(jni)); if (clearException(jni)) { return null; } @@ -715,7 +715,7 @@ private static List> extractBundleInfo(JNIEnvironment jni, } private static JNIObjectHandle getParent(JNIEnvironment jni, JNIObjectHandle curr) { - JNIObjectHandle parent = readObjectField(jni, curr, agent.handles().javaUtilResourceBundleParentField); + JNIObjectHandle parent = readObjectField(jni, curr, agent.handles().getJavaUtilResourceBundleParentField(jni)); if (!clearException(jni)) { return parent; } diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java index 7001e6ba4114..6082e1e4161a 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java @@ -72,9 +72,9 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet { private JNIMethodId javaLangReflectConstructorDeclaringClassName; - final JNIMethodId javaUtilLocaleToLanguageTag; - final JNIFieldId javaUtilResourceBundleParentField; - final JNIMethodId javaUtilResourceBundleGetLocale; + private JNIMethodId javaUtilLocaleToLanguageTag = WordFactory.nullPointer(); + private JNIFieldId javaUtilResourceBundleParentField = WordFactory.nullPointer(); + private JNIMethodId javaUtilResourceBundleGetLocale = WordFactory.nullPointer(); NativeImageAgentJNIHandleSet(JNIEnvironment env) { super(env); @@ -107,11 +107,6 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet { javaLangInvokeWrongMethodTypeException = newClassGlobalRef(env, "java/lang/invoke/WrongMethodTypeException"); javaLangIllegalArgumentException = newClassGlobalRef(env, "java/lang/IllegalArgumentException"); - JNIObjectHandle javaUtilLocale = findClass(env, "java/util/Locale"); - javaUtilLocaleToLanguageTag = getMethodId(env, javaUtilLocale, "toLanguageTag", "()Ljava/lang/String;", false); - JNIObjectHandle javaUtilResourceBundle = findClass(env, "java/util/ResourceBundle"); - javaUtilResourceBundleParentField = getFieldId(env, javaUtilResourceBundle, "parent", "Ljava/util/ResourceBundle;", false); - javaUtilResourceBundleGetLocale = getMethodId(env, javaUtilResourceBundle, "getLocale", "()Ljava/util/Locale;", false); } JNIMethodId getJavaLangReflectExecutableGetParameterTypes(JNIEnvironment env) { @@ -189,4 +184,28 @@ JNIMethodId getJavaLangReflectConstructorDeclaringClassName(JNIEnvironment env, } return javaLangReflectConstructorDeclaringClassName; } + + public JNIMethodId getJavaUtilLocaleToLanguageTag(JNIEnvironment env) { + if (javaUtilLocaleToLanguageTag.isNull()) { + JNIObjectHandle javaUtilLocale = findClass(env, "java/util/Locale"); + javaUtilLocaleToLanguageTag = getMethodId(env, javaUtilLocale, "toLanguageTag", "()Ljava/lang/String;", false); + } + return javaUtilLocaleToLanguageTag; + } + + public JNIFieldId getJavaUtilResourceBundleParentField(JNIEnvironment env) { + if (javaUtilResourceBundleParentField.isNull()) { + JNIObjectHandle javaUtilResourceBundle = findClass(env, "java/util/ResourceBundle"); + javaUtilResourceBundleParentField = getFieldId(env, javaUtilResourceBundle, "parent", "Ljava/util/ResourceBundle;", false); + } + return javaUtilResourceBundleParentField; + } + + public JNIMethodId getJavaUtilResourceBundleGetLocale(JNIEnvironment env) { + if (javaUtilResourceBundleGetLocale.isNull()) { + JNIObjectHandle javaUtilResourceBundle = findClass(env, "java/util/ResourceBundle"); + javaUtilResourceBundleGetLocale = getMethodId(env, javaUtilResourceBundle, "getLocale", "()Ljava/util/Locale;", false); + } + return javaUtilResourceBundleGetLocale; + } } From 72041ec3013aea3e57a2bcec5ae5e71c309f5aa3 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Thu, 26 Aug 2021 12:20:20 +0200 Subject: [PATCH 17/26] fix incorrect bundle analysis in BreakpointInterceptor.extractBundleInfo --- .../src/com/oracle/svm/agent/BreakpointInterceptor.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index 5609a1453f19..85b5d1c86e2d 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -702,7 +702,11 @@ private static List> extractBundleInfo(JNIEnvironment jni, return null; } String localeTag = readLocaleTag(jni, locale); - JNIObjectHandle clazz = callObjectMethod(jni, result, agent.handles().javaLangObjectGetClass); + if (localeTag.equals("und")) { + /*- Root locale is serialized into "und" */ + localeTag = ""; + } + JNIObjectHandle clazz = callObjectMethod(jni, curr, agent.handles().javaLangObjectGetClass); if (!clearException(jni)) { JNIObjectHandle classNameHandle = callObjectMethod(jni, clazz, agent.handles().javaLangClassGetName); if (!clearException(jni)) { From 913a06fd0ad8e7322df0e35e2beed121e8221a44 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Tue, 14 Sep 2021 13:06:39 +0200 Subject: [PATCH 18/26] fix imports in ResourcesFeature --- .../src/com/oracle/svm/hosted/ResourcesFeature.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index edd16bffa9c7..46d3c20cb254 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -35,14 +35,12 @@ import java.nio.file.Path; import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; -import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -54,12 +52,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.oracle.svm.core.util.ClasspathUtils; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionType; import org.graalvm.compiler.serviceprovider.JavaVersionUtil; -import org.graalvm.home.HomeFinder; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.impl.ConfigurationCondition; From aab0db2825973316d6d306f3a8834d6cdc08bd13 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Tue, 14 Sep 2021 13:30:26 +0200 Subject: [PATCH 19/26] use JSONParserException instead of UserError in ResourceConfigurationParser --- .../svm/core/configure/ResourceConfigurationParser.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index b16185a16082..b3ab2b6a6392 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -38,7 +38,6 @@ import org.graalvm.nativeimage.impl.ConfigurationCondition; import com.oracle.svm.core.jdk.localization.LocalizationSupport; -import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.json.JSONParser; import com.oracle.svm.core.util.json.JSONParserException; @@ -111,13 +110,17 @@ private void parseBundle(Object bundle) { .stream() .map(ResourceConfigurationParser::parseLocale) .collect(Collectors.toList()); - UserError.guarantee(!asList.isEmpty(), "List of locales for %s is empty", basename); + if (asList.isEmpty()) { + throw new JSONParserException("List of locales for " + basename + " is empty"); + } registry.addResourceBundle(basename, asList); } Object classNames = resource.get("classNames"); if (classNames != null) { List asList = asList(classNames, "Attribute 'classNames' must be a list of classes"); - UserError.guarantee(!asList.isEmpty(), "List of classNames for %s is empty", basename); + if (asList.isEmpty()) { + throw new JSONParserException("List of classNames for " + basename + " is empty"); + } for (Object o : asList) { String className = asString(o, "Elements of 'classNames' must of strings."); registry.addClassResourceBundle(basename, className); From 5cda4fde0ee35441ba36e7df887dc812a8a9d2ca Mon Sep 17 00:00:00 2001 From: David Kozak Date: Sat, 25 Sep 2021 11:37:51 +0200 Subject: [PATCH 20/26] fixes after rebase --- .../config/ResourceConfigurationTest.java | 4 +- .../config/ResourceConfiguration.java | 55 ++++++++----------- .../ResourceConfigurationParser.java | 8 +-- .../svm/core/configure/ResourcesRegistry.java | 6 +- .../oracle/svm/hosted/ResourcesFeature.java | 17 +++--- 5 files changed, 42 insertions(+), 48 deletions(-) diff --git a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java index 852bd6039696..1d5fe077170b 100644 --- a/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java +++ b/substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java @@ -103,12 +103,12 @@ public void addResourceBundles(ConfigurationCondition condition, String name) { } @Override - public void addResourceBundle(String basename, Collection locales) { + public void addResourceBundles(ConfigurationCondition condition, String basename, Collection locales) { } @Override - public void addClassResourceBundle(String basename, String className) { + public void addClassBasedResourceBundle(ConfigurationCondition condition, String basename, String className) { } }; diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index c3ae3ba3436f..26a0cdc31bb6 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -30,7 +30,6 @@ import java.util.List; import java.util.Locale; import java.util.Set; -import java.util.Comparator; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; @@ -66,37 +65,29 @@ public void ignoreResources(ConfigurationCondition condition, String pattern) { configuration.ignoreResourcePattern(condition, pattern); } - @Override - public void addResourceBundles(ConfigurationCondition condition, String name) { - // TODO extent the API - configuration.addBundle(condition, Collections.emptyList(), name, "TODO"); - } - - public void addResourceBundles(String baseName) { - configuration.addBundle(baseName); + public void addResourceBundles(ConfigurationCondition condition, String baseName) { + configuration.addBundle(condition, baseName); } @Override - public void addResourceBundle(String basename, Collection locales) { - configuration.addBundle(basename, locales); + public void addResourceBundles(ConfigurationCondition condition, String basename, Collection locales) { + configuration.addBundle(condition, basename, locales); } @Override - public void addClassResourceBundle(String basename, String className) { - configuration.addClassResourceBundle(basename, className); + public void addClassBasedResourceBundle(ConfigurationCondition condition, String basename, String className) { + configuration.addClassResourceBundle(condition, basename, className); } } - private void addClassResourceBundle(String basename, String className) { - getOrCreateBundleConfig(basename).classNames.add(className); - } - public static class BundleConfiguration { + public final ConfigurationCondition condition; public final String baseName; public final Set locales = ConcurrentHashMap.newKeySet(); public final Set classNames = ConcurrentHashMap.newKeySet(); - public BundleConfiguration(String baseName) { + public BundleConfiguration(ConfigurationCondition condition, String baseName) { + this.condition = condition; this.baseName = baseName; } } @@ -130,24 +121,23 @@ public void ignoreResourcePattern(ConfigurationCondition condition, String patte ignoredResources.computeIfAbsent(new ConditionalElement<>(condition, pattern), p -> Pattern.compile(p.getElement())); } - private void addBundle(String basename, Collection locales) { - BundleConfiguration config = getOrCreateBundleConfig(basename); + public void addBundle(ConfigurationCondition condition, String basename, Collection locales) { + BundleConfiguration config = getOrCreateBundleConfig(condition, basename); for (Locale locale : locales) { config.locales.add(locale.toLanguageTag()); } } - private void addBundle(String baseName) { - getOrCreateBundleConfig(baseName); + private void addBundle(ConfigurationCondition condition, String baseName) { + getOrCreateBundleConfig(condition, baseName); } - public void addBundle(ConfigurationCondition condition, List> bundleInfo, String baseName, String queriedLocaleTag) { - ConditionalElement key = new ConditionalElement<>(condition, baseName); - BundleConfiguration config = bundles.get(key); - if (config == null) { - config = new BundleConfiguration(baseName); - bundles.put(key, config); - } + private void addClassResourceBundle(ConfigurationCondition condition, String basename, String className) { + getOrCreateBundleConfig(condition, basename).classNames.add(className); + } + + public void addBundle(ConfigurationCondition condition, List> bundleInfo, String baseName) { + BundleConfiguration config = getOrCreateBundleConfig(condition, baseName); for (Pair pair : bundleInfo) { String className = pair.getLeft(); String localeTag = pair.getRight(); @@ -159,8 +149,9 @@ public void addBundle(ConfigurationCondition condition, List new BundleConfiguration(baseName)); + private BundleConfiguration getOrCreateBundleConfig(ConfigurationCondition condition, String baseName) { + ConditionalElement key = new ConditionalElement<>(condition, baseName); + return bundles.computeIfAbsent(key, cond -> new BundleConfiguration(condition, baseName)); } public boolean anyResourceMatches(String s) { @@ -198,7 +189,7 @@ public void printJson(JsonWriter writer) throws IOException { } writer.append('}').append(',').newline(); writer.quote("bundles").append(':'); - printResourceBundle(writer, bundles.keySet(), Comparator.naturalOrder(), (p, w) -> printResourceBundle(bundles.get(p), w)); + JsonPrinter.printCollection(writer, bundles.keySet(), ConditionalElement.comparator(), (p, w) -> printResourceBundle(bundles.get(p), w)); writer.unindent().newline().append('}'); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index b3ab2b6a6392..384e13164b32 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -31,7 +31,6 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.function.BiConsumer; @@ -104,6 +103,7 @@ private void parseBundle(Object bundle) { Map resource = asMap(bundle, "Elements of 'bundles' list must be a bundle descriptor object"); checkAttributes(resource, "bundle descriptor object", Collections.singletonList("name"), Arrays.asList("locales", "classNames")); String basename = asString(resource.get("name"), "Missing attribute 'name' in bundle descriptor object"); + ConfigurationCondition condition = parseCondition(resource); Object locales = resource.get("locales"); if (locales != null) { List asList = asList(locales, "Attribute 'locales' must be a list of locales") @@ -113,7 +113,7 @@ private void parseBundle(Object bundle) { if (asList.isEmpty()) { throw new JSONParserException("List of locales for " + basename + " is empty"); } - registry.addResourceBundle(basename, asList); + registry.addResourceBundles(condition, basename, asList); } Object classNames = resource.get("classNames"); if (classNames != null) { @@ -123,12 +123,12 @@ private void parseBundle(Object bundle) { } for (Object o : asList) { String className = asString(o, "Elements of 'classNames' must of strings."); - registry.addClassResourceBundle(basename, className); + registry.addClassBasedResourceBundle(condition, basename, className); } } if (locales == null && classNames == null) { /*- If nothing more precise is specified, register in every included locale */ - registry.addResourceBundle(basename); + registry.addResourceBundles(condition, basename); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java index fc0c3802ba81..40532732fd5b 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourcesRegistry.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.core.configure; +import org.graalvm.nativeimage.impl.ConfigurationCondition; + import java.util.Collection; import java.util.Locale; @@ -65,7 +67,7 @@ default void addResourceBundles(String name) { void addResourceBundles(ConfigurationCondition condition, String name); - void addResourceBundle(String basename, Collection locales); + void addResourceBundles(ConfigurationCondition condition, String basename, Collection locales); - void addClassResourceBundle(String basename, String className); + void addClassBasedResourceBundle(ConfigurationCondition condition, String basename, String className); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index 46d3c20cb254..d2abddc3dcb6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -164,19 +164,20 @@ public void addResourceBundles(ConfigurationCondition condition, String name) { } @Override - public void addClassBasedResourceBundle(String basename, String className) { - ImageSingletons.lookup(LocalizationFeature.class).addClassBasedResourceBundle(basename, className); + public void addClassBasedResourceBundle(ConfigurationCondition condition, String basename, String className) { + if (configurationTypeResolver.resolveType(condition.getTypeName()) == null) { + return; + } + ImageSingletons.lookup(LocalizationFeature.class).prepareClassResourceBundle(basename, className); } @Override - public void addResourceBundle(String basename, Collection locales) { + public void addResourceBundles(ConfigurationCondition condition, String basename, Collection locales) { + if (configurationTypeResolver.resolveType(condition.getTypeName()) == null) { + return; + } ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(basename, locales); } - - @Override - public void addClassResourceBundle(String basename, String className) { - ImageSingletons.lookup(LocalizationFeature.class).prepareClassResourceBundle(basename, className); - } } @Override From 5d13c71f65c4ae0af2684264ce4aeb275153c416 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Mon, 27 Sep 2021 12:50:45 +0200 Subject: [PATCH 21/26] style issue: add missing override --- .../com/oracle/svm/configure/config/ResourceConfiguration.java | 1 + 1 file changed, 1 insertion(+) diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index 26a0cdc31bb6..345a5044bfe6 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -65,6 +65,7 @@ public void ignoreResources(ConfigurationCondition condition, String pattern) { configuration.ignoreResourcePattern(condition, pattern); } + @Override public void addResourceBundles(ConfigurationCondition condition, String baseName) { configuration.addBundle(condition, baseName); } From f87ef4a715b73c2718e508866060e0afb58d7153 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Tue, 28 Sep 2021 15:23:04 +0200 Subject: [PATCH 22/26] localization: code cleanup --- .../oracle/svm/agent/BreakpointInterceptor.java | 15 +++++++++------ .../svm/agent/NativeImageAgentJNIHandleSet.java | 1 - .../configure/config/ResourceConfiguration.java | 4 +--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index 85b5d1c86e2d..f30a40183abb 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -53,6 +53,7 @@ import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -649,15 +650,14 @@ private static boolean getBundleImplJDK8OrEarlier(JNIEnvironment jni, Breakpoint JNIObjectHandle loader = getObjectArgument(2); JNIObjectHandle control = getObjectArgument(3); JNIObjectHandle result = Support.callStaticObjectMethodLLLL(jni, bp.clazz, bp.method, baseName, locale, loader, control); - List> bundleInfo = null; + List> bundleInfo = Collections.emptyList(); if (clearException(jni)) { result = nullHandle(); } else { bundleInfo = extractBundleInfo(jni, result); } - String languageTag = readLocaleTag(jni, locale); traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK8OrEarlier", result.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), fromJniString(jni, baseName), languageTag, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo); + state.getFullStackTraceOrNull(), fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo); return true; } @@ -677,15 +677,14 @@ private static boolean getBundleImplJDK11OrLater(JNIEnvironment jni, Breakpoint JNIObjectHandle locale = getObjectArgument(3); JNIObjectHandle control = getObjectArgument(4); JNIObjectHandle result = Support.callStaticObjectMethodLLLLL(jni, bp.clazz, bp.method, callerModule, module, baseName, locale, control); - List> bundleInfo = null; + List> bundleInfo = Collections.emptyList(); if (clearException(jni)) { result = nullHandle(); } else { bundleInfo = extractBundleInfo(jni, result); } - String languageTag = readLocaleTag(jni, locale); traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK11OrLater", result.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), languageTag, Tracer.UNKNOWN_VALUE, bundleInfo); + state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo); return true; } @@ -693,6 +692,10 @@ private static String readLocaleTag(JNIEnvironment jni, JNIObjectHandle locale) return fromJniString(jni, callObjectMethod(jni, locale, agent.handles().getJavaUtilLocaleToLanguageTag(jni))); } + /** + * Traverses the bundle parent chain and collects classnames and locales of all encountered + * bundles. + */ private static List> extractBundleInfo(JNIEnvironment jni, JNIObjectHandle result) { List> res = new ArrayList<>(); JNIObjectHandle curr = result; diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java index 6082e1e4161a..3c5cf3f31534 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java @@ -106,7 +106,6 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet { javaLangIllegalAccessException = newClassGlobalRef(env, "java/lang/IllegalAccessException"); javaLangInvokeWrongMethodTypeException = newClassGlobalRef(env, "java/lang/invoke/WrongMethodTypeException"); javaLangIllegalArgumentException = newClassGlobalRef(env, "java/lang/IllegalArgumentException"); - } JNIMethodId getJavaLangReflectExecutableGetParameterTypes(JNIEnvironment env) { diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index 345a5044bfe6..d190f086f3b8 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -109,9 +109,7 @@ public ResourceConfiguration(ResourceConfiguration other) { public void removeAll(ResourceConfiguration other) { addedResources.keySet().removeAll(other.addedResources.keySet()); ignoredResources.keySet().removeAll(other.ignoredResources.keySet()); - for (ConditionalElement key : other.bundles.keySet()) { - bundles.remove(key); - } + bundles.keySet().removeAll(other.bundles.keySet()); } public void addResourcePattern(ConfigurationCondition condition, String pattern) { From 860f58212fea8afe42eea67e460ecb59f779fc72 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Fri, 22 Oct 2021 13:02:54 +0200 Subject: [PATCH 23/26] address issues from the review --- .../svm/agent/BreakpointInterceptor.java | 61 +++++++++++++------ .../agent/NativeImageAgentJNIHandleSet.java | 6 +- .../config/ResourceConfiguration.java | 20 +++--- .../configure/trace/ReflectionProcessor.java | 17 +++--- .../ResourceConfigurationParser.java | 17 +++--- .../OptimizedLocalizationSupport.java | 30 +++++++++ .../oracle/svm/hosted/ResourcesFeature.java | 4 +- .../jdk/localization/LocalizationFeature.java | 40 ++++-------- 8 files changed, 118 insertions(+), 77 deletions(-) diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index f30a40183abb..60bc7367ddac 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -41,7 +41,6 @@ import static com.oracle.svm.jvmtiagentbase.Support.jvmtiEnv; import static com.oracle.svm.jvmtiagentbase.Support.jvmtiFunctions; import static com.oracle.svm.jvmtiagentbase.Support.newObjectL; -import static com.oracle.svm.jvmtiagentbase.Support.readObjectField; import static com.oracle.svm.jvmtiagentbase.Support.testException; import static com.oracle.svm.jvmtiagentbase.Support.toCString; import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_BREAKPOINT; @@ -53,7 +52,6 @@ import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -61,7 +59,6 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Supplier; -import org.graalvm.collections.Pair; import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.UnmanagedMemory; @@ -650,14 +647,19 @@ private static boolean getBundleImplJDK8OrEarlier(JNIEnvironment jni, Breakpoint JNIObjectHandle loader = getObjectArgument(2); JNIObjectHandle control = getObjectArgument(3); JNIObjectHandle result = Support.callStaticObjectMethodLLLL(jni, bp.clazz, bp.method, baseName, locale, loader, control); - List> bundleInfo = Collections.emptyList(); + String[] classNames = new String[0]; + String[] locales = new String[0]; if (clearException(jni)) { result = nullHandle(); } else { - bundleInfo = extractBundleInfo(jni, result); + BundleInfo bundleInfo = extractBundleInfo(jni, result); + if (bundleInfo != null) { + classNames = bundleInfo.classNames; + locales = bundleInfo.locales; + } } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK8OrEarlier", result.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo); + state.getFullStackTraceOrNull(), fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, classNames, locales); return true; } @@ -677,28 +679,50 @@ private static boolean getBundleImplJDK11OrLater(JNIEnvironment jni, Breakpoint JNIObjectHandle locale = getObjectArgument(3); JNIObjectHandle control = getObjectArgument(4); JNIObjectHandle result = Support.callStaticObjectMethodLLLLL(jni, bp.clazz, bp.method, callerModule, module, baseName, locale, control); - List> bundleInfo = Collections.emptyList(); + String[] classNames = new String[0]; + String[] locales = new String[0]; if (clearException(jni)) { result = nullHandle(); } else { - bundleInfo = extractBundleInfo(jni, result); + BundleInfo bundleInfo = extractBundleInfo(jni, result); + if (bundleInfo != null) { + classNames = bundleInfo.classNames; + locales = bundleInfo.locales; + } } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK11OrLater", result.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo); + state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, classNames, locales); return true; } private static String readLocaleTag(JNIEnvironment jni, JNIObjectHandle locale) { - return fromJniString(jni, callObjectMethod(jni, locale, agent.handles().getJavaUtilLocaleToLanguageTag(jni))); + JNIObjectHandle languageTag = callObjectMethod(jni, locale, agent.handles().getJavaUtilLocaleToLanguageTag(jni)); + if (clearException(jni)) { + /*- return root locale */ + return ""; + } + return fromJniString(jni, languageTag); + } + + private static final class BundleInfo { + public final String[] classNames; + public final String[] locales; + + private BundleInfo(String[] classNames, String[] locales) { + this.classNames = classNames; + this.locales = locales; + } } /** * Traverses the bundle parent chain and collects classnames and locales of all encountered * bundles. + * */ - private static List> extractBundleInfo(JNIEnvironment jni, JNIObjectHandle result) { - List> res = new ArrayList<>(); - JNIObjectHandle curr = result; + private static BundleInfo extractBundleInfo(JNIEnvironment jni, JNIObjectHandle bundle) { + List locales = new ArrayList<>(); + List classNames = new ArrayList<>(); + JNIObjectHandle curr = bundle; while (!nullHandle().equal(curr)) { JNIObjectHandle locale = callObjectMethod(jni, curr, agent.handles().getJavaUtilResourceBundleGetLocale(jni)); if (clearException(jni)) { @@ -713,16 +737,17 @@ private static List> extractBundleInfo(JNIEnvironment jni, if (!clearException(jni)) { JNIObjectHandle classNameHandle = callObjectMethod(jni, clazz, agent.handles().javaLangClassGetName); if (!clearException(jni)) { - res.add(Pair.create(fromJniString(jni, classNameHandle), localeTag)); + classNames.add(fromJniString(jni, classNameHandle)); + locales.add(localeTag); } } - curr = getParent(jni, curr); + curr = getResourceBundleParent(jni, curr); } - return res; + return new BundleInfo(classNames.toArray(new String[0]), locales.toArray(new String[0])); } - private static JNIObjectHandle getParent(JNIEnvironment jni, JNIObjectHandle curr) { - JNIObjectHandle parent = readObjectField(jni, curr, agent.handles().getJavaUtilResourceBundleParentField(jni)); + private static JNIObjectHandle getResourceBundleParent(JNIEnvironment jni, JNIObjectHandle bundle) { + JNIObjectHandle parent = Support.readObjectField(jni, bundle, agent.handles().getJavaUtilResourceBundleParentField(jni)); if (!clearException(jni)) { return parent; } diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java index 3c5cf3f31534..31290feb3b78 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java @@ -72,9 +72,9 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet { private JNIMethodId javaLangReflectConstructorDeclaringClassName; - private JNIMethodId javaUtilLocaleToLanguageTag = WordFactory.nullPointer(); - private JNIFieldId javaUtilResourceBundleParentField = WordFactory.nullPointer(); - private JNIMethodId javaUtilResourceBundleGetLocale = WordFactory.nullPointer(); + private JNIMethodId javaUtilLocaleToLanguageTag; + private JNIFieldId javaUtilResourceBundleParentField; + private JNIMethodId javaUtilResourceBundleGetLocale; NativeImageAgentJNIHandleSet(JNIEnvironment env) { super(env); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index d190f086f3b8..0cd9c16e49a1 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -41,7 +41,6 @@ import com.oracle.svm.configure.json.JsonWriter; import com.oracle.svm.core.configure.ConditionalElement; import com.oracle.svm.core.configure.ResourcesRegistry; -import org.graalvm.collections.Pair; public class ResourceConfiguration implements ConfigurationBase { @@ -81,13 +80,13 @@ public void addClassBasedResourceBundle(ConfigurationCondition condition, String } } - public static class BundleConfiguration { + private static final class BundleConfiguration { public final ConfigurationCondition condition; public final String baseName; public final Set locales = ConcurrentHashMap.newKeySet(); public final Set classNames = ConcurrentHashMap.newKeySet(); - public BundleConfiguration(ConfigurationCondition condition, String baseName) { + private BundleConfiguration(ConfigurationCondition condition, String baseName) { this.condition = condition; this.baseName = baseName; } @@ -95,7 +94,7 @@ public BundleConfiguration(ConfigurationCondition condition, String baseName) { private final ConcurrentMap, Pattern> addedResources = new ConcurrentHashMap<>(); private final ConcurrentMap, Pattern> ignoredResources = new ConcurrentHashMap<>(); - private final ConcurrentHashMap, BundleConfiguration> bundles = new ConcurrentHashMap<>(); + private final ConcurrentMap, BundleConfiguration> bundles = new ConcurrentHashMap<>(); public ResourceConfiguration() { } @@ -135,11 +134,12 @@ private void addClassResourceBundle(ConfigurationCondition condition, String bas getOrCreateBundleConfig(condition, basename).classNames.add(className); } - public void addBundle(ConfigurationCondition condition, List> bundleInfo, String baseName) { + public void addBundle(ConfigurationCondition condition, List classNames, List locales, String baseName) { + assert classNames.size() == locales.size() : "Each bundle should be represented by both classname and locale"; BundleConfiguration config = getOrCreateBundleConfig(condition, baseName); - for (Pair pair : bundleInfo) { - String className = pair.getLeft(); - String localeTag = pair.getRight(); + for (int i = 0; i < classNames.size(); i++) { + String className = classNames.get(i); + String localeTag = locales.get(i); if (!className.equals(PROPERTY_BUNDLE)) { config.classNames.add(className); } else { @@ -193,7 +193,9 @@ public void printJson(JsonWriter writer) throws IOException { } private static void printResourceBundle(BundleConfiguration config, JsonWriter writer) throws IOException { - writer.append('{').quote("name").append(':').quote(config.baseName); + writer.append('{'); + ConfigurationConditionPrintable.printConditionAttribute(config.condition, writer); + writer.quote("name").append(':').quote(config.baseName); if (!config.locales.isEmpty()) { writer.append(',').quote("locales").append(":"); JsonPrinter.printCollection(writer, config.locales, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java index 03180d57b9c1..413f76edaf9c 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/trace/ReflectionProcessor.java @@ -32,7 +32,6 @@ import java.util.Map; import java.util.regex.Pattern; -import org.graalvm.collections.Pair; import org.graalvm.compiler.phases.common.LazyValue; import org.graalvm.nativeimage.impl.ConfigurationCondition; @@ -247,19 +246,23 @@ public void processEntry(Map entry) { } case "getBundleImplJDK8OrEarlier": { - expectSize(args, 5); + expectSize(args, 6); String baseName = (String) args.get(0); @SuppressWarnings("unchecked") - List> bundleInfo = (List>) args.get(4); - resourceConfiguration.addBundle(condition, bundleInfo, baseName); + List classNames = (List) args.get(4); + @SuppressWarnings("unchecked") + List locales = (List) args.get(5); + resourceConfiguration.addBundle(condition, classNames, locales, baseName); break; } case "getBundleImplJDK11OrLater": { - expectSize(args, 6); + expectSize(args, 7); String baseName = (String) args.get(2); @SuppressWarnings("unchecked") - List> bundleInfo = (List>) args.get(5); - resourceConfiguration.addBundle(condition, bundleInfo, baseName); + List classNames = (List) args.get(5); + @SuppressWarnings("unchecked") + List locales = (List) args.get(6); + resourceConfiguration.addBundle(condition, classNames, locales, baseName); break; } default: diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index 384e13164b32..687533b65236 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -102,7 +102,7 @@ private void parseTopLevelObject(Map obj) { private void parseBundle(Object bundle) { Map resource = asMap(bundle, "Elements of 'bundles' list must be a bundle descriptor object"); checkAttributes(resource, "bundle descriptor object", Collections.singletonList("name"), Arrays.asList("locales", "classNames")); - String basename = asString(resource.get("name"), "Missing attribute 'name' in bundle descriptor object"); + String basename = asString(resource.get("name")); ConfigurationCondition condition = parseCondition(resource); Object locales = resource.get("locales"); if (locales != null) { @@ -110,30 +110,27 @@ private void parseBundle(Object bundle) { .stream() .map(ResourceConfigurationParser::parseLocale) .collect(Collectors.toList()); - if (asList.isEmpty()) { - throw new JSONParserException("List of locales for " + basename + " is empty"); + if (!asList.isEmpty()) { + registry.addResourceBundles(condition, basename, asList); } - registry.addResourceBundles(condition, basename, asList); + } Object classNames = resource.get("classNames"); if (classNames != null) { List asList = asList(classNames, "Attribute 'classNames' must be a list of classes"); - if (asList.isEmpty()) { - throw new JSONParserException("List of classNames for " + basename + " is empty"); - } for (Object o : asList) { - String className = asString(o, "Elements of 'classNames' must of strings."); + String className = asString(o); registry.addClassBasedResourceBundle(condition, basename, className); } } if (locales == null && classNames == null) { - /*- If nothing more precise is specified, register in every included locale */ + /* If nothing more precise is specified, register in every included locale */ registry.addResourceBundles(condition, basename); } } private static Locale parseLocale(Object input) { - String localeTag = asString(input, "Elements of 'locales' must be strings."); + String localeTag = asString(input); Locale locale = LocalizationSupport.parseLocaleFromTag(localeTag); if (locale == null) { throw new JSONParserException(localeTag + " is not a valid locale tag"); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java index 68474bb2e938..91af206cb7f1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/localization/OptimizedLocalizationSupport.java @@ -24,8 +24,12 @@ */ package com.oracle.svm.core.jdk.localization; +import com.oracle.svm.core.util.UserError; +import com.oracle.svm.util.ReflectionUtil; import org.graalvm.collections.Pair; +//Checkstyle: allow reflection +import java.lang.reflect.Field; import java.nio.charset.Charset; import java.util.HashMap; import java.util.Locale; @@ -76,6 +80,32 @@ public ResourceBundle getCached(String baseName, Locale locale) throws MissingRe } + private final Field bundleNameField = ReflectionUtil.lookupField(ResourceBundle.class, "name"); + private final Field bundleLocaleField = ReflectionUtil.lookupField(ResourceBundle.class, "locale"); + + @Override + public void prepareClassResourceBundle(String basename, Class bundleClass) { + try { + ResourceBundle bundle = ((ResourceBundle) ReflectionUtil.newInstance(bundleClass)); + Locale locale = extractLocale(bundleClass); + /*- Set the basename and locale to be consistent with JVM lookup process */ + bundleNameField.set(bundle, basename); + bundleLocaleField.set(bundle, locale); + prepareBundle(basename, bundle, locale); + } catch (ReflectionUtil.ReflectionUtilError | ReflectiveOperationException e) { + throw UserError.abort(e, "Failed to instantiated bundle from class %s, reason %s", bundleClass, e.getCause().getMessage()); + } + } + + private static Locale extractLocale(Class bundleClass) { + String name = bundleClass.getName(); + int split = name.lastIndexOf('_'); + if (split == -1) { + return Locale.ROOT; + } + return parseLocaleFromTag(name.substring(split + 1)); + } + @Platforms(Platform.HOSTED_ONLY.class) @Override public void prepareBundle(String bundleName, ResourceBundle bundle, Locale locale) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index d2abddc3dcb6..a6b7aba6637e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -168,7 +168,7 @@ public void addClassBasedResourceBundle(ConfigurationCondition condition, String if (configurationTypeResolver.resolveType(condition.getTypeName()) == null) { return; } - ImageSingletons.lookup(LocalizationFeature.class).prepareClassResourceBundle(basename, className); + registerConditionalConfiguration(condition, () -> ImageSingletons.lookup(LocalizationFeature.class).prepareClassResourceBundle(basename, className)); } @Override @@ -176,7 +176,7 @@ public void addResourceBundles(ConfigurationCondition condition, String basename if (configurationTypeResolver.resolveType(condition.getTypeName()) == null) { return; } - ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(basename, locales); + registerConditionalConfiguration(condition, () -> ImageSingletons.lookup(LocalizationFeature.class).prepareBundle(basename, locales)); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java index 3d38100016f8..3189006adb9b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/localization/LocalizationFeature.java @@ -42,7 +42,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.IllformedLocaleException; import java.util.List; import java.util.Locale; import java.util.Map; @@ -237,7 +236,7 @@ private static ResolvedJavaField findStaticField(ResolvedJavaType declaringClass public void afterRegistration(AfterRegistrationAccess access) { findClassByName = access::findClassByName; allLocales = processLocalesOption(); - defaultLocale = parseLocaleFromTag(Options.DefaultLocale.getValue()); + defaultLocale = LocalizationSupport.parseLocaleFromTag(Options.DefaultLocale.getValue()); UserError.guarantee(defaultLocale != null, "Invalid default locale %s", Options.DefaultLocale.getValue()); try { defaultCharset = Charset.forName(Options.DefaultCharset.getValue()); @@ -283,29 +282,6 @@ public void afterAnalysis(AfterAnalysisAccess access) { } } - /** - * @return locale for given tag or null for invalid ones - */ - @Platforms(Platform.HOSTED_ONLY.class) - private static Locale parseLocaleFromTag(String tag) { - try { - return new Locale.Builder().setLanguageTag(tag).build(); - } catch (IllformedLocaleException ex) { - /*- Custom made locales consisting of at most three parts separated by '-' are also supported */ - String[] parts = tag.split("-"); - switch (parts.length) { - case 1: - return new Locale(parts[0]); - case 2: - return new Locale(parts[0], parts[1]); - case 3: - return new Locale(parts[0], parts[1], parts[2]); - default: - return null; - } - } - } - @Platforms(Platform.HOSTED_ONLY.class) private static Set processLocalesOption() { Set locales = new HashSet<>(); @@ -315,7 +291,7 @@ private static Set processLocalesOption() { } List invalid = new ArrayList<>(); for (String tag : OptionUtils.flatten(",", Options.IncludeLocales.getValue().values())) { - Locale locale = parseLocaleFromTag(tag); + Locale locale = LocalizationSupport.parseLocaleFromTag(tag); if (locale != null) { locales.add(locale); } else { @@ -451,7 +427,7 @@ private void processRequestedBundle(String input) { prepareBundle(input, allLocales); return; } - Locale locale = splitIndex + 1 < input.length() ? parseLocaleFromTag(input.substring(splitIndex + 1)) : Locale.ROOT; + Locale locale = splitIndex + 1 < input.length() ? LocalizationSupport.parseLocaleFromTag(input.substring(splitIndex + 1)) : Locale.ROOT; if (locale == null) { trace("Cannot parse wanted locale " + input.substring(splitIndex + 1) + ", default will be used instead."); locale = defaultLocale; @@ -461,6 +437,14 @@ private void processRequestedBundle(String input) { prepareBundle(baseName, Collections.singletonList(locale)); } + @Platforms(Platform.HOSTED_ONLY.class) + public void prepareClassResourceBundle(String basename, String className) { + Class bundleClass = findClassByName.apply(className); + UserError.guarantee(ResourceBundle.class.isAssignableFrom(bundleClass), "%s is not a subclass of ResourceBundle", bundleClass.getName()); + trace("Adding class based resource bundle: " + className + " " + bundleClass); + support.prepareClassResourceBundle(basename, bundleClass); + } + @Platforms(Platform.HOSTED_ONLY.class) public void prepareBundle(String baseName) { prepareBundle(baseName, allLocales); @@ -480,7 +464,7 @@ public void prepareBundle(String baseName, Collection wantedLocales) { } catch (MissingResourceException mre) { continue; } - somethingFound = !resourceBundle.isEmpty(); + somethingFound |= !resourceBundle.isEmpty(); for (ResourceBundle bundle : resourceBundle) { prepareBundle(baseName, bundle, locale); } From f1d7dc06cd903ab7c425d028ea93ed24237c3ba2 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Fri, 22 Oct 2021 14:41:57 +0200 Subject: [PATCH 24/26] fix formatting issues when printing resource bundle config --- .../svm/configure/config/ResourceConfiguration.java | 8 ++++---- .../svm/core/configure/ResourceConfigurationParser.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java index 0cd9c16e49a1..88dc4eebe357 100644 --- a/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java +++ b/substratevm/src/com.oracle.svm.configure/src/com/oracle/svm/configure/config/ResourceConfiguration.java @@ -193,18 +193,18 @@ public void printJson(JsonWriter writer) throws IOException { } private static void printResourceBundle(BundleConfiguration config, JsonWriter writer) throws IOException { - writer.append('{'); + writer.append('{').indent().newline(); ConfigurationConditionPrintable.printConditionAttribute(config.condition, writer); writer.quote("name").append(':').quote(config.baseName); if (!config.locales.isEmpty()) { - writer.append(',').quote("locales").append(":"); + writer.append(',').newline().quote("locales").append(":"); JsonPrinter.printCollection(writer, config.locales, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); } if (!config.classNames.isEmpty()) { - writer.append(',').quote("classNames").append(":"); + writer.append(',').newline().quote("classNames").append(":"); JsonPrinter.printCollection(writer, config.classNames, Comparator.naturalOrder(), (String p, JsonWriter w) -> w.quote(p)); } - writer.append('}'); + writer.unindent().newline().append('}'); } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java index 687533b65236..929fe5b38edc 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/configure/ResourceConfigurationParser.java @@ -101,7 +101,7 @@ private void parseTopLevelObject(Map obj) { private void parseBundle(Object bundle) { Map resource = asMap(bundle, "Elements of 'bundles' list must be a bundle descriptor object"); - checkAttributes(resource, "bundle descriptor object", Collections.singletonList("name"), Arrays.asList("locales", "classNames")); + checkAttributes(resource, "bundle descriptor object", Collections.singletonList("name"), Arrays.asList("locales", "classNames", "condition")); String basename = asString(resource.get("name")); ConfigurationCondition condition = parseCondition(resource); Object locales = resource.get("locales"); From 8fefd4fa40ffe9f858c2a96ab21e783ce22e7ae6 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Mon, 1 Nov 2021 11:54:42 +0100 Subject: [PATCH 25/26] remove static imports for Support.callObjectMethod & Support.newObjectL --- .../svm/agent/BreakpointInterceptor.java | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index 60bc7367ddac..52502f20faa8 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -26,7 +26,6 @@ import static com.oracle.svm.core.util.VMError.guarantee; import static com.oracle.svm.jni.JNIObjectHandles.nullHandle; -import static com.oracle.svm.jvmtiagentbase.Support.callObjectMethod; import static com.oracle.svm.jvmtiagentbase.Support.check; import static com.oracle.svm.jvmtiagentbase.Support.checkNoException; import static com.oracle.svm.jvmtiagentbase.Support.clearException; @@ -40,7 +39,6 @@ import static com.oracle.svm.jvmtiagentbase.Support.jniFunctions; import static com.oracle.svm.jvmtiagentbase.Support.jvmtiEnv; import static com.oracle.svm.jvmtiagentbase.Support.jvmtiFunctions; -import static com.oracle.svm.jvmtiagentbase.Support.newObjectL; import static com.oracle.svm.jvmtiagentbase.Support.testException; import static com.oracle.svm.jvmtiagentbase.Support.toCString; import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_BREAKPOINT; @@ -696,7 +694,7 @@ private static boolean getBundleImplJDK11OrLater(JNIEnvironment jni, Breakpoint } private static String readLocaleTag(JNIEnvironment jni, JNIObjectHandle locale) { - JNIObjectHandle languageTag = callObjectMethod(jni, locale, agent.handles().getJavaUtilLocaleToLanguageTag(jni)); + JNIObjectHandle languageTag = Support.callObjectMethod(jni, locale, agent.handles().getJavaUtilLocaleToLanguageTag(jni)); if (clearException(jni)) { /*- return root locale */ return ""; @@ -724,7 +722,7 @@ private static BundleInfo extractBundleInfo(JNIEnvironment jni, JNIObjectHandle List classNames = new ArrayList<>(); JNIObjectHandle curr = bundle; while (!nullHandle().equal(curr)) { - JNIObjectHandle locale = callObjectMethod(jni, curr, agent.handles().getJavaUtilResourceBundleGetLocale(jni)); + JNIObjectHandle locale = Support.callObjectMethod(jni, curr, agent.handles().getJavaUtilResourceBundleGetLocale(jni)); if (clearException(jni)) { return null; } @@ -733,9 +731,9 @@ private static BundleInfo extractBundleInfo(JNIEnvironment jni, JNIObjectHandle /*- Root locale is serialized into "und" */ localeTag = ""; } - JNIObjectHandle clazz = callObjectMethod(jni, curr, agent.handles().javaLangObjectGetClass); + JNIObjectHandle clazz = Support.callObjectMethod(jni, curr, agent.handles().javaLangObjectGetClass); if (!clearException(jni)) { - JNIObjectHandle classNameHandle = callObjectMethod(jni, clazz, agent.handles().javaLangClassGetName); + JNIObjectHandle classNameHandle = Support.callObjectMethod(jni, clazz, agent.handles().javaLangClassGetName); if (!clearException(jni)) { classNames.add(fromJniString(jni, classNameHandle)); locales.add(localeTag); @@ -1056,7 +1054,7 @@ private static boolean objectStreamClassConstructor(JNIEnvironment jni, Breakpoi JNIObjectHandle serializeTargetClass = getObjectArgument(1); String serializeTargetClassName = getClassNameOrNull(jni, serializeTargetClass); - JNIObjectHandle objectStreamClassInstance = newObjectL(jni, bp.clazz, bp.method, serializeTargetClass); + JNIObjectHandle objectStreamClassInstance = Support.newObjectL(jni, bp.clazz, bp.method, serializeTargetClass); boolean validObjectStreamClassInstance = nullHandle().notEqual(objectStreamClassInstance); if (clearException(jni)) { validObjectStreamClassInstance = false; @@ -1076,7 +1074,7 @@ private static boolean objectStreamClassConstructor(JNIEnvironment jni, Breakpoi * recursively. Call ObjectStreamClass.getClassDataLayout0() can get all of them. */ JNIMethodId getClassDataLayout0MId = agent.handles().getJavaIoObjectStreamClassGetClassDataLayout0(jni, bp.clazz); - JNIObjectHandle dataLayoutArray = callObjectMethod(jni, objectStreamClassInstance, getClassDataLayout0MId); + JNIObjectHandle dataLayoutArray = Support.callObjectMethod(jni, objectStreamClassInstance, getClassDataLayout0MId); if (!clearException(jni) && nullHandle().notEqual(dataLayoutArray)) { int length = jniFunctions().getGetArrayLength().invoke(jni, dataLayoutArray); // If only 1 element is got from getClassDataLayout0(). it is base ObjectStreamClass @@ -1091,7 +1089,7 @@ private static boolean objectStreamClassConstructor(JNIEnvironment jni, Breakpoi if (hasData) { JNIObjectHandle oscInstanceInSlot = jniFunctions().getGetObjectField().invoke(jni, classDataSlot, descFId); if (!jniFunctions().getIsSameObject().invoke(jni, oscInstanceInSlot, objectStreamClassInstance)) { - JNIObjectHandle oscClazz = callObjectMethod(jni, oscInstanceInSlot, javaIoObjectStreamClassForClassMId); + JNIObjectHandle oscClazz = Support.callObjectMethod(jni, oscInstanceInSlot, javaIoObjectStreamClassForClassMId); String oscClassName = getClassNameOrNull(jni, oscClazz); transitiveSerializeTargets.add(oscClassName); } @@ -1137,7 +1135,7 @@ private static boolean customTargetConstructorSerialization(JNIEnvironment jni, JNIObjectHandle customConstructorObj = getObjectArgument(2); JNIObjectHandle customConstructorClass = jniFunctions().getGetObjectClass().invoke(jni, customConstructorObj); JNIMethodId getDeclaringClassNameMethodID = agent.handles().getJavaLangReflectConstructorDeclaringClassName(jni, customConstructorClass); - JNIObjectHandle declaredClassNameObj = callObjectMethod(jni, customConstructorObj, getDeclaringClassNameMethodID); + JNIObjectHandle declaredClassNameObj = Support.callObjectMethod(jni, customConstructorObj, getDeclaringClassNameMethodID); String customConstructorClassName = fromJniString(jni, declaredClassNameObj); if (tracer != null) { @@ -1336,9 +1334,9 @@ private static void setupClassLoadEvent(JvmtiEnv jvmti, JNIEnvironment jni) { JNIMethodId getPlatformLoader = agent.handles().getMethodIdOptional(jni, classLoader, "getPlatformClassLoader", "()Ljava/lang/ClassLoader;", true); JNIMethodId getAppLoader = agent.handles().getMethodIdOptional(jni, classLoader, "getBuiltinAppClassLoader", "()Ljava/lang/ClassLoader;", true); if (getPlatformLoader.isNonNull() && getAppLoader.isNonNull()) { // only on JDK 9 and later - JNIObjectHandle platformLoader = callObjectMethod(jni, classLoader, getPlatformLoader); + JNIObjectHandle platformLoader = Support.callObjectMethod(jni, classLoader, getPlatformLoader); checkNoException(jni); - JNIObjectHandle appLoader = callObjectMethod(jni, classLoader, getAppLoader); + JNIObjectHandle appLoader = Support.callObjectMethod(jni, classLoader, getAppLoader); checkNoException(jni); guarantee(platformLoader.notEqual(nullHandle()) && appLoader.notEqual(nullHandle())); From f51640092b89c3905b18bb3853e00f28f815aa38 Mon Sep 17 00:00:00 2001 From: David Kozak Date: Mon, 1 Nov 2021 12:01:21 +0100 Subject: [PATCH 26/26] refactoring BreakpointInterceptor --- .../svm/agent/BreakpointInterceptor.java | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java index 52502f20faa8..011a68a0d3b6 100644 --- a/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java +++ b/substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java @@ -645,19 +645,14 @@ private static boolean getBundleImplJDK8OrEarlier(JNIEnvironment jni, Breakpoint JNIObjectHandle loader = getObjectArgument(2); JNIObjectHandle control = getObjectArgument(3); JNIObjectHandle result = Support.callStaticObjectMethodLLLL(jni, bp.clazz, bp.method, baseName, locale, loader, control); - String[] classNames = new String[0]; - String[] locales = new String[0]; + BundleInfo bundleInfo = BundleInfo.NONE; if (clearException(jni)) { result = nullHandle(); } else { - BundleInfo bundleInfo = extractBundleInfo(jni, result); - if (bundleInfo != null) { - classNames = bundleInfo.classNames; - locales = bundleInfo.locales; - } + bundleInfo = extractBundleInfo(jni, result); } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK8OrEarlier", result.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, classNames, locales); + state.getFullStackTraceOrNull(), fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo.classNames, bundleInfo.locales); return true; } @@ -677,19 +672,15 @@ private static boolean getBundleImplJDK11OrLater(JNIEnvironment jni, Breakpoint JNIObjectHandle locale = getObjectArgument(3); JNIObjectHandle control = getObjectArgument(4); JNIObjectHandle result = Support.callStaticObjectMethodLLLLL(jni, bp.clazz, bp.method, callerModule, module, baseName, locale, control); - String[] classNames = new String[0]; - String[] locales = new String[0]; + BundleInfo bundleInfo = BundleInfo.NONE; if (clearException(jni)) { result = nullHandle(); } else { - BundleInfo bundleInfo = extractBundleInfo(jni, result); - if (bundleInfo != null) { - classNames = bundleInfo.classNames; - locales = bundleInfo.locales; - } + bundleInfo = extractBundleInfo(jni, result); } traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK11OrLater", result.notEqual(nullHandle()), - state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, classNames, locales); + state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo.classNames, + bundleInfo.locales); return true; } @@ -703,10 +694,13 @@ private static String readLocaleTag(JNIEnvironment jni, JNIObjectHandle locale) } private static final class BundleInfo { - public final String[] classNames; - public final String[] locales; - private BundleInfo(String[] classNames, String[] locales) { + static final BundleInfo NONE = new BundleInfo(new String[0], new String[0]); + + final String[] classNames; + final String[] locales; + + BundleInfo(String[] classNames, String[] locales) { this.classNames = classNames; this.locales = locales; } @@ -721,10 +715,10 @@ private static BundleInfo extractBundleInfo(JNIEnvironment jni, JNIObjectHandle List locales = new ArrayList<>(); List classNames = new ArrayList<>(); JNIObjectHandle curr = bundle; - while (!nullHandle().equal(curr)) { + while (curr.notEqual(nullHandle())) { JNIObjectHandle locale = Support.callObjectMethod(jni, curr, agent.handles().getJavaUtilResourceBundleGetLocale(jni)); if (clearException(jni)) { - return null; + return BundleInfo.NONE; } String localeTag = readLocaleTag(jni, locale); if (localeTag.equals("und")) {