Skip to content

Commit 0ff6df6

Browse files
committed
Cut out aspects of Formatter$FormatSpecifier that require dynamic class lookup
1 parent 416b2af commit 0ff6df6

File tree

1 file changed

+126
-29
lines changed

1 file changed

+126
-29
lines changed

compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalSubstitutions.java

Lines changed: 126 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424
*/
2525
package jdk.graal.compiler.libgraal;
2626

27+
import java.io.IOException;
2728
import java.text.DateFormatSymbols;
29+
import java.time.temporal.TemporalAccessor;
30+
import java.util.Formatter;
2831
import java.util.Locale;
29-
import java.util.Set;
3032

3133
import com.oracle.svm.core.annotate.Alias;
3234
import com.oracle.svm.core.annotate.RecomputeFieldValue;
@@ -100,37 +102,132 @@ static Class<?> findBootstrapClassOrNull(String name) {
100102
}
101103
}
102104

103-
/**
104-
* Method {@link Module#getPackages()} is reachable via {@link java.util.Formatter}:
105-
*
106-
* <pre>
107-
* java.lang.Module.getPackages(Module.java:1166)
108-
* at java.lang.Module.getResourceAsStream(Module.java:1687)
109-
* at sun.util.resources.BreakIteratorResourceBundle.handleGetObject(BreakIteratorResourceBundle.java:72)
110-
* at java.util.ResourceBundle.getObject(ResourceBundle.java:549)
111-
* at java.util.ResourceBundle.getStringArray(ResourceBundle.java:532)
112-
* at sun.util.locale.provider.LocaleResources.getNumberStrings(LocaleResources.java:244)
113-
* at sun.util.locale.provider.LocaleResources.getNumberPatterns(LocaleResources.java:544)
114-
* at java.util.Formatter$FormatSpecifier.localizedMagnitude(Formatter.java:4719)
115-
* at java.util.Formatter$FormatSpecifier.print(Formatter.java:3511)
116-
* at java.util.Formatter$FormatSpecifier.print(Formatter.java:3496)
117-
* at java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:3194)
118-
* at java.util.Formatter$FormatSpecifier.print(Formatter.java:3155)
119-
* at java.util.Formatter.format(Formatter.java:2754)
120-
* </pre>
121-
*
122-
* It makes use of classloading-logic that we do not want to have in libgraal. Since this
123-
* code-path is not needed at runtime we can replace the implementation with a simple stub.
124-
*/
125-
@TargetClass(value = java.lang.Module.class, onlyWith = LibGraalFeature.IsEnabled.class)
126-
static final class Target_java_lang_Module {
127-
@Alias
128-
public native String getName();
105+
@TargetClass(className = "java.util.Formatter$FormatSpecifier", onlyWith = LibGraalFeature.IsEnabled.class)
106+
static final class Target_java_util_Formatter_FormatSpecifier {
107+
108+
/**
109+
* Custom version of
110+
* {@code java.util.Formatter.FormatSpecifier#localizedMagnitude(java.util.Formatter, java.lang.StringBuilder, java.lang.CharSequence, int, int, int, java.util.Locale)}
111+
* where the given locale is unconditionally replaced with {@code null}). Since the original
112+
* method was already able to accept `null` as locale, the substitution is straightforward.
113+
* The substitution does not contain any code path that requires dynamic class or resource
114+
* lookup.
115+
*/
116+
@Substitute
117+
StringBuilder localizedMagnitude(Formatter fmt, StringBuilder sb,
118+
CharSequence value, final int offset, int f, int width,
119+
Locale unused) {
120+
if (sb == null) {
121+
sb = new StringBuilder();
122+
}
123+
int begin = sb.length();
124+
125+
char zero = '0'; // getZero(l);
126+
127+
// determine localized grouping separator and size
128+
char grpSep = '\0';
129+
int grpSize = -1;
130+
char decSep = '\0';
131+
132+
int len = value.length();
133+
int dot = len;
134+
for (int j = offset; j < len; j++) {
135+
if (value.charAt(j) == '.') {
136+
dot = j;
137+
break;
138+
}
139+
}
129140

141+
if (dot < len) {
142+
decSep = '.'; // getDecimalSeparator(l);
143+
}
144+
145+
if (Target_java_util_Formatter_Flags.contains(f, Target_java_util_Formatter_Flags.GROUP)) {
146+
grpSep = ','; // getGroupingSeparator(l);
147+
148+
Locale l = null;
149+
if (l == null || l.equals(Locale.US)) {
150+
grpSize = 3;
151+
} else {
152+
throw GraalError.shouldNotReachHere("localizedMagnitude with l != null");
153+
}
154+
}
155+
156+
// localize the digits inserting group separators as necessary
157+
for (int j = offset; j < len; j++) {
158+
if (j == dot) {
159+
sb.append(decSep);
160+
// no more group separators after the decimal separator
161+
grpSep = '\0';
162+
continue;
163+
}
164+
165+
char c = value.charAt(j);
166+
sb.append((char) ((c - '0') + zero));
167+
if (grpSep != '\0' && j != dot - 1 && ((dot - j) % grpSize == 1)) {
168+
sb.append(grpSep);
169+
}
170+
}
171+
172+
// apply zero padding
173+
if (width > sb.length() && Target_java_util_Formatter_Flags.contains(f, Target_java_util_Formatter_Flags.ZERO_PAD)) {
174+
String zeros = String.valueOf(zero).repeat(width - sb.length());
175+
sb.insert(begin, zeros);
176+
}
177+
178+
return sb;
179+
}
180+
181+
/**
182+
* Custom version of
183+
* {@code java.util.Formatter.FormatSpecifier#print(java.util.Formatter, java.time.temporal.TemporalAccessor, char, java.util.Locale)}
184+
* where the given locale is unconditionally replaced with {@code null}). Since the original
185+
* method was already able to accept `null` as locale, the substitution is straightforward.
186+
* The substitution does not contain any code path that requires dynamic class or resource
187+
* lookup.
188+
*/
130189
@Substitute
131-
public Set<String> getPackages() {
132-
throw GraalError.unimplemented("Module.getPackages() for module " + getName() + " (class loading not supported in libgraal)");
190+
void print(Target_java_util_Formatter fmt, TemporalAccessor t, char c, Locale unused) throws IOException {
191+
StringBuilder sb = new StringBuilder();
192+
print(fmt, sb, t, c, null);
193+
// justify based on width
194+
if (Target_java_util_Formatter_Flags.contains(flags, Target_java_util_Formatter_Flags.UPPERCASE)) {
195+
appendJustified(fmt.a, sb.toString().toUpperCase(Locale.ROOT));
196+
} else {
197+
appendJustified(fmt.a, sb);
198+
}
133199
}
200+
201+
@Alias
202+
native Appendable print(Target_java_util_Formatter fmt, StringBuilder sb, TemporalAccessor t, char c,
203+
Locale l) throws IOException;
204+
205+
@Alias
206+
native void appendJustified(Appendable a, CharSequence cs) throws IOException;
207+
208+
@Alias //
209+
int flags;
210+
}
211+
212+
@TargetClass(className = "java.util.Formatter", onlyWith = LibGraalFeature.IsEnabled.class)
213+
static final class Target_java_util_Formatter {
214+
@Alias //
215+
Appendable a;
216+
}
217+
218+
@TargetClass(className = "java.util.Formatter$Flags", onlyWith = LibGraalFeature.IsEnabled.class)
219+
static final class Target_java_util_Formatter_Flags {
220+
// Checkstyle: stop
221+
@Alias //
222+
static int ZERO_PAD;
223+
@Alias //
224+
static int GROUP;
225+
@Alias //
226+
static int UPPERCASE;
227+
// Checkstyle: resume
228+
229+
@Alias
230+
static native boolean contains(int flags, int f);
134231
}
135232

136233
@TargetClass(value = java.text.DateFormatSymbols.class, onlyWith = LibGraalFeature.IsEnabled.class)

0 commit comments

Comments
 (0)