|
24 | 24 | */
|
25 | 25 | package com.oracle.svm.core.jdk;
|
26 | 26 |
|
| 27 | +import static java.util.Locale.Category.DISPLAY; |
| 28 | +import static java.util.Locale.Category.FORMAT; |
| 29 | + |
27 | 30 | import java.util.Collections;
|
28 | 31 | import java.util.HashMap;
|
29 | 32 | import java.util.Locale;
|
|
35 | 38 | import org.graalvm.nativeimage.ImageSingletons;
|
36 | 39 | import org.graalvm.nativeimage.Platform;
|
37 | 40 | import org.graalvm.nativeimage.Platforms;
|
| 41 | +import org.graalvm.nativeimage.c.type.CCharPointerPointer; |
| 42 | +import org.graalvm.nativeimage.c.type.CTypeConversion; |
38 | 43 | import org.graalvm.nativeimage.impl.RuntimeSystemPropertiesSupport;
|
39 | 44 |
|
| 45 | +import com.oracle.svm.core.LibCHelper; |
40 | 46 | import com.oracle.svm.core.VM;
|
41 | 47 | import com.oracle.svm.core.config.ConfigurationValues;
|
| 48 | +import com.oracle.svm.core.headers.LibCSupport; |
42 | 49 | import com.oracle.svm.core.util.VMError;
|
43 | 50 |
|
44 | 51 | import jdk.graal.compiler.api.replacements.Fold;
|
| 52 | +import jdk.graal.compiler.debug.GraalError; |
45 | 53 |
|
46 | 54 | /**
|
47 | 55 | * This class maintains the system properties at run time.
|
@@ -76,6 +84,13 @@ public abstract class SystemPropertiesSupport implements RuntimeSystemProperties
|
76 | 84 | "java.vm.specification.version"
|
77 | 85 | };
|
78 | 86 |
|
| 87 | + /* The list of field positions in locale_props_t (see locale_str.h). */ |
| 88 | + private static final int LANGUAGE_POSITION = 0; |
| 89 | + private static final int SCRIPT_POSITION = LANGUAGE_POSITION + 1; |
| 90 | + private static final int COUNTRY_POSITION = SCRIPT_POSITION + 1; |
| 91 | + private static final int VARIANT_POSITION = COUNTRY_POSITION + 1; |
| 92 | + private static final int EXTENSION_POSITION = VARIANT_POSITION + 1; |
| 93 | + |
79 | 94 | /** System properties that are lazily computed at run time on first access. */
|
80 | 95 | private final Map<String, Supplier<String>> lazyRuntimeValues;
|
81 | 96 |
|
@@ -139,6 +154,21 @@ protected SystemPropertiesSupport() {
|
139 | 154 | lazyRuntimeValues.put("java.io.tmpdir", this::javaIoTmpDir);
|
140 | 155 | lazyRuntimeValues.put("java.library.path", this::javaLibraryPath);
|
141 | 156 | lazyRuntimeValues.put("os.version", this::osVersionValue);
|
| 157 | + lazyRuntimeValues.put(UserSystemProperty.USER_LANGUAGE, () -> postProcessLocale(UserSystemProperty.USER_LANGUAGE, parseLocale(DISPLAY).language(), null)); |
| 158 | + lazyRuntimeValues.put(UserSystemProperty.USER_LANGUAGE_DISPLAY, () -> postProcessLocale(UserSystemProperty.USER_LANGUAGE, parseLocale(DISPLAY).language(), DISPLAY)); |
| 159 | + lazyRuntimeValues.put(UserSystemProperty.USER_LANGUAGE_FORMAT, () -> postProcessLocale(UserSystemProperty.USER_LANGUAGE, parseLocale(FORMAT).language(), FORMAT)); |
| 160 | + lazyRuntimeValues.put(UserSystemProperty.USER_SCRIPT, () -> postProcessLocale(UserSystemProperty.USER_SCRIPT, parseLocale(DISPLAY).script(), null)); |
| 161 | + lazyRuntimeValues.put(UserSystemProperty.USER_SCRIPT_DISPLAY, () -> postProcessLocale(UserSystemProperty.USER_SCRIPT, parseLocale(DISPLAY).script(), DISPLAY)); |
| 162 | + lazyRuntimeValues.put(UserSystemProperty.USER_SCRIPT_FORMAT, () -> postProcessLocale(UserSystemProperty.USER_SCRIPT, parseLocale(FORMAT).script(), FORMAT)); |
| 163 | + lazyRuntimeValues.put(UserSystemProperty.USER_COUNTRY, () -> postProcessLocale(UserSystemProperty.USER_COUNTRY, parseLocale(DISPLAY).country(), null)); |
| 164 | + lazyRuntimeValues.put(UserSystemProperty.USER_COUNTRY_DISPLAY, () -> postProcessLocale(UserSystemProperty.USER_COUNTRY, parseLocale(DISPLAY).country(), DISPLAY)); |
| 165 | + lazyRuntimeValues.put(UserSystemProperty.USER_COUNTRY_FORMAT, () -> postProcessLocale(UserSystemProperty.USER_COUNTRY, parseLocale(FORMAT).country(), FORMAT)); |
| 166 | + lazyRuntimeValues.put(UserSystemProperty.USER_VARIANT, () -> postProcessLocale(UserSystemProperty.USER_VARIANT, parseLocale(DISPLAY).variant(), null)); |
| 167 | + lazyRuntimeValues.put(UserSystemProperty.USER_VARIANT_DISPLAY, () -> postProcessLocale(UserSystemProperty.USER_VARIANT, parseLocale(DISPLAY).variant(), DISPLAY)); |
| 168 | + lazyRuntimeValues.put(UserSystemProperty.USER_VARIANT_FORMAT, () -> postProcessLocale(UserSystemProperty.USER_VARIANT, parseLocale(FORMAT).variant(), FORMAT)); |
| 169 | + lazyRuntimeValues.put(UserSystemProperty.USER_EXTENSIONS, () -> postProcessLocale(UserSystemProperty.USER_EXTENSIONS, parseLocale(DISPLAY).extensions(), null)); |
| 170 | + lazyRuntimeValues.put(UserSystemProperty.USER_EXTENSIONS_DISPLAY, () -> postProcessLocale(UserSystemProperty.USER_EXTENSIONS, parseLocale(DISPLAY).extensions(), DISPLAY)); |
| 171 | + lazyRuntimeValues.put(UserSystemProperty.USER_EXTENSIONS_FORMAT, () -> postProcessLocale(UserSystemProperty.USER_EXTENSIONS, parseLocale(FORMAT).extensions(), FORMAT)); |
142 | 172 |
|
143 | 173 | String targetName = System.getProperty("svm.targetName");
|
144 | 174 | if (targetName != null) {
|
@@ -183,6 +213,12 @@ protected String getProperty(String key) {
|
183 | 213 | return properties.getProperty(key);
|
184 | 214 | }
|
185 | 215 |
|
| 216 | + protected String getSavedProperty(String key, String defaultValue) { |
| 217 | + initializeLazyValue(key); |
| 218 | + String value = savedProperties.get(key); |
| 219 | + return value != null ? value : defaultValue; |
| 220 | + } |
| 221 | + |
186 | 222 | public void setProperties(Properties props) {
|
187 | 223 | // Flush lazy values into savedProperties
|
188 | 224 | ensureFullyInitialized();
|
@@ -235,10 +271,14 @@ private void initializeLazyValue(String key) {
|
235 | 271 | * manual updates of the same property key.
|
236 | 272 | */
|
237 | 273 | String value = lazyRuntimeValues.get(key).get();
|
238 |
| - if (properties.putIfAbsent(key, value) == null) { |
239 |
| - synchronized (savedProperties) { |
240 |
| - savedProperties.put(key, value); |
241 |
| - } |
| 274 | + setRawProperty(key, value); |
| 275 | + } |
| 276 | + } |
| 277 | + |
| 278 | + private void setRawProperty(String key, String value) { |
| 279 | + if (value != null && properties.putIfAbsent(key, value) == null) { |
| 280 | + synchronized (savedProperties) { |
| 281 | + savedProperties.put(key, value); |
242 | 282 | }
|
243 | 283 | }
|
244 | 284 | }
|
@@ -318,4 +358,90 @@ protected String osNameValue() {
|
318 | 358 | }
|
319 | 359 |
|
320 | 360 | protected abstract String osVersionValue();
|
| 361 | + |
| 362 | + public record LocaleEncoding(String language, String script, String country, String variant, String extensions) { |
| 363 | + private LocaleEncoding(CCharPointerPointer properties) { |
| 364 | + this(fromCStringArray(properties, LANGUAGE_POSITION), |
| 365 | + fromCStringArray(properties, SCRIPT_POSITION), |
| 366 | + fromCStringArray(properties, COUNTRY_POSITION), |
| 367 | + fromCStringArray(properties, VARIANT_POSITION), |
| 368 | + fromCStringArray(properties, EXTENSION_POSITION)); |
| 369 | + } |
| 370 | + |
| 371 | + private static String fromCStringArray(CCharPointerPointer cString, int index) { |
| 372 | + if (cString.isNull()) { |
| 373 | + return null; |
| 374 | + } |
| 375 | + return CTypeConversion.toJavaString(cString.read(index)); |
| 376 | + } |
| 377 | + } |
| 378 | + |
| 379 | + private LocaleEncoding displayLocale; |
| 380 | + |
| 381 | + private LocaleEncoding formatLocale; |
| 382 | + |
| 383 | + protected LocaleEncoding parseLocale(Locale.Category category) { |
| 384 | + if (!ImageSingletons.contains(LibCSupport.class)) { |
| 385 | + /* If native calls are not supported, just return fixed values. */ |
| 386 | + return new LocaleEncoding("en", "", "US", "", ""); |
| 387 | + } |
| 388 | + switch (category) { |
| 389 | + case DISPLAY -> { |
| 390 | + if (displayLocale == null) { |
| 391 | + displayLocale = new LocaleEncoding(LibCHelper.Locale.parseDisplayLocale()); |
| 392 | + } |
| 393 | + return displayLocale; |
| 394 | + } |
| 395 | + case FORMAT -> { |
| 396 | + if (formatLocale == null) { |
| 397 | + formatLocale = new LocaleEncoding(LibCHelper.Locale.parseFormatLocale()); |
| 398 | + } |
| 399 | + return formatLocale; |
| 400 | + } |
| 401 | + default -> throw new GraalError("Unknown locale category: " + category + "."); |
| 402 | + } |
| 403 | + } |
| 404 | + |
| 405 | + private String postProcessLocale(String base, String value, Locale.Category category) { |
| 406 | + if (category == null) { |
| 407 | + /* user.xxx property */ |
| 408 | + String baseValue = null; |
| 409 | + if (value != null) { |
| 410 | + setRawProperty(base, value); |
| 411 | + baseValue = value; |
| 412 | + } |
| 413 | + return baseValue; |
| 414 | + } |
| 415 | + switch (category) { |
| 416 | + case DISPLAY, FORMAT -> { |
| 417 | + /* user.xxx.(display|format) property */ |
| 418 | + String baseValue = getProperty(base); |
| 419 | + if (baseValue == null && value != null) { |
| 420 | + setRawProperty(base + '.' + category.name().toLowerCase(Locale.ROOT), value); |
| 421 | + return value; |
| 422 | + } |
| 423 | + return null; |
| 424 | + } |
| 425 | + default -> throw new GraalError("Unknown locale category: " + category + "."); |
| 426 | + } |
| 427 | + } |
| 428 | + |
| 429 | + public static class UserSystemProperty { |
| 430 | + public static final String USER_LANGUAGE = "user.language"; |
| 431 | + public static final String USER_LANGUAGE_DISPLAY = USER_LANGUAGE + ".display"; |
| 432 | + public static final String USER_LANGUAGE_FORMAT = USER_LANGUAGE + ".format"; |
| 433 | + public static final String USER_SCRIPT = "user.script"; |
| 434 | + public static final String USER_SCRIPT_DISPLAY = USER_SCRIPT + ".display"; |
| 435 | + public static final String USER_SCRIPT_FORMAT = USER_SCRIPT + ".format"; |
| 436 | + public static final String USER_COUNTRY = "user.country"; |
| 437 | + public static final String USER_COUNTRY_DISPLAY = USER_COUNTRY + ".display"; |
| 438 | + public static final String USER_COUNTRY_FORMAT = USER_COUNTRY + ".format"; |
| 439 | + public static final String USER_VARIANT = "user.variant"; |
| 440 | + public static final String USER_VARIANT_DISPLAY = USER_VARIANT + ".display"; |
| 441 | + public static final String USER_VARIANT_FORMAT = USER_VARIANT + ".format"; |
| 442 | + public static final String USER_EXTENSIONS = "user.extensions"; |
| 443 | + public static final String USER_EXTENSIONS_DISPLAY = USER_EXTENSIONS + ".display"; |
| 444 | + public static final String USER_EXTENSIONS_FORMAT = USER_EXTENSIONS + ".format"; |
| 445 | + public static final String USER_REGION = "user.region"; |
| 446 | + } |
321 | 447 | }
|
0 commit comments