|
24 | 24 | */
|
25 | 25 | package jdk.graal.compiler.libgraal;
|
26 | 26 |
|
| 27 | +import java.io.IOException; |
27 | 28 | import java.text.DateFormatSymbols;
|
| 29 | +import java.time.temporal.TemporalAccessor; |
| 30 | +import java.util.Formatter; |
28 | 31 | import java.util.Locale;
|
29 |
| -import java.util.Set; |
30 | 32 |
|
31 | 33 | import com.oracle.svm.core.annotate.Alias;
|
32 | 34 | import com.oracle.svm.core.annotate.RecomputeFieldValue;
|
@@ -100,37 +102,132 @@ static Class<?> findBootstrapClassOrNull(String name) {
|
100 | 102 | }
|
101 | 103 | }
|
102 | 104 |
|
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 | + } |
129 | 140 |
|
| 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 | + */ |
130 | 189 | @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 | + } |
133 | 199 | }
|
| 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); |
134 | 231 | }
|
135 | 232 |
|
136 | 233 | @TargetClass(value = java.text.DateFormatSymbols.class, onlyWith = LibGraalFeature.IsEnabled.class)
|
|
0 commit comments