Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-52096] Set default locale at run-time. #9694

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions substratevm/mx.substratevm/suite.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@
"sun.security.util",
"sun.text.spi",
"sun.util",
"sun.util.locale",
"sun.util.calendar",
"sun.util.locale.provider",
"sun.util.resources",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CCharPointerPointer;

import com.oracle.svm.core.util.BasedOnJDKFile;

@CLibrary(value = "libchelper", requireStatic = true, dependsOn = "java")
public class LibCHelper {
@CFunction(transition = Transition.NO_TRANSITION)
Expand All @@ -39,4 +41,18 @@ public class LibCHelper {
// Checkstyle: stop
public static native CCharPointer SVM_FindJavaTZmd(CCharPointer tzMappings, int length);
// Checkstyle: start

@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+13/src/java.base/unix/native/libjava/locale_str.h")
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+13/src/java.base/windows/native/libjava/locale_str.h")
public static class Locale {
@CFunction(transition = Transition.TO_NATIVE)
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+13/src/java.base/unix/native/libjava/java_props_md.c#L93-L540")
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+13/src/java.base/windows/native/libjava/java_props_md.c#L257-L713")
public static native CCharPointerPointer parseDisplayLocale();

@CFunction(transition = Transition.TO_NATIVE)
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+13/src/java.base/unix/native/libjava/java_props_md.c#L93-L540")
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+13/src/java.base/windows/native/libjava/java_props_md.c#L257-L713")
public static native CCharPointerPointer parseFormatLocale();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
*/
package com.oracle.svm.core.jdk;

import static java.util.Locale.Category.DISPLAY;
import static java.util.Locale.Category.FORMAT;

import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
Expand All @@ -35,13 +38,18 @@
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.c.type.CCharPointerPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion;
import org.graalvm.nativeimage.impl.RuntimeSystemPropertiesSupport;

import com.oracle.svm.core.LibCHelper;
import com.oracle.svm.core.VM;
import com.oracle.svm.core.config.ConfigurationValues;
import com.oracle.svm.core.headers.LibCSupport;
import com.oracle.svm.core.util.VMError;

import jdk.graal.compiler.api.replacements.Fold;
import jdk.graal.compiler.debug.GraalError;

/**
* This class maintains the system properties at run time.
Expand Down Expand Up @@ -76,6 +84,13 @@ public abstract class SystemPropertiesSupport implements RuntimeSystemProperties
"java.vm.specification.version"
};

/* The list of field positions in locale_props_t (see locale_str.h). */
private static final int LANGUAGE_POSITION = 0;
private static final int SCRIPT_POSITION = LANGUAGE_POSITION + 1;
private static final int COUNTRY_POSITION = SCRIPT_POSITION + 1;
private static final int VARIANT_POSITION = COUNTRY_POSITION + 1;
private static final int EXTENSION_POSITION = VARIANT_POSITION + 1;

/** System properties that are lazily computed at run time on first access. */
private final Map<String, Supplier<String>> lazyRuntimeValues;

Expand Down Expand Up @@ -139,6 +154,21 @@ protected SystemPropertiesSupport() {
lazyRuntimeValues.put("java.io.tmpdir", this::javaIoTmpDir);
lazyRuntimeValues.put("java.library.path", this::javaLibraryPath);
lazyRuntimeValues.put("os.version", this::osVersionValue);
lazyRuntimeValues.put(UserSystemProperty.USER_LANGUAGE, () -> postProcessLocale(UserSystemProperty.USER_LANGUAGE, parseLocale(DISPLAY).language(), null));
lazyRuntimeValues.put(UserSystemProperty.USER_LANGUAGE_DISPLAY, () -> postProcessLocale(UserSystemProperty.USER_LANGUAGE, parseLocale(DISPLAY).language(), DISPLAY));
lazyRuntimeValues.put(UserSystemProperty.USER_LANGUAGE_FORMAT, () -> postProcessLocale(UserSystemProperty.USER_LANGUAGE, parseLocale(FORMAT).language(), FORMAT));
lazyRuntimeValues.put(UserSystemProperty.USER_SCRIPT, () -> postProcessLocale(UserSystemProperty.USER_SCRIPT, parseLocale(DISPLAY).script(), null));
lazyRuntimeValues.put(UserSystemProperty.USER_SCRIPT_DISPLAY, () -> postProcessLocale(UserSystemProperty.USER_SCRIPT, parseLocale(DISPLAY).script(), DISPLAY));
lazyRuntimeValues.put(UserSystemProperty.USER_SCRIPT_FORMAT, () -> postProcessLocale(UserSystemProperty.USER_SCRIPT, parseLocale(FORMAT).script(), FORMAT));
lazyRuntimeValues.put(UserSystemProperty.USER_COUNTRY, () -> postProcessLocale(UserSystemProperty.USER_COUNTRY, parseLocale(DISPLAY).country(), null));
lazyRuntimeValues.put(UserSystemProperty.USER_COUNTRY_DISPLAY, () -> postProcessLocale(UserSystemProperty.USER_COUNTRY, parseLocale(DISPLAY).country(), DISPLAY));
lazyRuntimeValues.put(UserSystemProperty.USER_COUNTRY_FORMAT, () -> postProcessLocale(UserSystemProperty.USER_COUNTRY, parseLocale(FORMAT).country(), FORMAT));
lazyRuntimeValues.put(UserSystemProperty.USER_VARIANT, () -> postProcessLocale(UserSystemProperty.USER_VARIANT, parseLocale(DISPLAY).variant(), null));
lazyRuntimeValues.put(UserSystemProperty.USER_VARIANT_DISPLAY, () -> postProcessLocale(UserSystemProperty.USER_VARIANT, parseLocale(DISPLAY).variant(), DISPLAY));
lazyRuntimeValues.put(UserSystemProperty.USER_VARIANT_FORMAT, () -> postProcessLocale(UserSystemProperty.USER_VARIANT, parseLocale(FORMAT).variant(), FORMAT));
lazyRuntimeValues.put(UserSystemProperty.USER_EXTENSIONS, () -> postProcessLocale(UserSystemProperty.USER_EXTENSIONS, parseLocale(DISPLAY).extensions(), null));
lazyRuntimeValues.put(UserSystemProperty.USER_EXTENSIONS_DISPLAY, () -> postProcessLocale(UserSystemProperty.USER_EXTENSIONS, parseLocale(DISPLAY).extensions(), DISPLAY));
lazyRuntimeValues.put(UserSystemProperty.USER_EXTENSIONS_FORMAT, () -> postProcessLocale(UserSystemProperty.USER_EXTENSIONS, parseLocale(FORMAT).extensions(), FORMAT));

String targetName = System.getProperty("svm.targetName");
if (targetName != null) {
Expand Down Expand Up @@ -183,6 +213,12 @@ protected String getProperty(String key) {
return properties.getProperty(key);
}

protected String getSavedProperty(String key, String defaultValue) {
initializeLazyValue(key);
String value = savedProperties.get(key);
return value != null ? value : defaultValue;
}

public void setProperties(Properties props) {
// Flush lazy values into savedProperties
ensureFullyInitialized();
Expand Down Expand Up @@ -235,10 +271,14 @@ private void initializeLazyValue(String key) {
* manual updates of the same property key.
*/
String value = lazyRuntimeValues.get(key).get();
if (properties.putIfAbsent(key, value) == null) {
synchronized (savedProperties) {
savedProperties.put(key, value);
}
setRawProperty(key, value);
}
}

private void setRawProperty(String key, String value) {
if (value != null && properties.putIfAbsent(key, value) == null) {
synchronized (savedProperties) {
savedProperties.put(key, value);
}
}
}
Expand Down Expand Up @@ -318,4 +358,90 @@ protected String osNameValue() {
}

protected abstract String osVersionValue();

public record LocaleEncoding(String language, String script, String country, String variant, String extensions) {
private LocaleEncoding(CCharPointerPointer properties) {
this(fromCStringArray(properties, LANGUAGE_POSITION),
fromCStringArray(properties, SCRIPT_POSITION),
fromCStringArray(properties, COUNTRY_POSITION),
fromCStringArray(properties, VARIANT_POSITION),
fromCStringArray(properties, EXTENSION_POSITION));
}

private static String fromCStringArray(CCharPointerPointer cString, int index) {
if (cString.isNull()) {
return null;
}
return CTypeConversion.toJavaString(cString.read(index));
}
}

private LocaleEncoding displayLocale;

private LocaleEncoding formatLocale;

protected LocaleEncoding parseLocale(Locale.Category category) {
if (!ImageSingletons.contains(LibCSupport.class)) {
/* If native calls are not supported, just return fixed values. */
return new LocaleEncoding("en", "", "US", "", "");
}
switch (category) {
case DISPLAY -> {
if (displayLocale == null) {
displayLocale = new LocaleEncoding(LibCHelper.Locale.parseDisplayLocale());
}
return displayLocale;
}
case FORMAT -> {
if (formatLocale == null) {
formatLocale = new LocaleEncoding(LibCHelper.Locale.parseFormatLocale());
}
return formatLocale;
}
default -> throw new GraalError("Unknown locale category: " + category + ".");
}
}

private String postProcessLocale(String base, String value, Locale.Category category) {
if (category == null) {
/* user.xxx property */
String baseValue = null;
if (value != null) {
setRawProperty(base, value);
baseValue = value;
}
return baseValue;
}
switch (category) {
case DISPLAY, FORMAT -> {
/* user.xxx.(display|format) property */
String baseValue = getProperty(base);
if (baseValue == null && value != null) {
setRawProperty(base + '.' + category.name().toLowerCase(Locale.ROOT), value);
return value;
}
return null;
}
default -> throw new GraalError("Unknown locale category: " + category + ".");
}
}

public static class UserSystemProperty {
public static final String USER_LANGUAGE = "user.language";
public static final String USER_LANGUAGE_DISPLAY = USER_LANGUAGE + ".display";
public static final String USER_LANGUAGE_FORMAT = USER_LANGUAGE + ".format";
public static final String USER_SCRIPT = "user.script";
public static final String USER_SCRIPT_DISPLAY = USER_SCRIPT + ".display";
public static final String USER_SCRIPT_FORMAT = USER_SCRIPT + ".format";
public static final String USER_COUNTRY = "user.country";
public static final String USER_COUNTRY_DISPLAY = USER_COUNTRY + ".display";
public static final String USER_COUNTRY_FORMAT = USER_COUNTRY + ".format";
public static final String USER_VARIANT = "user.variant";
public static final String USER_VARIANT_DISPLAY = USER_VARIANT + ".display";
public static final String USER_VARIANT_FORMAT = USER_VARIANT + ".format";
public static final String USER_EXTENSIONS = "user.extensions";
public static final String USER_EXTENSIONS_DISPLAY = USER_EXTENSIONS + ".display";
public static final String USER_EXTENSIONS_FORMAT = USER_EXTENSIONS + ".format";
public static final String USER_REGION = "user.region";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

import java.util.function.BooleanSupplier;

import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.annotate.Alias;
import com.oracle.svm.core.annotate.RecomputeFieldValue;
import com.oracle.svm.core.annotate.Substitute;
import com.oracle.svm.core.annotate.TargetClass;
import com.oracle.svm.core.annotate.TargetElement;
Expand All @@ -44,6 +47,93 @@
@SuppressWarnings("unused")
final class Target_jdk_internal_util_StaticProperty {

// Checkstyle: stop
@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_LANGUAGE;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_LANGUAGE_DISPLAY;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_LANGUAGE_FORMAT;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_SCRIPT;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_SCRIPT_DISPLAY;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_SCRIPT_FORMAT;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_COUNTRY;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_COUNTRY_DISPLAY;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_COUNTRY_FORMAT;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_VARIANT;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_VARIANT_DISPLAY;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_VARIANT_FORMAT;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_EXTENSIONS;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_EXTENSIONS_DISPLAY;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_EXTENSIONS_FORMAT;

@Alias//
@RecomputeFieldValue(kind = RecomputeFieldValue.Kind.FromAlias)//
public static String USER_REGION;
// Checkstyle: resume

static {
if (!SubstrateUtil.HOSTED) {
USER_LANGUAGE = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_LANGUAGE, "en");
USER_LANGUAGE_DISPLAY = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_LANGUAGE_DISPLAY, USER_LANGUAGE);
USER_LANGUAGE_FORMAT = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_SCRIPT_FORMAT, USER_LANGUAGE);
USER_SCRIPT = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_SCRIPT, "");
USER_SCRIPT_DISPLAY = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_SCRIPT_DISPLAY, USER_SCRIPT);
USER_SCRIPT_FORMAT = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_SCRIPT_FORMAT, USER_SCRIPT);
USER_COUNTRY = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_COUNTRY, "");
USER_COUNTRY_DISPLAY = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_COUNTRY_DISPLAY, USER_COUNTRY);
USER_COUNTRY_FORMAT = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_COUNTRY_FORMAT, USER_COUNTRY);
USER_VARIANT = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_VARIANT, "");
USER_VARIANT_DISPLAY = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_VARIANT_DISPLAY, USER_VARIANT);
USER_VARIANT_FORMAT = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_VARIANT_FORMAT, USER_VARIANT);
USER_EXTENSIONS = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_EXTENSIONS, "");
USER_EXTENSIONS_DISPLAY = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_EXTENSIONS_DISPLAY, USER_EXTENSIONS);
USER_EXTENSIONS_FORMAT = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_EXTENSIONS_FORMAT, USER_EXTENSIONS);
USER_REGION = SystemPropertiesSupport.singleton().getSavedProperty(SystemPropertiesSupport.UserSystemProperty.USER_REGION, "");
}
}

@Substitute
private static String javaHome() {
/* Native images do not have a Java home directory. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ public class BundleContentSubstitutedLocalizationSupport extends LocalizationSup

private final Set<String> existingBundles = ConcurrentHashMap.newKeySet();

public BundleContentSubstitutedLocalizationSupport(Locale defaultLocale, Set<Locale> locales, Charset defaultCharset, List<String> requestedPatterns, ForkJoinPool pool) {
super(defaultLocale, locales, defaultCharset);
public BundleContentSubstitutedLocalizationSupport(Set<Locale> locales, Charset defaultCharset, List<String> requestedPatterns, ForkJoinPool pool) {
super(locales, defaultCharset);
this.pool = pool;
this.compressBundlesPatterns = parseCompressBundlePatterns(requestedPatterns);
}
Expand Down Expand Up @@ -112,8 +112,7 @@ private void storeBundleContentOf(ResourceBundle bundle) {

@Platforms(Platform.HOSTED_ONLY.class)
private StoredBundle processBundle(ResourceBundle bundle) {
boolean isInDefaultLocale = bundle.getLocale().equals(defaultLocale);
if (!isInDefaultLocale && shouldCompressBundle(bundle) && GzipBundleCompression.canCompress(bundle)) {
if (shouldCompressBundle(bundle) && GzipBundleCompression.canCompress(bundle)) {
return GzipBundleCompression.compress(bundle);
}
Map<String, Object> content = BundleSerializationUtils.extractContent(bundle);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,6 @@ public class LocalizationSupport {

public final Map<String, Charset> charsets = new HashMap<>();

public final Locale defaultLocale;

public final Locale[] allLocales;

public final Set<String> supportedLanguageTags;
Expand All @@ -91,8 +89,7 @@ public class LocalizationSupport {

private final EconomicMap<String, RuntimeConditionSet> registeredBundles = ImageHeapMap.create();

public LocalizationSupport(Locale defaultLocale, Set<Locale> locales, Charset defaultCharset) {
this.defaultLocale = defaultLocale;
public LocalizationSupport(Set<Locale> locales, Charset defaultCharset) {
this.allLocales = locales.toArray(new Locale[0]);
this.defaultCharset = defaultCharset;
this.supportedLanguageTags = locales.stream().map(Locale::toString).collect(Collectors.toSet());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ private record BundleCacheKey(String bundleName, Locale locale) {

final Map<BundleCacheKey, ResourceBundle> resourceBundles = new HashMap<>();

public OptimizedLocalizationSupport(Locale defaultLocale, Set<Locale> locales, Charset defaultCharset) {
super(defaultLocale, locales, defaultCharset);
public OptimizedLocalizationSupport(Set<Locale> locales, Charset defaultCharset) {
super(locales, defaultCharset);
}

@Override
Expand Down
Loading
Loading