Skip to content

Commit a53da53

Browse files
committed
[GR-30206] [GR-30205] Extend the agent to collect the locale and classname of resource bundles.
PullRequest: graal/9307
2 parents d0c7394 + f516400 commit a53da53

File tree

13 files changed

+385
-61
lines changed

13 files changed

+385
-61
lines changed

substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/BreakpointInterceptor.java

Lines changed: 77 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import static com.oracle.svm.core.util.VMError.guarantee;
2828
import static com.oracle.svm.jni.JNIObjectHandles.nullHandle;
29-
import static com.oracle.svm.jvmtiagentbase.Support.callObjectMethod;
3029
import static com.oracle.svm.jvmtiagentbase.Support.check;
3130
import static com.oracle.svm.jvmtiagentbase.Support.checkNoException;
3231
import static com.oracle.svm.jvmtiagentbase.Support.clearException;
@@ -40,7 +39,6 @@
4039
import static com.oracle.svm.jvmtiagentbase.Support.jniFunctions;
4140
import static com.oracle.svm.jvmtiagentbase.Support.jvmtiEnv;
4241
import static com.oracle.svm.jvmtiagentbase.Support.jvmtiFunctions;
43-
import static com.oracle.svm.jvmtiagentbase.Support.newObjectL;
4442
import static com.oracle.svm.jvmtiagentbase.Support.testException;
4543
import static com.oracle.svm.jvmtiagentbase.Support.toCString;
4644
import static com.oracle.svm.jvmtiagentbase.jvmti.JvmtiEvent.JVMTI_EVENT_BREAKPOINT;
@@ -647,11 +645,14 @@ private static boolean getBundleImplJDK8OrEarlier(JNIEnvironment jni, Breakpoint
647645
JNIObjectHandle loader = getObjectArgument(2);
648646
JNIObjectHandle control = getObjectArgument(3);
649647
JNIObjectHandle result = Support.callStaticObjectMethodLLLL(jni, bp.clazz, bp.method, baseName, locale, loader, control);
648+
BundleInfo bundleInfo = BundleInfo.NONE;
650649
if (clearException(jni)) {
651650
result = nullHandle();
651+
} else {
652+
bundleInfo = extractBundleInfo(jni, result);
652653
}
653654
traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK8OrEarlier", result.notEqual(nullHandle()),
654-
state.getFullStackTraceOrNull(), fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE);
655+
state.getFullStackTraceOrNull(), fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo.classNames, bundleInfo.locales);
655656
return true;
656657
}
657658

@@ -671,14 +672,80 @@ private static boolean getBundleImplJDK11OrLater(JNIEnvironment jni, Breakpoint
671672
JNIObjectHandle locale = getObjectArgument(3);
672673
JNIObjectHandle control = getObjectArgument(4);
673674
JNIObjectHandle result = Support.callStaticObjectMethodLLLLL(jni, bp.clazz, bp.method, callerModule, module, baseName, locale, control);
675+
BundleInfo bundleInfo = BundleInfo.NONE;
674676
if (clearException(jni)) {
675677
result = nullHandle();
678+
} else {
679+
bundleInfo = extractBundleInfo(jni, result);
676680
}
677681
traceBreakpoint(jni, nullHandle(), nullHandle(), callerClass, "getBundleImplJDK11OrLater", result.notEqual(nullHandle()),
678-
state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE);
682+
state.getFullStackTraceOrNull(), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, fromJniString(jni, baseName), Tracer.UNKNOWN_VALUE, Tracer.UNKNOWN_VALUE, bundleInfo.classNames,
683+
bundleInfo.locales);
679684
return true;
680685
}
681686

687+
private static String readLocaleTag(JNIEnvironment jni, JNIObjectHandle locale) {
688+
JNIObjectHandle languageTag = Support.callObjectMethod(jni, locale, agent.handles().getJavaUtilLocaleToLanguageTag(jni));
689+
if (clearException(jni)) {
690+
/*- return root locale */
691+
return "";
692+
}
693+
return fromJniString(jni, languageTag);
694+
}
695+
696+
private static final class BundleInfo {
697+
698+
static final BundleInfo NONE = new BundleInfo(new String[0], new String[0]);
699+
700+
final String[] classNames;
701+
final String[] locales;
702+
703+
BundleInfo(String[] classNames, String[] locales) {
704+
this.classNames = classNames;
705+
this.locales = locales;
706+
}
707+
}
708+
709+
/**
710+
* Traverses the bundle parent chain and collects classnames and locales of all encountered
711+
* bundles.
712+
*
713+
*/
714+
private static BundleInfo extractBundleInfo(JNIEnvironment jni, JNIObjectHandle bundle) {
715+
List<String> locales = new ArrayList<>();
716+
List<String> classNames = new ArrayList<>();
717+
JNIObjectHandle curr = bundle;
718+
while (curr.notEqual(nullHandle())) {
719+
JNIObjectHandle locale = Support.callObjectMethod(jni, curr, agent.handles().getJavaUtilResourceBundleGetLocale(jni));
720+
if (clearException(jni)) {
721+
return BundleInfo.NONE;
722+
}
723+
String localeTag = readLocaleTag(jni, locale);
724+
if (localeTag.equals("und")) {
725+
/*- Root locale is serialized into "und" */
726+
localeTag = "";
727+
}
728+
JNIObjectHandle clazz = Support.callObjectMethod(jni, curr, agent.handles().javaLangObjectGetClass);
729+
if (!clearException(jni)) {
730+
JNIObjectHandle classNameHandle = Support.callObjectMethod(jni, clazz, agent.handles().javaLangClassGetName);
731+
if (!clearException(jni)) {
732+
classNames.add(fromJniString(jni, classNameHandle));
733+
locales.add(localeTag);
734+
}
735+
}
736+
curr = getResourceBundleParent(jni, curr);
737+
}
738+
return new BundleInfo(classNames.toArray(new String[0]), locales.toArray(new String[0]));
739+
}
740+
741+
private static JNIObjectHandle getResourceBundleParent(JNIEnvironment jni, JNIObjectHandle bundle) {
742+
JNIObjectHandle parent = Support.readObjectField(jni, bundle, agent.handles().getJavaUtilResourceBundleParentField(jni));
743+
if (!clearException(jni)) {
744+
return parent;
745+
}
746+
return nullHandle();
747+
}
748+
682749
private static boolean loadClass(JNIEnvironment jni, Breakpoint bp, InterceptedState state) {
683750
assert experimentalClassLoaderSupport;
684751
/*
@@ -981,7 +1048,7 @@ private static boolean objectStreamClassConstructor(JNIEnvironment jni, Breakpoi
9811048
JNIObjectHandle serializeTargetClass = getObjectArgument(1);
9821049
String serializeTargetClassName = getClassNameOrNull(jni, serializeTargetClass);
9831050

984-
JNIObjectHandle objectStreamClassInstance = newObjectL(jni, bp.clazz, bp.method, serializeTargetClass);
1051+
JNIObjectHandle objectStreamClassInstance = Support.newObjectL(jni, bp.clazz, bp.method, serializeTargetClass);
9851052
boolean validObjectStreamClassInstance = nullHandle().notEqual(objectStreamClassInstance);
9861053
if (clearException(jni)) {
9871054
validObjectStreamClassInstance = false;
@@ -1001,7 +1068,7 @@ private static boolean objectStreamClassConstructor(JNIEnvironment jni, Breakpoi
10011068
* recursively. Call ObjectStreamClass.getClassDataLayout0() can get all of them.
10021069
*/
10031070
JNIMethodId getClassDataLayout0MId = agent.handles().getJavaIoObjectStreamClassGetClassDataLayout0(jni, bp.clazz);
1004-
JNIObjectHandle dataLayoutArray = callObjectMethod(jni, objectStreamClassInstance, getClassDataLayout0MId);
1071+
JNIObjectHandle dataLayoutArray = Support.callObjectMethod(jni, objectStreamClassInstance, getClassDataLayout0MId);
10051072
if (!clearException(jni) && nullHandle().notEqual(dataLayoutArray)) {
10061073
int length = jniFunctions().getGetArrayLength().invoke(jni, dataLayoutArray);
10071074
// If only 1 element is got from getClassDataLayout0(). it is base ObjectStreamClass
@@ -1016,7 +1083,7 @@ private static boolean objectStreamClassConstructor(JNIEnvironment jni, Breakpoi
10161083
if (hasData) {
10171084
JNIObjectHandle oscInstanceInSlot = jniFunctions().getGetObjectField().invoke(jni, classDataSlot, descFId);
10181085
if (!jniFunctions().getIsSameObject().invoke(jni, oscInstanceInSlot, objectStreamClassInstance)) {
1019-
JNIObjectHandle oscClazz = callObjectMethod(jni, oscInstanceInSlot, javaIoObjectStreamClassForClassMId);
1086+
JNIObjectHandle oscClazz = Support.callObjectMethod(jni, oscInstanceInSlot, javaIoObjectStreamClassForClassMId);
10201087
String oscClassName = getClassNameOrNull(jni, oscClazz);
10211088
transitiveSerializeTargets.add(oscClassName);
10221089
}
@@ -1062,7 +1129,7 @@ private static boolean customTargetConstructorSerialization(JNIEnvironment jni,
10621129
JNIObjectHandle customConstructorObj = getObjectArgument(2);
10631130
JNIObjectHandle customConstructorClass = jniFunctions().getGetObjectClass().invoke(jni, customConstructorObj);
10641131
JNIMethodId getDeclaringClassNameMethodID = agent.handles().getJavaLangReflectConstructorDeclaringClassName(jni, customConstructorClass);
1065-
JNIObjectHandle declaredClassNameObj = callObjectMethod(jni, customConstructorObj, getDeclaringClassNameMethodID);
1132+
JNIObjectHandle declaredClassNameObj = Support.callObjectMethod(jni, customConstructorObj, getDeclaringClassNameMethodID);
10661133
String customConstructorClassName = fromJniString(jni, declaredClassNameObj);
10671134

10681135
if (tracer != null) {
@@ -1261,9 +1328,9 @@ private static void setupClassLoadEvent(JvmtiEnv jvmti, JNIEnvironment jni) {
12611328
JNIMethodId getPlatformLoader = agent.handles().getMethodIdOptional(jni, classLoader, "getPlatformClassLoader", "()Ljava/lang/ClassLoader;", true);
12621329
JNIMethodId getAppLoader = agent.handles().getMethodIdOptional(jni, classLoader, "getBuiltinAppClassLoader", "()Ljava/lang/ClassLoader;", true);
12631330
if (getPlatformLoader.isNonNull() && getAppLoader.isNonNull()) { // only on JDK 9 and later
1264-
JNIObjectHandle platformLoader = callObjectMethod(jni, classLoader, getPlatformLoader);
1331+
JNIObjectHandle platformLoader = Support.callObjectMethod(jni, classLoader, getPlatformLoader);
12651332
checkNoException(jni);
1266-
JNIObjectHandle appLoader = callObjectMethod(jni, classLoader, getAppLoader);
1333+
JNIObjectHandle appLoader = Support.callObjectMethod(jni, classLoader, getAppLoader);
12671334
checkNoException(jni);
12681335
guarantee(platformLoader.notEqual(nullHandle()) && appLoader.notEqual(nullHandle()));
12691336

substratevm/src/com.oracle.svm.agent/src/com/oracle/svm/agent/NativeImageAgentJNIHandleSet.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ public class NativeImageAgentJNIHandleSet extends JNIHandleSet {
7272

7373
private JNIMethodId javaLangReflectConstructorDeclaringClassName;
7474

75+
private JNIMethodId javaUtilLocaleToLanguageTag;
76+
private JNIFieldId javaUtilResourceBundleParentField;
77+
private JNIMethodId javaUtilResourceBundleGetLocale;
78+
7579
NativeImageAgentJNIHandleSet(JNIEnvironment env) {
7680
super(env);
7781
javaLangClass = newClassGlobalRef(env, "java/lang/Class");
@@ -179,4 +183,28 @@ JNIMethodId getJavaLangReflectConstructorDeclaringClassName(JNIEnvironment env,
179183
}
180184
return javaLangReflectConstructorDeclaringClassName;
181185
}
186+
187+
public JNIMethodId getJavaUtilLocaleToLanguageTag(JNIEnvironment env) {
188+
if (javaUtilLocaleToLanguageTag.isNull()) {
189+
JNIObjectHandle javaUtilLocale = findClass(env, "java/util/Locale");
190+
javaUtilLocaleToLanguageTag = getMethodId(env, javaUtilLocale, "toLanguageTag", "()Ljava/lang/String;", false);
191+
}
192+
return javaUtilLocaleToLanguageTag;
193+
}
194+
195+
public JNIFieldId getJavaUtilResourceBundleParentField(JNIEnvironment env) {
196+
if (javaUtilResourceBundleParentField.isNull()) {
197+
JNIObjectHandle javaUtilResourceBundle = findClass(env, "java/util/ResourceBundle");
198+
javaUtilResourceBundleParentField = getFieldId(env, javaUtilResourceBundle, "parent", "Ljava/util/ResourceBundle;", false);
199+
}
200+
return javaUtilResourceBundleParentField;
201+
}
202+
203+
public JNIMethodId getJavaUtilResourceBundleGetLocale(JNIEnvironment env) {
204+
if (javaUtilResourceBundleGetLocale.isNull()) {
205+
JNIObjectHandle javaUtilResourceBundle = findClass(env, "java/util/ResourceBundle");
206+
javaUtilResourceBundleGetLocale = getMethodId(env, javaUtilResourceBundle, "getLocale", "()Ljava/util/Locale;", false);
207+
}
208+
return javaUtilResourceBundleGetLocale;
209+
}
182210
}

substratevm/src/com.oracle.svm.configure.test/src/com/oracle/svm/configure/test/config/ResourceConfigurationTest.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2021, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -28,8 +28,10 @@
2828
import java.io.IOException;
2929
import java.io.PipedReader;
3030
import java.io.PipedWriter;
31+
import java.util.Collection;
3132
import java.util.LinkedList;
3233
import java.util.List;
34+
import java.util.Locale;
3335

3436
import org.graalvm.nativeimage.impl.ConfigurationCondition;
3537
import org.junit.Assert;
@@ -99,6 +101,16 @@ public void ignoreResources(ConfigurationCondition condition, String pattern) {
99101
@Override
100102
public void addResourceBundles(ConfigurationCondition condition, String name) {
101103
}
104+
105+
@Override
106+
public void addResourceBundles(ConfigurationCondition condition, String basename, Collection<Locale> locales) {
107+
108+
}
109+
110+
@Override
111+
public void addClassBasedResourceBundle(ConfigurationCondition condition, String basename, String className) {
112+
113+
}
102114
};
103115

104116
ResourceConfigurationParser rcp = new ResourceConfigurationParser(registry, true);

0 commit comments

Comments
 (0)