From 1d3025cdf04a8f15190e7e09ac5f7add7a61032e Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Mon, 9 Mar 2020 12:30:17 -0700 Subject: [PATCH 01/13] [GR-21741] Adds support for custom timezones through the IncludeTimeZones flag --- .../oracle/svm/core/jdk/TimeZoneSubstitutions.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java index 1fca7379397a..ef287cee62c4 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Map; import java.util.TimeZone; +import java.util.function.Function; import org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.hosted.Feature; @@ -82,7 +83,7 @@ static class Options { public static final HostedOptionKey<Boolean> IncludeAllTimeZones = new HostedOptionKey<>(false); @Option(help = "The time zones, in addition to the default zone of the host, that will be pre-initialized in the image.")// - public static final HostedOptionKey<String[]> IncludeTimeZones = new HostedOptionKey<>(new String[]{"GMT", "UTC", defaultZone.getID()}); + public static final HostedOptionKey<String> IncludeTimeZones = new HostedOptionKey<>("GMT,UTC," + defaultZone.getID()); } @Override @@ -91,11 +92,14 @@ public void afterRegistration(AfterRegistrationAccess access) { if (Options.IncludeAllTimeZones.getValue()) { supportedZoneIDs = TimeZone.getAvailableIDs(); } else { - supportedZoneIDs = Options.IncludeTimeZones.getValue(); + supportedZoneIDs = Options.IncludeTimeZones.getValue().split(","); } Map<String, TimeZone> supportedZones = Arrays.stream(supportedZoneIDs) - .map(TimeZone::getTimeZone) - .collect(toMap(TimeZone::getID, tz -> tz, (tz1, tz2) -> tz1)); + .collect(toMap(Function.identity(), TimeZone::getTimeZone, (tz1, tz2) -> tz1)); + + // In al cases put "GMT", "UTC" and default. The JDK returns the GMT time zone for missing + // time zone ids + Arrays.asList("GMT", "UTC", Options.defaultZone.getID()).forEach(id -> supportedZones.putIfAbsent(id, TimeZone.getTimeZone(id))); ImageSingletons.add(TimeZoneSupport.class, new TimeZoneSupport(supportedZones, Options.defaultZone)); } } From fcc93ff69217d7064f2579e1dd06de9cb1288c73 Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Tue, 10 Mar 2020 12:23:34 -0700 Subject: [PATCH 02/13] [GR-21741] Deleting TimeZoneSubstitutions --- .../svm/core/jdk/TimeZoneSubstitutions.java | 111 ------------------ .../src/JvmFuncs.c | 12 ++ .../src/JvmFuncs.c | 12 ++ 3 files changed, 24 insertions(+), 111 deletions(-) delete mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java deleted file mode 100644 index ef287cee62c4..000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.core.jdk; - -import static java.util.stream.Collectors.toMap; - -import java.util.Arrays; -import java.util.Map; -import java.util.TimeZone; -import java.util.function.Function; - -import org.graalvm.compiler.options.Option; -import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.nativeimage.ImageSingletons; - -import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.annotate.Substitute; -import com.oracle.svm.core.annotate.TargetClass; -import com.oracle.svm.core.option.HostedOptionKey; - -final class TimeZoneSupport { - - final Map<String, TimeZone> zones; - TimeZone defaultZone; - - TimeZoneSupport(Map<String, TimeZone> zones, TimeZone defaultZone) { - this.zones = zones; - this.defaultZone = defaultZone; - } - - public static TimeZoneSupport instance() { - return ImageSingletons.lookup(TimeZoneSupport.class); - } -} - -@TargetClass(java.util.TimeZone.class) -final class Target_java_util_TimeZone { - - @Substitute - private static TimeZone getDefaultRef() { - return TimeZoneSupport.instance().defaultZone; - } - - @Substitute - private static void setDefault(TimeZone zone) { - TimeZoneSupport.instance().defaultZone = zone; - } - - @Substitute - public static TimeZone getTimeZone(String id) { - return TimeZoneSupport.instance().zones.getOrDefault(id, TimeZoneSupport.instance().zones.get("GMT")); - } -} - -@AutomaticFeature -final class TimeZoneFeature implements Feature { - static class Options { - private static final TimeZone defaultZone = TimeZone.getDefault(); - - @Option(help = "When true, all time zones will be pre-initialized in the image.")// - public static final HostedOptionKey<Boolean> IncludeAllTimeZones = new HostedOptionKey<>(false); - - @Option(help = "The time zones, in addition to the default zone of the host, that will be pre-initialized in the image.")// - public static final HostedOptionKey<String> IncludeTimeZones = new HostedOptionKey<>("GMT,UTC," + defaultZone.getID()); - } - - @Override - public void afterRegistration(AfterRegistrationAccess access) { - final String[] supportedZoneIDs; - if (Options.IncludeAllTimeZones.getValue()) { - supportedZoneIDs = TimeZone.getAvailableIDs(); - } else { - supportedZoneIDs = Options.IncludeTimeZones.getValue().split(","); - } - Map<String, TimeZone> supportedZones = Arrays.stream(supportedZoneIDs) - .collect(toMap(Function.identity(), TimeZone::getTimeZone, (tz1, tz2) -> tz1)); - - // In al cases put "GMT", "UTC" and default. The JDK returns the GMT time zone for missing - // time zone ids - Arrays.asList("GMT", "UTC", Options.defaultZone.getID()).forEach(id -> supportedZones.putIfAbsent(id, TimeZone.getTimeZone(id))); - ImageSingletons.add(TimeZoneSupport.class, new TimeZoneSupport(supportedZones, Options.defaultZone)); - } -} - -/** - * This whole file should be eventually removed: GR-11844. - */ -public class TimeZoneSubstitutions { -} diff --git a/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c b/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c index d2e78ee02034..1ae558a4b783 100644 --- a/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c +++ b/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c @@ -244,6 +244,18 @@ int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { return result; } +int jio_fprintf(FILE *fp, const char *fmt, ...) +{ + int len; + + va_list args; + va_start(args, fmt); + len = jio_vfprintf(fp, fmt, args); + va_end(args); + + return len; +} + #ifdef JNI_VERSION_9 int jio_snprintf(char *str, size_t count, const char *fmt, ...) { va_list args; diff --git a/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c b/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c index a6349f727999..9d23098038bb 100644 --- a/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c +++ b/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c @@ -260,6 +260,18 @@ int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { return result; } +int jio_fprintf(FILE *fp, const char *fmt, ...) +{ + int len; + + va_list args; + va_start(args, fmt); + len = jio_vfprintf(fp, fmt, args); + va_end(args); + + return len; +} + #ifdef JNI_VERSION_9 int jio_snprintf(char *str, size_t count, const char *fmt, ...) { va_list args; From f4e8511da9dffa3afcda6427209e8c0e4b87c591 Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Tue, 10 Mar 2020 16:12:38 -0700 Subject: [PATCH 03/13] [GR-21741] Adding substituition back, mainting JDK code for timezones --- .../svm/core/jdk/TimeZoneSubstitutions.java | 143 ++++++++++++++++++ .../src/JvmFuncs.c | 20 +-- .../src/JvmFuncs.c | 20 +-- 3 files changed, 163 insertions(+), 20 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java new file mode 100644 index 000000000000..aa5382ced104 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.jdk; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.Substitute; +import org.graalvm.collections.EconomicMap; +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.options.OptionKey; + +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.option.HostedOptionKey; +//Checkstyle: stop +import sun.security.action.GetPropertyAction; +//Checkstyle: resume + +import java.security.AccessController; +import java.util.Properties; +import java.util.TimeZone; + +@TargetClass(java.util.TimeZone.class) +@SuppressWarnings("unused") +final class Target_java_util_TimeZone { + + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) private static volatile TimeZone defaultTimeZone; + + @Alias + // Checkstyle: stop + private static String GMT_ID; + // Checkstyle: resume + + @Alias + static native TimeZone getDefaultRef(); + + @Alias + static native String getSystemTimeZoneID(String javaHome); + + @Alias + private static native TimeZone getTimeZone(String id, boolean fallback); + + @Alias + private static native String getSystemGMTOffsetID(); + + @SuppressWarnings("checkstyle:IllegalToken") + @Substitute + // Checkstyle: stop + private static synchronized TimeZone setDefaultZone() { + // Checkstyle: resume + TimeZone tz; + // get the time zone ID from the system properties + Properties props = System.getProperties(); + String zoneID = AccessController.doPrivileged( + new GetPropertyAction("user.timezone")); + + // if the time zone ID is not set (yet), perform the + // platform to Java time zone ID mapping. + if (zoneID == null || zoneID.isEmpty()) { + try { + // This is only different part from the JDK + // because native-image java.home property is null + zoneID = getSystemTimeZoneID(""); + if (zoneID == null) { + zoneID = GMT_ID; + } + } catch (NullPointerException e) { + zoneID = GMT_ID; + } + } + + // Get the time zone for zoneID. But not fall back to + // "GMT" here. + tz = getTimeZone(zoneID, false); + + if (tz == null) { + // If the given zone ID is unknown in Java, try to + // get the GMT-offset-based time zone ID, + // a.k.a. custom time zone ID (e.g., "GMT-08:00"). + String gmtOffsetID = getSystemGMTOffsetID(); + if (gmtOffsetID != null) { + zoneID = gmtOffsetID; + } + tz = getTimeZone(zoneID, true); + } + assert tz != null; + + final String id = zoneID; + props.setProperty("user.timezone", id); + + defaultTimeZone = tz; + return tz; + } +} + +public class TimeZoneSubstitutions { + static class Options { + @Option(help = "When true, all time zones will be pre-initialized in the image.")// + public static final HostedOptionKey<Boolean> IncludeAllTimeZones = new HostedOptionKey<Boolean>(false) { + @Override + protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) { + super.onValueUpdate(values, oldValue, newValue); + printWarning("-H:IncludeAllTimeZones and -H:IncludeTimeZones are deprecated"); + } + }; + + @Option(help = "The time zones, in addition to the default zone of the host, that will be pre-initialized in the image.")// + public static final HostedOptionKey<String> IncludeTimeZones = new HostedOptionKey<String>("") { + @Override + protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, String oldValue, String newValue) { + super.onValueUpdate(values, oldValue, newValue); + printWarning("-H:IncludeAllTimeZones and -H:IncludeTimeZones are deprecated"); + } + }; + + private static void printWarning(String warning) { + // Checkstyle: stop + System.err.println(warning); + // Checkstyle: resume + } + } +} diff --git a/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c b/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c index 1ae558a4b783..4ad73b8be781 100644 --- a/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c +++ b/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c @@ -244,6 +244,16 @@ int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { return result; } +#ifdef JNI_VERSION_9 +int jio_snprintf(char *str, size_t count, const char *fmt, ...) { + va_list args; + int len; + va_start(args, fmt); + len = jio_vsnprintf(str, count, fmt, args); + va_end(args); + return len; +} + int jio_fprintf(FILE *fp, const char *fmt, ...) { int len; @@ -255,14 +265,4 @@ int jio_fprintf(FILE *fp, const char *fmt, ...) return len; } - -#ifdef JNI_VERSION_9 -int jio_snprintf(char *str, size_t count, const char *fmt, ...) { - va_list args; - int len; - va_start(args, fmt); - len = jio_vsnprintf(str, count, fmt, args); - va_end(args); - return len; -} #endif diff --git a/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c b/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c index 9d23098038bb..437b3cf1dd3b 100644 --- a/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c +++ b/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c @@ -260,6 +260,16 @@ int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { return result; } +#ifdef JNI_VERSION_9 +int jio_snprintf(char *str, size_t count, const char *fmt, ...) { + va_list args; + int len; + va_start(args, fmt); + len = jio_vsnprintf(str, count, fmt, args); + va_end(args); + return len; +} + int jio_fprintf(FILE *fp, const char *fmt, ...) { int len; @@ -271,16 +281,6 @@ int jio_fprintf(FILE *fp, const char *fmt, ...) return len; } - -#ifdef JNI_VERSION_9 -int jio_snprintf(char *str, size_t count, const char *fmt, ...) { - va_list args; - int len; - va_start(args, fmt); - len = jio_vsnprintf(str, count, fmt, args); - va_end(args); - return len; -} #endif /* From c94bbb80c41400e8f6f2a2f958953479b2fa1348 Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Fri, 13 Mar 2020 17:40:47 -0700 Subject: [PATCH 04/13] [GR-21741] Adds implementation for time zone mappings for native method in windows --- .../src/com/oracle/svm/core/LibCHelper.java | 4 + .../svm/core/jdk/TimeZoneSubstitutions.java | 148 +++-- .../src/timeZone.c | 573 ++++++++++++++++++ 3 files changed, 670 insertions(+), 55 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java index bdc3ce3bee49..02c87d197bf9 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java @@ -27,6 +27,7 @@ import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.function.CFunction.Transition; import org.graalvm.nativeimage.c.function.CLibrary; +import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CCharPointerPointer; @CLibrary(value = "libchelper", requireStatic = true) @@ -34,4 +35,7 @@ public class LibCHelper { @CFunction(transition = Transition.NO_TRANSITION) public static native CCharPointerPointer getEnviron(); + @CFunction(transition = Transition.NO_TRANSITION) + public static native CCharPointer customFindJavaTZmd(CCharPointer tzmappings); + } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java index aa5382ced104..417f83853958 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -24,9 +24,14 @@ */ package com.oracle.svm.core.jdk; +import com.oracle.svm.core.LibCHelper; +import com.oracle.svm.core.OS; +import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.util.VMError; import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.options.Option; import org.graalvm.compiler.options.OptionKey; @@ -34,9 +39,20 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.option.HostedOptionKey; //Checkstyle: stop +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CTypeConversion; +import org.graalvm.nativeimage.hosted.Feature; import sun.security.action.GetPropertyAction; //Checkstyle: resume +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.security.AccessController; import java.util.Properties; import java.util.TimeZone; @@ -45,6 +61,18 @@ @SuppressWarnings("unused") final class Target_java_util_TimeZone { + static final class TimeZoneSupport { + final byte[] tzMappingsContent; + + TimeZoneSupport(final byte[] content) { + this.tzMappingsContent = content; + } + + public byte[] getTzMappingsContent() { + return tzMappingsContent; + } + } + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) private static volatile TimeZone defaultTimeZone; @Alias @@ -55,66 +83,58 @@ final class Target_java_util_TimeZone { @Alias static native TimeZone getDefaultRef(); - @Alias - static native String getSystemTimeZoneID(String javaHome); - - @Alias - private static native TimeZone getTimeZone(String id, boolean fallback); - - @Alias - private static native String getSystemGMTOffsetID(); - - @SuppressWarnings("checkstyle:IllegalToken") @Substitute - // Checkstyle: stop - private static synchronized TimeZone setDefaultZone() { - // Checkstyle: resume - TimeZone tz; - // get the time zone ID from the system properties - Properties props = System.getProperties(); - String zoneID = AccessController.doPrivileged( - new GetPropertyAction("user.timezone")); - - // if the time zone ID is not set (yet), perform the - // platform to Java time zone ID mapping. - if (zoneID == null || zoneID.isEmpty()) { - try { - // This is only different part from the JDK - // because native-image java.home property is null - zoneID = getSystemTimeZoneID(""); - if (zoneID == null) { - zoneID = GMT_ID; - } - } catch (NullPointerException e) { - zoneID = GMT_ID; - } + private static String getSystemTimeZoneID(String javaHome) { + // in windows call the custom function + // in unix call the normal function, can I call the jdk native function + TimeZoneSupport timeZoneSupport = ImageSingletons.lookup(TimeZoneSupport.class); + final byte[] content = timeZoneSupport.getTzMappingsContent(); + String tzmappings = new String(content); + try (CTypeConversion.CCharPointerHolder tzMappingsHolder = CTypeConversion.toCString(tzmappings)) { + CCharPointer tzMappings = tzMappingsHolder.get(); + CCharPointer tzId = LibCHelper.customFindJavaTZmd(tzMappings); + return CTypeConversion.toJavaString(tzId); } - - // Get the time zone for zoneID. But not fall back to - // "GMT" here. - tz = getTimeZone(zoneID, false); - - if (tz == null) { - // If the given zone ID is unknown in Java, try to - // get the GMT-offset-based time zone ID, - // a.k.a. custom time zone ID (e.g., "GMT-08:00"). - String gmtOffsetID = getSystemGMTOffsetID(); - if (gmtOffsetID != null) { - zoneID = gmtOffsetID; - } - tz = getTimeZone(zoneID, true); - } - assert tz != null; - - final String id = zoneID; - props.setProperty("user.timezone", id); - - defaultTimeZone = tz; - return tz; } + + /* + * @Alias private static native TimeZone getTimeZone(String id, boolean fallback); + * + * @Alias private static native String getSystemGMTOffsetID(); + * + * @Substitute // Checkstyle: stop private static synchronized TimeZone setDefaultZone() { // + * Checkstyle: resume TimeZone tz; // get the time zone ID from the system properties Properties + * props = System.getProperties(); String zoneID = AccessController.doPrivileged( new + * GetPropertyAction("user.timezone")); + * + * // if the time zone ID is not set (yet), perform the // platform to Java time zone ID + * mapping. if (zoneID == null || zoneID.isEmpty()) { try { // This is only different part from + * the JDK // because native-image java.home property is null zoneID = getSystemTimeZoneID(""); + * if (zoneID == null) { zoneID = GMT_ID; } } catch (NullPointerException e) { zoneID = GMT_ID; + * } } + * + * // Get the time zone for zoneID. But not fall back to // "GMT" here. tz = getTimeZone(zoneID, + * false); + * + * if (tz == null) { // If the given zone ID is unknown in Java, try to // get the + * GMT-offset-based time zone ID, // a.k.a. custom time zone ID (e.g., "GMT-08:00"). String + * gmtOffsetID = getSystemGMTOffsetID(); if (gmtOffsetID != null) { zoneID = gmtOffsetID; } tz = + * getTimeZone(zoneID, true); } assert tz != null; + * + * final String id = zoneID; props.setProperty("user.timezone", id); + * + * defaultTimeZone = tz; return tz; } + */ } -public class TimeZoneSubstitutions { +// Add feature again +// Replace Java_java_util_TimeZone_getSystemTimeZoneID with and call a CFunction +// it seems like I am going to have to pull in also all function that calls +// the jdk8 and jdk 11 implementations are different +// it brings functions from libjvm + +@AutomaticFeature +final class TimeZoneFeature implements Feature { static class Options { @Option(help = "When true, all time zones will be pre-initialized in the image.")// public static final HostedOptionKey<Boolean> IncludeAllTimeZones = new HostedOptionKey<Boolean>(false) { @@ -140,4 +160,22 @@ private static void printWarning(String warning) { // Checkstyle: resume } } + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + + if (OS.getCurrent() != OS.WINDOWS) { + return; + } + + // read tzmappings on windows + Path tzMappingsPath = Paths.get(System.getProperty("java.home"), "lib", "tzmappings"); + try { + byte[] content = Files.readAllBytes(tzMappingsPath); + ImageSingletons.add(Target_java_util_TimeZone.TimeZoneSupport.class, new Target_java_util_TimeZone.TimeZoneSupport(content)); + } catch (IOException e) { + VMError.shouldNotReachHere("Failed to read time zone mappings. The time zone mappings should be part" + + "of your JDK usually found: " + tzMappingsPath.toAbsolutePath(), e); + } + } } diff --git a/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c b/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c new file mode 100644 index 000000000000..f9a8f176602d --- /dev/null +++ b/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifdef _WIN64 +#include <windows.h> +#include <stdio.h> +#include <stdlib.h> +//#include "jvm.h" + +#define VALUE_UNKNOWN 0 +#define VALUE_KEY 1 +#define VALUE_MAPID 2 +#define VALUE_GMTOFFSET 3 + +#define MAX_ZONE_CHAR 256 +#define MAX_MAPID_LENGTH 32 +#define MAX_REGION_LENGTH 4 + +#define NT_TZ_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" +#define WIN_TZ_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones" +#define WIN_CURRENT_TZ_KEY "System\\CurrentControlSet\\Control\\TimeZoneInformation" + +typedef struct _TziValue { + LONG bias; + LONG stdBias; + LONG dstBias; + SYSTEMTIME stdDate; + SYSTEMTIME dstDate; +} TziValue; + +/* + * Registry key names + */ +static void *keyNames[] = { + (void *) L"StandardName", + (void *) "StandardName", + (void *) L"Std", + (void *) "Std" +}; + +/* + * Indices to keyNames[] + */ +#define STANDARD_NAME 0 +#define STD_NAME 2 + +/* + * Calls RegQueryValueEx() to get the value for the specified key. If + * the platform is NT, 2000 or XP, it calls the Unicode + * version. Otherwise, it calls the ANSI version and converts the + * value to Unicode. In this case, it assumes that the current ANSI + * Code Page is the same as the native platform code page (e.g., Code + * Page 932 for the Japanese Windows systems. + * + * `keyIndex' is an index value to the keyNames in Unicode + * (WCHAR). `keyIndex' + 1 points to its ANSI value. + * + * Returns the status value. ERROR_SUCCESS if succeeded, a + * non-ERROR_SUCCESS value otherwise. + */ +static LONG +getValueInRegistry(HKEY hKey, + int keyIndex, + LPDWORD typePtr, + LPBYTE buf, + LPDWORD bufLengthPtr) +{ + LONG ret; + DWORD bufLength = *bufLengthPtr; + char val[MAX_ZONE_CHAR]; + DWORD valSize; + int len; + + *typePtr = 0; + ret = RegQueryValueExW(hKey, (WCHAR *) keyNames[keyIndex], NULL, + typePtr, buf, bufLengthPtr); + if (ret == ERROR_SUCCESS && *typePtr == REG_SZ) { + return ret; + } + + valSize = sizeof(val); + ret = RegQueryValueExA(hKey, (char *) keyNames[keyIndex + 1], NULL, + typePtr, val, &valSize); + if (ret != ERROR_SUCCESS) { + return ret; + } + if (*typePtr != REG_SZ) { + return ERROR_BADKEY; + } + + len = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, + (LPCSTR) val, -1, + (LPWSTR) buf, bufLength/sizeof(WCHAR)); + if (len <= 0) { + return ERROR_BADKEY; + } + return ERROR_SUCCESS; +} + +/* + * Produces custom name "GMT+hh:mm" from the given bias in buffer. + */ +static void customZoneName(LONG bias, char *buffer) { + LONG gmtOffset; + int sign; + + if (bias > 0) { + gmtOffset = bias; + sign = -1; + } else { + gmtOffset = -bias; + sign = 1; + } + if (gmtOffset != 0) { + sprintf(buffer, "GMT%c%02d:%02d", + ((sign >= 0) ? '+' : '-'), + gmtOffset / 60, + gmtOffset % 60); + } else { + strcpy(buffer, "GMT"); + } +} + +/* + * Gets the current time zone entry in the "Time Zones" registry. + */ +static int getWinTimeZone(char *winZoneName) +{ + DYNAMIC_TIME_ZONE_INFORMATION dtzi; + DWORD timeType; + DWORD bufSize; + DWORD val; + HANDLE hKey = NULL; + LONG ret; + ULONG valueType; + + /* + * Get the dynamic time zone information so that time zone redirection + * can be supported. (see JDK-7044727) + */ + timeType = GetDynamicTimeZoneInformation(&dtzi); + if (timeType == TIME_ZONE_ID_INVALID) { + goto err; + } + + /* + * Make sure TimeZoneKeyName is available from the API call. If + * DynamicDaylightTime is disabled, return a custom time zone name + * based on the GMT offset. Otherwise, return the TimeZoneKeyName + * value. + */ + if (dtzi.TimeZoneKeyName[0] != 0) { + if (dtzi.DynamicDaylightTimeDisabled) { + customZoneName(dtzi.Bias, winZoneName); + return VALUE_GMTOFFSET; + } + wcstombs(winZoneName, dtzi.TimeZoneKeyName, MAX_ZONE_CHAR); + return VALUE_KEY; + } + + /* + * If TimeZoneKeyName is not available, check whether StandardName + * is available to fall back to the older API GetTimeZoneInformation. + * If not, directly read the value from registry keys. + */ + if (dtzi.StandardName[0] == 0) { + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, + KEY_READ, (PHKEY)&hKey); + if (ret != ERROR_SUCCESS) { + goto err; + } + + /* + * Determine if auto-daylight time adjustment is turned off. + */ + bufSize = sizeof(val); + ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL, + &valueType, (LPBYTE) &val, &bufSize); + if (ret != ERROR_SUCCESS) { + goto err; + } + /* + * Return a custom time zone name if auto-daylight time adjustment + * is disabled. + */ + if (val == 1) { + customZoneName(dtzi.Bias, winZoneName); + (void) RegCloseKey(hKey); + return VALUE_GMTOFFSET; + } + + bufSize = MAX_ZONE_CHAR; + ret = RegQueryValueExA(hKey, "TimeZoneKeyName", NULL, + &valueType, (LPBYTE) winZoneName, &bufSize); + if (ret != ERROR_SUCCESS) { + goto err; + } + (void) RegCloseKey(hKey); + return VALUE_KEY; + } else { + /* + * Fall back to GetTimeZoneInformation + */ + TIME_ZONE_INFORMATION tzi; + HANDLE hSubKey = NULL; + DWORD nSubKeys, i; + ULONG valueType; + TCHAR subKeyName[MAX_ZONE_CHAR]; + TCHAR szValue[MAX_ZONE_CHAR]; + WCHAR stdNameInReg[MAX_ZONE_CHAR]; + TziValue tempTzi; + WCHAR *stdNamePtr = tzi.StandardName; + int onlyMapID; + + timeType = GetTimeZoneInformation(&tzi); + if (timeType == TIME_ZONE_ID_INVALID) { + goto err; + } + + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, + KEY_READ, (PHKEY)&hKey); + if (ret == ERROR_SUCCESS) { + /* + * Determine if auto-daylight time adjustment is turned off. + */ + bufSize = sizeof(val); + ret = RegQueryValueExA(hKey, "DynamicDaylightTimeDisabled", NULL, + &valueType, (LPBYTE) &val, &bufSize); + if (ret == ERROR_SUCCESS) { + if (val == 1 && tzi.DaylightDate.wMonth != 0) { + (void) RegCloseKey(hKey); + customZoneName(tzi.Bias, winZoneName); + return VALUE_GMTOFFSET; + } + } + + /* + * Win32 problem: If the length of the standard time name is equal + * to (or probably longer than) 32 in the registry, + * GetTimeZoneInformation() on NT returns a null string as its + * standard time name. We need to work around this problem by + * getting the same information from the TimeZoneInformation + * registry. + */ + if (tzi.StandardName[0] == 0) { + bufSize = sizeof(stdNameInReg); + ret = getValueInRegistry(hKey, STANDARD_NAME, &valueType, + (LPBYTE) stdNameInReg, &bufSize); + if (ret != ERROR_SUCCESS) { + goto err; + } + stdNamePtr = stdNameInReg; + } + (void) RegCloseKey(hKey); + } + + /* + * Open the "Time Zones" registry. + */ + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, NT_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey); + if (ret != ERROR_SUCCESS) { + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_TZ_KEY, 0, KEY_READ, (PHKEY)&hKey); + /* + * If both failed, then give up. + */ + if (ret != ERROR_SUCCESS) { + return VALUE_UNKNOWN; + } + } + + /* + * Get the number of subkeys of the "Time Zones" registry for + * enumeration. + */ + ret = RegQueryInfoKey(hKey, NULL, NULL, NULL, &nSubKeys, + NULL, NULL, NULL, NULL, NULL, NULL, NULL); + if (ret != ERROR_SUCCESS) { + goto err; + } + + /* + * Compare to the "Std" value of each subkey and find the entry that + * matches the current control panel setting. + */ + onlyMapID = 0; + for (i = 0; i < nSubKeys; ++i) { + DWORD size = sizeof(subKeyName); + ret = RegEnumKeyEx(hKey, i, subKeyName, &size, NULL, NULL, NULL, NULL); + if (ret != ERROR_SUCCESS) { + goto err; + } + ret = RegOpenKeyEx(hKey, subKeyName, 0, KEY_READ, (PHKEY)&hSubKey); + if (ret != ERROR_SUCCESS) { + goto err; + } + + size = sizeof(szValue); + ret = getValueInRegistry(hSubKey, STD_NAME, &valueType, + szValue, &size); + if (ret != ERROR_SUCCESS) { + /* + * NT 4.0 SP3 fails here since it doesn't have the "Std" + * entry in the Time Zones registry. + */ + RegCloseKey(hSubKey); + onlyMapID = 1; + ret = RegOpenKeyExW(hKey, stdNamePtr, 0, KEY_READ, (PHKEY)&hSubKey); + if (ret != ERROR_SUCCESS) { + goto err; + } + break; + } + + if (wcscmp((WCHAR *)szValue, stdNamePtr) == 0) { + /* + * Some localized Win32 platforms use a same name to + * different time zones. So, we can't rely only on the name + * here. We need to check GMT offsets and transition dates + * to make sure it's the registry of the current time + * zone. + */ + DWORD tziValueSize = sizeof(tempTzi); + ret = RegQueryValueEx(hSubKey, "TZI", NULL, &valueType, + (unsigned char *) &tempTzi, &tziValueSize); + if (ret == ERROR_SUCCESS) { + if ((tzi.Bias != tempTzi.bias) || + (memcmp((const void *) &tzi.StandardDate, + (const void *) &tempTzi.stdDate, + sizeof(SYSTEMTIME)) != 0)) { + goto out; + } + + if (tzi.DaylightBias != 0) { + if ((tzi.DaylightBias != tempTzi.dstBias) || + (memcmp((const void *) &tzi.DaylightDate, + (const void *) &tempTzi.dstDate, + sizeof(SYSTEMTIME)) != 0)) { + goto out; + } + } + } + + /* + * found matched record, terminate search + */ + strcpy(winZoneName, subKeyName); + break; + } + out: + (void) RegCloseKey(hSubKey); + } + + (void) RegCloseKey(hKey); + } + + return VALUE_KEY; + + err: + if (hKey != NULL) { + (void) RegCloseKey(hKey); + } + return VALUE_UNKNOWN; +} + +/* + * The mapping table file name. + */ +#define MAPPINGS_FILE "\\lib\\tzmappings" + +/* + * Index values for the mapping table. + */ +#define TZ_WIN_NAME 0 +#define TZ_REGION 1 +#define TZ_JAVA_NAME 2 + +#define TZ_NITEMS 3 /* number of items (fields) */ + +/* + * Looks up the mapping table (tzmappings) and returns a Java time + * zone ID (e.g., "America/Los_Angeles") if found. Otherwise, NULL is + * returned. + */ +static char *matchJavaTZ(char *tzName, const char *tzmappings) +{ + int line; + int IDmatched = 0; + FILE *fp; + char *javaTZName = NULL; + char *items[TZ_NITEMS]; + char *mapFileName; + char lineBuffer[MAX_ZONE_CHAR * 4]; + int offset = 0; + const char* errorMessage = "unknown error"; + char region[MAX_REGION_LENGTH]; + + // Get the user's location + if (GetGeoInfo(GetUserGeoID(GEOCLASS_NATION), + GEO_ISO2, region, MAX_REGION_LENGTH, 0) == 0) { + // If GetGeoInfo fails, fallback to LCID's country + LCID lcid = GetUserDefaultLCID(); + if (GetLocaleInfo(lcid, + LOCALE_SISO3166CTRYNAME, region, MAX_REGION_LENGTH) == 0 && + GetLocaleInfo(lcid, + LOCALE_SISO3166CTRYNAME2, region, MAX_REGION_LENGTH) == 0) { + region[0] = '\0'; + } + } + + // Parsing below + line = 0; + int tzmappingsLen = strlen(tzmappings); + //while (fgets(lineBuffer, sizeof(lineBuffer), fp) != NULL) { + for(int i =0 ; i < tzmappingsLen; i+= sizeof(lineBuffer)) { + char *start, *idx, *endp; + int itemIndex = 0; + + line++; + strncpy(lineBuffer, tzmappings + i, sizeof(lineBuffer)); + start = idx = lineBuffer; + endp = &lineBuffer[sizeof(lineBuffer)]; + + /* + * Ignore comment and blank lines. + */ + if (*idx == '#' || *idx == '\n') { + continue; + } + + for (itemIndex = 0; itemIndex < TZ_NITEMS; itemIndex++) { + items[itemIndex] = start; + while (*idx && *idx != ':') { + if (++idx >= endp) { + errorMessage = "premature end of line"; + offset = (int)(idx - lineBuffer); + goto illegal_format; + } + } + if (*idx == '\0') { + errorMessage = "illegal null character found"; + offset = (int)(idx - lineBuffer); + goto illegal_format; + } + *idx++ = '\0'; + start = idx; + } + + if (*idx != '\n') { + errorMessage = "illegal non-newline character found"; + offset = (int)(idx - lineBuffer); + goto illegal_format; + } + + /* + * We need to scan items until the + * exact match is found or the end of data is detected. + */ + if (strcmp(items[TZ_WIN_NAME], tzName) == 0) { + /* + * Found the time zone in the mapping table. + * Check the region code and select the appropriate entry + */ + if (strcmp(items[TZ_REGION], region) == 0 || + strcmp(items[TZ_REGION], "001") == 0) { + javaTZName = _strdup(items[TZ_JAVA_NAME]); + printf("Found item %s", javaTZName); + break; + } + } + } + + return javaTZName; + + illegal_format: + jio_fprintf(stderr, "Illegal format in tzmappings file: %s at line %d, offset %d.\n", + errorMessage, line, offset); + printf("illegal format"); + return NULL; +} + +/** + * Returns a GMT-offset-based time zone ID. + */ +char * customGetGMTOffsetID() +{ + LONG bias = 0; + LONG ret; + HANDLE hKey = NULL; + char zonename[32]; + + // Obtain the current GMT offset value of ActiveTimeBias. + ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, + KEY_READ, (PHKEY)&hKey); + if (ret == ERROR_SUCCESS) { + DWORD val; + DWORD bufSize = sizeof(val); + ULONG valueType = 0; + ret = RegQueryValueExA(hKey, "ActiveTimeBias", + NULL, &valueType, (LPBYTE) &val, &bufSize); + if (ret == ERROR_SUCCESS) { + bias = (LONG) val; + } + (void) RegCloseKey(hKey); + } + + // If we can't get the ActiveTimeBias value, use Bias of TimeZoneInformation. + // Note: Bias doesn't reflect current daylight saving. + if (ret != ERROR_SUCCESS) { + TIME_ZONE_INFORMATION tzi; + if (GetTimeZoneInformation(&tzi) != TIME_ZONE_ID_INVALID) { + bias = tzi.Bias; + } + } + + customZoneName(bias, zonename); + return _strdup(zonename); +} + +/* + * Detects the platform time zone which maps to a Java time zone ID. + */ +char *customFindJavaTZmd(const char *tzmappings) +{ + char winZoneName[MAX_ZONE_CHAR]; + char *std_timezone = NULL; + int result; + + result = getWinTimeZone(winZoneName); + + if (result != VALUE_UNKNOWN) { + if (result == VALUE_GMTOFFSET) { + std_timezone = _strdup(winZoneName); + } else { + std_timezone = matchJavaTZ(winZoneName, tzmappings); + if (std_timezone == NULL) { + std_timezone = customGetGMTOffsetID(); + } + } + } + return std_timezone; +} +#else +#include <stdio.h> +char *customFindJavaTZmd(const char *tzmappings) { + printf("here2\n"); + printf("%d\n", strlen(tzmappings)); + + return "EGZ"; +} +#endif + From b653bbbcaf13446a09a258fb8784ff7990cb117c Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Fri, 13 Mar 2020 17:40:47 -0700 Subject: [PATCH 05/13] [GR-21741] Adds implementation for time zone mappings for native method in windows --- .../src/com/oracle/svm/core/LibCHelper.java | 2 +- .../com/oracle/svm/core/jdk/OSTargets.java | 68 ++++++++ .../svm/core/jdk/TimeZoneSubstitutions.java | 151 +++++++++++------- .../oracle/svm/hosted/meta/HostedMethod.java | 9 ++ .../src/timeZone.c | 149 +++++++++++------ 5 files changed, 272 insertions(+), 107 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/OSTargets.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java index 02c87d197bf9..4b61d7ccfef7 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java @@ -35,7 +35,7 @@ public class LibCHelper { @CFunction(transition = Transition.NO_TRANSITION) public static native CCharPointerPointer getEnviron(); - @CFunction(transition = Transition.NO_TRANSITION) + @CFunction(transition = Transition.TO_NATIVE) public static native CCharPointer customFindJavaTZmd(CCharPointer tzmappings); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/OSTargets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/OSTargets.java new file mode 100644 index 000000000000..c34238132e11 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/OSTargets.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.jdk; + +import com.oracle.svm.core.OS; + +import java.util.function.BooleanSupplier; + +public final class OSTargets { + + public static class Windows implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return OS.getCurrent() == OS.WINDOWS; + } + } + + public static class Darwin implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return OS.getCurrent() == OS.DARWIN; + } + } + + public static class Linux implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return OS.getCurrent() == OS.LINUX; + } + } + + public static class UnixLike implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return OS.getCurrent() == OS.LINUX || OS.getCurrent() == OS.DARWIN; + } + } + + public static class All implements BooleanSupplier { + @Override + public boolean getAsBoolean() { + return true; + } + + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java index 417f83853958..195aa2f6f511 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -26,11 +26,11 @@ import com.oracle.svm.core.LibCHelper; import com.oracle.svm.core.OS; -import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.util.VMError; import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.options.Option; @@ -38,18 +38,15 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.option.HostedOptionKey; -//Checkstyle: stop import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.hosted.Feature; +//Checkstyle: stop import sun.security.action.GetPropertyAction; //Checkstyle: resume -import java.io.File; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -61,18 +58,6 @@ @SuppressWarnings("unused") final class Target_java_util_TimeZone { - static final class TimeZoneSupport { - final byte[] tzMappingsContent; - - TimeZoneSupport(final byte[] content) { - this.tzMappingsContent = content; - } - - public byte[] getTzMappingsContent() { - return tzMappingsContent; - } - } - @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) private static volatile TimeZone defaultTimeZone; @Alias @@ -81,14 +66,20 @@ public byte[] getTzMappingsContent() { // Checkstyle: resume @Alias - static native TimeZone getDefaultRef(); + private static native TimeZone getTimeZone(String id, boolean fallback); + + @Alias + private static native String getSystemGMTOffsetID(); + + @Alias + @TargetElement(name = "getSystemTimeZoneID", onlyWith = OSTargets.UnixLike.class) + private static native String getSystemTimeZoneID(String javaHome); @Substitute - private static String getSystemTimeZoneID(String javaHome) { - // in windows call the custom function - // in unix call the normal function, can I call the jdk native function + @TargetElement(name = "getSystemTimeZoneID", onlyWith = {OSTargets.Windows.class}) + private static String getSystemTimeZoneIDWindows(String javaHome) { TimeZoneSupport timeZoneSupport = ImageSingletons.lookup(TimeZoneSupport.class); - final byte[] content = timeZoneSupport.getTzMappingsContent(); + byte[] content = timeZoneSupport.getTzMappingsContent(); String tzmappings = new String(content); try (CTypeConversion.CCharPointerHolder tzMappingsHolder = CTypeConversion.toCString(tzmappings)) { CCharPointer tzMappings = tzMappingsHolder.get(); @@ -97,41 +88,70 @@ private static String getSystemTimeZoneID(String javaHome) { } } - /* - * @Alias private static native TimeZone getTimeZone(String id, boolean fallback); - * - * @Alias private static native String getSystemGMTOffsetID(); - * - * @Substitute // Checkstyle: stop private static synchronized TimeZone setDefaultZone() { // - * Checkstyle: resume TimeZone tz; // get the time zone ID from the system properties Properties - * props = System.getProperties(); String zoneID = AccessController.doPrivileged( new - * GetPropertyAction("user.timezone")); - * - * // if the time zone ID is not set (yet), perform the // platform to Java time zone ID - * mapping. if (zoneID == null || zoneID.isEmpty()) { try { // This is only different part from - * the JDK // because native-image java.home property is null zoneID = getSystemTimeZoneID(""); - * if (zoneID == null) { zoneID = GMT_ID; } } catch (NullPointerException e) { zoneID = GMT_ID; - * } } - * - * // Get the time zone for zoneID. But not fall back to // "GMT" here. tz = getTimeZone(zoneID, - * false); - * - * if (tz == null) { // If the given zone ID is unknown in Java, try to // get the - * GMT-offset-based time zone ID, // a.k.a. custom time zone ID (e.g., "GMT-08:00"). String - * gmtOffsetID = getSystemGMTOffsetID(); if (gmtOffsetID != null) { zoneID = gmtOffsetID; } tz = - * getTimeZone(zoneID, true); } assert tz != null; - * - * final String id = zoneID; props.setProperty("user.timezone", id); - * - * defaultTimeZone = tz; return tz; } - */ + @Substitute + // Checkstyle: stop + private static synchronized TimeZone setDefaultZone() { + // Checkstyle: resume + TimeZone tz; + // get the time zone ID from the system properties + Properties props = System.getProperties(); + String zoneID = AccessController.doPrivileged( + new GetPropertyAction("user.timezone")); + + // if the time zone ID is not set (yet), perform the + // platform to Java time zone ID mapping. + if (zoneID == null || zoneID.isEmpty()) { + try { + // Send empty for as java home as opposed to null + if (OS.getCurrent() == OS.WINDOWS) { + zoneID = getSystemTimeZoneIDWindows(""); + } else { + zoneID = getSystemTimeZoneID(""); + } + if (zoneID == null) { + zoneID = GMT_ID; + } + } catch (NullPointerException e) { + zoneID = GMT_ID; + } + } + + // Get the time zone for zoneID. But not fall back to + // "GMT" here. + tz = getTimeZone(zoneID, false); + + if (tz == null) { + // If the given zone ID is unknown in Java, try to + // get the GMT-offset-based time zone ID, + // a.k.a. custom time zone ID (e.g., "GMT-08:00"). + String gmtOffsetID = getSystemGMTOffsetID(); + if (gmtOffsetID != null) { + zoneID = gmtOffsetID; + } + tz = getTimeZone(zoneID, true); + } + assert tz != null; + + final String id = zoneID; + props.setProperty("user.timezone", id); + + defaultTimeZone = tz; + return tz; + } } -// Add feature again -// Replace Java_java_util_TimeZone_getSystemTimeZoneID with and call a CFunction -// it seems like I am going to have to pull in also all function that calls -// the jdk8 and jdk 11 implementations are different -// it brings functions from libjvm +final class TimeZoneSupport { + final byte[] tzMappingsContent; + + TimeZoneSupport(final byte[] content) { + this.tzMappingsContent = content; + } + + public byte[] getTzMappingsContent() { + return tzMappingsContent; + } + +} @AutomaticFeature final class TimeZoneFeature implements Feature { @@ -161,18 +181,35 @@ private static void printWarning(String warning) { } } + private static byte[] cleanCR(byte[] buffer) { + byte[] scratch = new byte[buffer.length]; + int copied = 0; + for (byte b : buffer) { + if (b == 13) { + continue; + } + scratch[copied++] = b; + } + byte[] content = new byte[copied]; + System.arraycopy(scratch, 0, content, 0, copied); + return content; + } + @Override public void afterRegistration(AfterRegistrationAccess access) { if (OS.getCurrent() != OS.WINDOWS) { + ImageSingletons.add(TimeZoneSupport.class, new TimeZoneSupport(null)); return; } // read tzmappings on windows Path tzMappingsPath = Paths.get(System.getProperty("java.home"), "lib", "tzmappings"); try { - byte[] content = Files.readAllBytes(tzMappingsPath); - ImageSingletons.add(Target_java_util_TimeZone.TimeZoneSupport.class, new Target_java_util_TimeZone.TimeZoneSupport(content)); + byte[] buffer = Files.readAllBytes(tzMappingsPath); + // tzmappings has windows line endings on windows?? + byte[] content = cleanCR(buffer); + ImageSingletons.add(TimeZoneSupport.class, new TimeZoneSupport(content)); } catch (IOException e) { VMError.shouldNotReachHere("Failed to read time zone mappings. The time zone mappings should be part" + "of your JDK usually found: " + tzMappingsPath.toAbsolutePath(), e); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java index 11beab0266ba..0da30f1e45e3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java @@ -31,6 +31,7 @@ import java.lang.reflect.Executable; import java.lang.reflect.Type; +import com.oracle.svm.hosted.substitute.SubstitutionMethod; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.JavaMethodContext; import org.graalvm.compiler.nodes.StructuredGraph; @@ -476,10 +477,18 @@ public int compareTo(HostedMethod other) { if (result == 0) { result = ((HostedType) this.getSignature().getReturnType(null)).compareTo((HostedType) other.getSignature().getReturnType(null)); } + + if (result == 0 && (isSubstitution() || other.isSubstitution())) { + result = isSubstitution() ? 1 : -1; + } assert result != 0; return result; } + protected boolean isSubstitution() { + return wrapped.getWrapped() instanceof SubstitutionMethod; + } + @Override public Executable getJavaMethod() { return OriginalMethodProvider.getJavaMethod(getDeclaringClass().universe.getSnippetReflection(), wrapped); diff --git a/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c b/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c index f9a8f176602d..40f3d2d37686 100644 --- a/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c +++ b/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c @@ -21,13 +21,18 @@ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. + */ + +/* + * The following is an identical copy of the TimeZone_md.c file found at + * https://github.com/graalvm/labs-openjdk-11/blob/67ddc3bcadd985ea26997457aec6696f21caf154/src/java.base/unix/native/libjava/TimeZone_md.c + * With the exceptions of the commented functions this file has not been modified from its original. */ #ifdef _WIN64 #include <windows.h> #include <stdio.h> #include <stdlib.h> -//#include "jvm.h" #define VALUE_UNKNOWN 0 #define VALUE_KEY 1 @@ -36,7 +41,6 @@ #define MAX_ZONE_CHAR 256 #define MAX_MAPID_LENGTH 32 -#define MAX_REGION_LENGTH 4 #define NT_TZ_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones" #define WIN_TZ_KEY "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Time Zones" @@ -146,7 +150,7 @@ static void customZoneName(LONG bias, char *buffer) { /* * Gets the current time zone entry in the "Time Zones" registry. */ -static int getWinTimeZone(char *winZoneName) +static int getWinTimeZone(char *winZoneName, char *winMapID) { DYNAMIC_TIME_ZONE_INFORMATION dtzi; DWORD timeType; @@ -232,6 +236,7 @@ static int getWinTimeZone(char *winZoneName) WCHAR stdNameInReg[MAX_ZONE_CHAR]; TziValue tempTzi; WCHAR *stdNamePtr = tzi.StandardName; + DWORD valueSize; int onlyMapID; timeType = GetTimeZoneInformation(&tzi); @@ -372,7 +377,24 @@ static int getWinTimeZone(char *winZoneName) (void) RegCloseKey(hSubKey); } + /* + * Get the "MapID" value of the registry to be able to eliminate + * duplicated key names later. + */ + valueSize = MAX_MAPID_LENGTH; + ret = RegQueryValueExA(hSubKey, "MapID", NULL, &valueType, winMapID, &valueSize); + (void) RegCloseKey(hSubKey); (void) RegCloseKey(hKey); + + if (ret != ERROR_SUCCESS) { + /* + * Vista doesn't have mapID. VALUE_UNKNOWN should be returned + * only for Windows NT. + */ + if (onlyMapID == 1) { + return VALUE_UNKNOWN; + } + } } return VALUE_KEY; @@ -385,25 +407,51 @@ static int getWinTimeZone(char *winZoneName) } /* - * The mapping table file name. + * An implementation of fgets on a buffer as opposed to a file object. + * */ -#define MAPPINGS_FILE "\\lib\\tzmappings" +static int readnewLine(char *dst, int num, int offset, char *source, int source_len) { + int read = 0; + while(read < num - 1) { + if (offset + read == source_len) { + break; + } + dst[read] = source[offset + read]; + if (dst[read] == '\n') { + read++; + break; + } + read++; + } + dst[read+1] = '\0'; + return read; +} /* * Index values for the mapping table. */ #define TZ_WIN_NAME 0 -#define TZ_REGION 1 -#define TZ_JAVA_NAME 2 +#define TZ_MAPID 1 +#define TZ_REGION 2 +#define TZ_JAVA_NAME 3 -#define TZ_NITEMS 3 /* number of items (fields) */ +#define TZ_NITEMS 4 /* number of items (fields) */ /* * Looks up the mapping table (tzmappings) and returns a Java time * zone ID (e.g., "America/Los_Angeles") if found. Otherwise, NULL is * returned. + * + * value_type is one of the following values: + * VALUE_KEY for exact key matching + * VALUE_MAPID for MapID (this is + * required for the old Windows, such as NT 4.0 SP3). + * + * The following function differs from the original by accepting a buffer and reading + * tzmappings data from it. The original function opens and reads such data from a file. */ -static char *matchJavaTZ(char *tzName, const char *tzmappings) +static char *matchJavaTZ_Java11(const char *tzmappings, int value_type, char *tzName, + char *mapID) { int line; int IDmatched = 0; @@ -412,33 +460,23 @@ static char *matchJavaTZ(char *tzName, const char *tzmappings) char *items[TZ_NITEMS]; char *mapFileName; char lineBuffer[MAX_ZONE_CHAR * 4]; + int noMapID = *mapID == '\0'; /* no mapID on Vista and later */ int offset = 0; const char* errorMessage = "unknown error"; - char region[MAX_REGION_LENGTH]; - - // Get the user's location - if (GetGeoInfo(GetUserGeoID(GEOCLASS_NATION), - GEO_ISO2, region, MAX_REGION_LENGTH, 0) == 0) { - // If GetGeoInfo fails, fallback to LCID's country - LCID lcid = GetUserDefaultLCID(); - if (GetLocaleInfo(lcid, - LOCALE_SISO3166CTRYNAME, region, MAX_REGION_LENGTH) == 0 && - GetLocaleInfo(lcid, - LOCALE_SISO3166CTRYNAME2, region, MAX_REGION_LENGTH) == 0) { - region[0] = '\0'; - } - } + int tzmappingsLen; + int currLocation; + int readChars; - // Parsing below line = 0; - int tzmappingsLen = strlen(tzmappings); - //while (fgets(lineBuffer, sizeof(lineBuffer), fp) != NULL) { - for(int i =0 ; i < tzmappingsLen; i+= sizeof(lineBuffer)) { + tzmappingsLen = strlen(tzmappings); + currLocation = 0; + readChars = 0; + while((readChars = readnewLine(lineBuffer, sizeof(lineBuffer), currLocation, tzmappings, tzmappingsLen)) != 0) { char *start, *idx, *endp; int itemIndex = 0; + currLocation += readChars; line++; - strncpy(lineBuffer, tzmappings + i, sizeof(lineBuffer)); start = idx = lineBuffer; endp = &lineBuffer[sizeof(lineBuffer)]; @@ -473,19 +511,26 @@ static char *matchJavaTZ(char *tzName, const char *tzmappings) goto illegal_format; } - /* - * We need to scan items until the - * exact match is found or the end of data is detected. - */ - if (strcmp(items[TZ_WIN_NAME], tzName) == 0) { + if (noMapID || strcmp(mapID, items[TZ_MAPID]) == 0) { /* - * Found the time zone in the mapping table. - * Check the region code and select the appropriate entry + * When there's no mapID, we need to scan items until the + * exact match is found or the end of data is detected. */ - if (strcmp(items[TZ_REGION], region) == 0 || - strcmp(items[TZ_REGION], "001") == 0) { + if (!noMapID) { + IDmatched = 1; + } + if (strcmp(items[TZ_WIN_NAME], tzName) == 0) { + /* + * Found the time zone in the mapping table. + */ javaTZName = _strdup(items[TZ_JAVA_NAME]); - printf("Found item %s", javaTZName); + break; + } + } else { + if (IDmatched == 1) { + /* + * No need to look up the mapping table further. + */ break; } } @@ -496,14 +541,16 @@ static char *matchJavaTZ(char *tzName, const char *tzmappings) illegal_format: jio_fprintf(stderr, "Illegal format in tzmappings file: %s at line %d, offset %d.\n", errorMessage, line, offset); - printf("illegal format"); return NULL; } /** * Returns a GMT-offset-based time zone ID. + * + * No change from original just different function name */ -char * customGetGMTOffsetID() +static char * +customGetGMTOffsetID() { LONG bias = 0; LONG ret; @@ -540,20 +587,29 @@ char * customGetGMTOffsetID() /* * Detects the platform time zone which maps to a Java time zone ID. + * + * The following function only differs from the original by calling a custom parsing + * function called "matchJavaTZ_Java11" that accepts a data buffer. + * + * This function expects its argument to contain the whole tzmappings data as an argument. + * The original function expected a buffer whose content was the path to JAVA_HOME */ char *customFindJavaTZmd(const char *tzmappings) { char winZoneName[MAX_ZONE_CHAR]; + char winMapID[MAX_MAPID_LENGTH]; char *std_timezone = NULL; int result; - result = getWinTimeZone(winZoneName); + winMapID[0] = 0; + result = getWinTimeZone(winZoneName, winMapID); if (result != VALUE_UNKNOWN) { if (result == VALUE_GMTOFFSET) { std_timezone = _strdup(winZoneName); } else { - std_timezone = matchJavaTZ(winZoneName, tzmappings); + std_timezone = matchJavaTZ_Java11(tzmappings, result, + winZoneName, winMapID); if (std_timezone == NULL) { std_timezone = customGetGMTOffsetID(); } @@ -562,12 +618,7 @@ char *customFindJavaTZmd(const char *tzmappings) return std_timezone; } #else -#include <stdio.h> char *customFindJavaTZmd(const char *tzmappings) { - printf("here2\n"); - printf("%d\n", strlen(tzmappings)); - - return "EGZ"; + return ((void*)0); } -#endif - +#endif // _WIN64 From 5070568d79e9d3066724d037f4f94f4fb5726ac3 Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Mon, 23 Mar 2020 11:22:01 -0700 Subject: [PATCH 06/13] [GR-21741] Removes substitution and adding C method implementation to call into the JDK --- .../svm/core/jdk/TimeZoneSubstitutions.java | 84 ++----------------- .../src/timeZone.c | 3 +- 2 files changed, 9 insertions(+), 78 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java index 195aa2f6f511..8a2392e2d2f8 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -30,7 +30,6 @@ import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; -import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.util.VMError; import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.options.Option; @@ -42,16 +41,11 @@ import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.hosted.Feature; -//Checkstyle: stop -import sun.security.action.GetPropertyAction; -//Checkstyle: resume import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.security.AccessController; -import java.util.Properties; import java.util.TimeZone; @TargetClass(java.util.TimeZone.class) @@ -60,84 +54,20 @@ final class Target_java_util_TimeZone { @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) private static volatile TimeZone defaultTimeZone; - @Alias - // Checkstyle: stop - private static String GMT_ID; - // Checkstyle: resume - - @Alias - private static native TimeZone getTimeZone(String id, boolean fallback); - - @Alias - private static native String getSystemGMTOffsetID(); - - @Alias - @TargetElement(name = "getSystemTimeZoneID", onlyWith = OSTargets.UnixLike.class) - private static native String getSystemTimeZoneID(String javaHome); - @Substitute - @TargetElement(name = "getSystemTimeZoneID", onlyWith = {OSTargets.Windows.class}) - private static String getSystemTimeZoneIDWindows(String javaHome) { - TimeZoneSupport timeZoneSupport = ImageSingletons.lookup(TimeZoneSupport.class); - byte[] content = timeZoneSupport.getTzMappingsContent(); - String tzmappings = new String(content); + private static String getSystemTimeZoneID(String javaHome) { + String tzmappings = ""; + if (OS.getCurrent() == OS.WINDOWS) { + TimeZoneSupport timeZoneSupport = ImageSingletons.lookup(TimeZoneSupport.class); + byte[] content = timeZoneSupport.getTzMappingsContent(); + tzmappings = new String(content); + } try (CTypeConversion.CCharPointerHolder tzMappingsHolder = CTypeConversion.toCString(tzmappings)) { CCharPointer tzMappings = tzMappingsHolder.get(); CCharPointer tzId = LibCHelper.customFindJavaTZmd(tzMappings); return CTypeConversion.toJavaString(tzId); } } - - @Substitute - // Checkstyle: stop - private static synchronized TimeZone setDefaultZone() { - // Checkstyle: resume - TimeZone tz; - // get the time zone ID from the system properties - Properties props = System.getProperties(); - String zoneID = AccessController.doPrivileged( - new GetPropertyAction("user.timezone")); - - // if the time zone ID is not set (yet), perform the - // platform to Java time zone ID mapping. - if (zoneID == null || zoneID.isEmpty()) { - try { - // Send empty for as java home as opposed to null - if (OS.getCurrent() == OS.WINDOWS) { - zoneID = getSystemTimeZoneIDWindows(""); - } else { - zoneID = getSystemTimeZoneID(""); - } - if (zoneID == null) { - zoneID = GMT_ID; - } - } catch (NullPointerException e) { - zoneID = GMT_ID; - } - } - - // Get the time zone for zoneID. But not fall back to - // "GMT" here. - tz = getTimeZone(zoneID, false); - - if (tz == null) { - // If the given zone ID is unknown in Java, try to - // get the GMT-offset-based time zone ID, - // a.k.a. custom time zone ID (e.g., "GMT-08:00"). - String gmtOffsetID = getSystemGMTOffsetID(); - if (gmtOffsetID != null) { - zoneID = gmtOffsetID; - } - tz = getTimeZone(zoneID, true); - } - assert tz != null; - - final String id = zoneID; - props.setProperty("user.timezone", id); - - defaultTimeZone = tz; - return tz; - } } final class TimeZoneSupport { diff --git a/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c b/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c index 40f3d2d37686..7ae1a63c1bef 100644 --- a/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c +++ b/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c @@ -618,7 +618,8 @@ char *customFindJavaTZmd(const char *tzmappings) return std_timezone; } #else +extern char* findJavaTZ_md(const char *); char *customFindJavaTZmd(const char *tzmappings) { - return ((void*)0); + return findJavaTZ_md(""); } #endif // _WIN64 From 3756affc7ba16533ee10636938fb6ca013718dec Mon Sep 17 00:00:00 2001 From: eginez <esteban.ginez@oracle.com> Date: Mon, 23 Mar 2020 12:10:54 -0700 Subject: [PATCH 07/13] [GR-21741] Adding debug statement --- .../src/com/oracle/svm/hosted/meta/HostedMethod.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java index 0da30f1e45e3..7bd532a176a6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java @@ -479,7 +479,11 @@ public int compareTo(HostedMethod other) { } if (result == 0 && (isSubstitution() || other.isSubstitution())) { - result = isSubstitution() ? 1 : -1; + System.out.printf("EGZ!"); + System.out.println(this.toString()); + System.out.println(other.toString()); + System.out.println("-----"); + //result = isSubstitution() ? 1 : -1; } assert result != 0; return result; From 464116e87204ad55c36e350771175acbd87c5baf Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Mon, 23 Mar 2020 14:10:06 -0700 Subject: [PATCH 08/13] [GR-21741] removes debug messages --- .../com/oracle/svm/hosted/meta/HostedMethod.java | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java index 7bd532a176a6..11beab0266ba 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java @@ -31,7 +31,6 @@ import java.lang.reflect.Executable; import java.lang.reflect.Type; -import com.oracle.svm.hosted.substitute.SubstitutionMethod; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.JavaMethodContext; import org.graalvm.compiler.nodes.StructuredGraph; @@ -477,22 +476,10 @@ public int compareTo(HostedMethod other) { if (result == 0) { result = ((HostedType) this.getSignature().getReturnType(null)).compareTo((HostedType) other.getSignature().getReturnType(null)); } - - if (result == 0 && (isSubstitution() || other.isSubstitution())) { - System.out.printf("EGZ!"); - System.out.println(this.toString()); - System.out.println(other.toString()); - System.out.println("-----"); - //result = isSubstitution() ? 1 : -1; - } assert result != 0; return result; } - protected boolean isSubstitution() { - return wrapped.getWrapped() instanceof SubstitutionMethod; - } - @Override public Executable getJavaMethod() { return OriginalMethodProvider.getJavaMethod(getDeclaringClass().universe.getSnippetReflection(), wrapped); From 280c5484c4d5c10498913cbf4600ba4b3eb59a1f Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Tue, 24 Mar 2020 11:28:18 -0700 Subject: [PATCH 09/13] [GR-21741] Limiting substitution to JNI platforms --- .../src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java index 8a2392e2d2f8..2e927bf0c3cd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -38,9 +38,11 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.option.HostedOptionKey; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.hosted.Feature; +import org.graalvm.nativeimage.impl.InternalPlatform; import java.io.IOException; import java.nio.file.Files; @@ -48,6 +50,7 @@ import java.nio.file.Paths; import java.util.TimeZone; +@Platforms(InternalPlatform.PLATFORM_JNI.class) @TargetClass(java.util.TimeZone.class) @SuppressWarnings("unused") final class Target_java_util_TimeZone { @@ -84,6 +87,7 @@ public byte[] getTzMappingsContent() { } @AutomaticFeature +@Platforms(InternalPlatform.PLATFORM_JNI.class) final class TimeZoneFeature implements Feature { static class Options { @Option(help = "When true, all time zones will be pre-initialized in the image.")// From d36738bc2224fa17d9f6ed03361deb568539e315 Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Tue, 24 Mar 2020 15:37:08 -0700 Subject: [PATCH 10/13] [GR-21741] Fixing typos in header of c file, and other minor fixes --- .../src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java | 3 +++ .../src/com.oracle.svm.native.libchelper/src/timeZone.c | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java index 2e927bf0c3cd..8d162f8f6b91 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -150,3 +150,6 @@ public void afterRegistration(AfterRegistrationAccess access) { } } } + +class TimeZoneSubstitutions { +} diff --git a/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c b/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c index 7ae1a63c1bef..a4be7e9b706a 100644 --- a/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c +++ b/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -10,7 +10,7 @@ * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * @@ -21,7 +21,7 @@ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. - */ + */ /* * The following is an identical copy of the TimeZone_md.c file found at From 0a144d55b2a787c3b9a258bdd0fcaffe0834d717 Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Wed, 25 Mar 2020 15:03:13 -0700 Subject: [PATCH 11/13] [GR-21741] Addressing comments from review --- .../src/com/oracle/svm/core/LibCHelper.java | 4 +- .../com/oracle/svm/core/jdk/OSTargets.java | 68 -------------- .../svm/core/jdk/TimeZoneSubstitutions.java | 69 ++++++++++---- .../src/timeZone.c | 90 +++++++------------ 4 files changed, 85 insertions(+), 146 deletions(-) delete mode 100644 substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/OSTargets.java diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java index 4b61d7ccfef7..860eecae4d84 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java @@ -36,6 +36,8 @@ public class LibCHelper { public static native CCharPointerPointer getEnviron(); @CFunction(transition = Transition.TO_NATIVE) - public static native CCharPointer customFindJavaTZmd(CCharPointer tzmappings); + // Checkstyle: stop + public static native CCharPointer SVM_FindJavaTZmd(CCharPointer tzMappings, int length); + // Checkstyle: start } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/OSTargets.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/OSTargets.java deleted file mode 100644 index c34238132e11..000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/OSTargets.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2020, 2020, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package com.oracle.svm.core.jdk; - -import com.oracle.svm.core.OS; - -import java.util.function.BooleanSupplier; - -public final class OSTargets { - - public static class Windows implements BooleanSupplier { - @Override - public boolean getAsBoolean() { - return OS.getCurrent() == OS.WINDOWS; - } - } - - public static class Darwin implements BooleanSupplier { - @Override - public boolean getAsBoolean() { - return OS.getCurrent() == OS.DARWIN; - } - } - - public static class Linux implements BooleanSupplier { - @Override - public boolean getAsBoolean() { - return OS.getCurrent() == OS.LINUX; - } - } - - public static class UnixLike implements BooleanSupplier { - @Override - public boolean getAsBoolean() { - return OS.getCurrent() == OS.LINUX || OS.getCurrent() == OS.DARWIN; - } - } - - public static class All implements BooleanSupplier { - @Override - public boolean getAsBoolean() { - return true; - } - - } -} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java index 8d162f8f6b91..7bd6641d2810 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -38,11 +38,11 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.option.HostedOptionKey; import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.PinnedObject; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.hosted.Feature; -import org.graalvm.nativeimage.impl.InternalPlatform; +import org.graalvm.word.WordFactory; import java.io.IOException; import java.nio.file.Files; @@ -50,29 +50,60 @@ import java.nio.file.Paths; import java.util.TimeZone; -@Platforms(InternalPlatform.PLATFORM_JNI.class) +/** + * The following classes aim to provide full support for time zones for native-image. This is + * substitution is necessary due to the reliance in JAVA_HOME in the JDK. + * + * In summary in the JDK time zone data is extracted from the underlying platform via native + * methods, later the JDK code process that data to create time zone objects exposed via java apis. + * + * Luckily JAVA_HOME is only really necessary for the Windows operating system. Posix operating + * systems rely on system calls independent of JAVA_HOME (except for null checks done in the JNI + * function wrapping the native time zone JDK functions). Thus for Posix operating this + * implementation, simply, by-passes the null check(in native image JAVA_HOME is null) and calls + * into the original native functions by substituting the JNI method + * TimeZone.getSystemTimeZoneID(String). + * + * In windows, the JRE contains a special file called tzmappings, (see + * <a href="https://docs.oracle.com/javase/9/troubleshoot/time-zone-settings-jre.htm#JSTGD359">time + * zones in the jre</a>), representing the mapping between Windows and Java time zones. The + * tzmappings file is read and parsed in the native JDK code. Thus for windows, + * {@link TimeZoneFeature} reads such file and stores in the image heap at build-time. At run-time + * the contents of the file are passed to a special version of the time zone native functions that + * parse data from the buffer and not from a file. + */ @TargetClass(java.util.TimeZone.class) @SuppressWarnings("unused") final class Target_java_util_TimeZone { - @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) private static volatile TimeZone defaultTimeZone; + @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset) private static TimeZone defaultTimeZone; @Substitute private static String getSystemTimeZoneID(String javaHome) { - String tzmappings = ""; - if (OS.getCurrent() == OS.WINDOWS) { + CCharPointer tzMappingsPtr = WordFactory.nullPointer(); + int contentLen = 0; + PinnedObject pinnedContent = null; + try { TimeZoneSupport timeZoneSupport = ImageSingletons.lookup(TimeZoneSupport.class); byte[] content = timeZoneSupport.getTzMappingsContent(); - tzmappings = new String(content); - } - try (CTypeConversion.CCharPointerHolder tzMappingsHolder = CTypeConversion.toCString(tzmappings)) { - CCharPointer tzMappings = tzMappingsHolder.get(); - CCharPointer tzId = LibCHelper.customFindJavaTZmd(tzMappings); + if (content != null) { + contentLen = content.length; + pinnedContent = PinnedObject.create(content); + tzMappingsPtr = pinnedContent.addressOfArrayElement(0); + } + CCharPointer tzId = LibCHelper.SVM_FindJavaTZmd(tzMappingsPtr, contentLen); return CTypeConversion.toJavaString(tzId); + } finally { + if (pinnedContent != null) { + pinnedContent.close(); + } } } } +/** + * Holds time zone mapping data. + */ final class TimeZoneSupport { final byte[] tzMappingsContent; @@ -83,11 +114,12 @@ final class TimeZoneSupport { public byte[] getTzMappingsContent() { return tzMappingsContent; } - } +/** + * Reads time zone mappings data and stores in the image heap, if necessary. + */ @AutomaticFeature -@Platforms(InternalPlatform.PLATFORM_JNI.class) final class TimeZoneFeature implements Feature { static class Options { @Option(help = "When true, all time zones will be pre-initialized in the image.")// @@ -95,7 +127,7 @@ static class Options { @Override protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) { super.onValueUpdate(values, oldValue, newValue); - printWarning("-H:IncludeAllTimeZones and -H:IncludeTimeZones are deprecated"); + printWarning(); } }; @@ -104,13 +136,14 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean o @Override protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, String oldValue, String newValue) { super.onValueUpdate(values, oldValue, newValue); - printWarning("-H:IncludeAllTimeZones and -H:IncludeTimeZones are deprecated"); + printWarning(); } }; - private static void printWarning(String warning) { + private static void printWarning() { // Checkstyle: stop - System.err.println(warning); + System.err.println("-H:IncludeAllTimeZones and -H:IncludeTimeZones are now deprecated. Native-image includes all timezones" + + "by default."); // Checkstyle: resume } } @@ -119,7 +152,7 @@ private static byte[] cleanCR(byte[] buffer) { byte[] scratch = new byte[buffer.length]; int copied = 0; for (byte b : buffer) { - if (b == 13) { + if (b == '\r') { continue; } scratch[copied++] = b; diff --git a/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c b/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c index a4be7e9b706a..77b0591dc884 100644 --- a/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c +++ b/substratevm/src/com.oracle.svm.native.libchelper/src/timeZone.c @@ -23,13 +23,13 @@ * questions. */ + +#ifdef _WIN64 /* - * The following is an identical copy of the TimeZone_md.c file found at - * https://github.com/graalvm/labs-openjdk-11/blob/67ddc3bcadd985ea26997457aec6696f21caf154/src/java.base/unix/native/libjava/TimeZone_md.c + * The following functions are an identical copy of the functions in file TimeZone_md.c found at + * https://github.com/graalvm/labs-openjdk-11/blob/67ddc3bcadd985ea26997457aec6696f21caf154/src/java.base/windows/native/libjava/TimeZone_md.c * With the exceptions of the commented functions this file has not been modified from its original. */ - -#ifdef _WIN64 #include <windows.h> #include <stdio.h> #include <stdlib.h> @@ -410,7 +410,7 @@ static int getWinTimeZone(char *winZoneName, char *winMapID) * An implementation of fgets on a buffer as opposed to a file object. * */ -static int readnewLine(char *dst, int num, int offset, char *source, int source_len) { +static int SVM_readBufferUntilNewLine(char *dst, int num, int offset, char *source, int source_len) { int read = 0; while(read < num - 1) { if (offset + read == source_len) { @@ -450,8 +450,8 @@ static int readnewLine(char *dst, int num, int offset, char *source, int source_ * The following function differs from the original by accepting a buffer and reading * tzmappings data from it. The original function opens and reads such data from a file. */ -static char *matchJavaTZ_Java11(const char *tzmappings, int value_type, char *tzName, - char *mapID) +static char *SVM_matchJavaTZ(const char *tzmappings, int value_type, char *tzName, + char *mapID, int tzmappingsLen) { int line; int IDmatched = 0; @@ -463,15 +463,14 @@ static char *matchJavaTZ_Java11(const char *tzmappings, int value_type, char *tz int noMapID = *mapID == '\0'; /* no mapID on Vista and later */ int offset = 0; const char* errorMessage = "unknown error"; - int tzmappingsLen; int currLocation; int readChars; line = 0; - tzmappingsLen = strlen(tzmappings); currLocation = 0; readChars = 0; - while((readChars = readnewLine(lineBuffer, sizeof(lineBuffer), currLocation, tzmappings, tzmappingsLen)) != 0) { + // Reads from buffer and not from file + while((readChars = SVM_readBufferUntilNewLine(lineBuffer, sizeof(lineBuffer), currLocation, tzmappings, tzmappingsLen)) != 0) { char *start, *idx, *endp; int itemIndex = 0; @@ -544,57 +543,18 @@ static char *matchJavaTZ_Java11(const char *tzmappings, int value_type, char *tz return NULL; } -/** - * Returns a GMT-offset-based time zone ID. - * - * No change from original just different function name - */ -static char * -customGetGMTOffsetID() -{ - LONG bias = 0; - LONG ret; - HANDLE hKey = NULL; - char zonename[32]; - - // Obtain the current GMT offset value of ActiveTimeBias. - ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, WIN_CURRENT_TZ_KEY, 0, - KEY_READ, (PHKEY)&hKey); - if (ret == ERROR_SUCCESS) { - DWORD val; - DWORD bufSize = sizeof(val); - ULONG valueType = 0; - ret = RegQueryValueExA(hKey, "ActiveTimeBias", - NULL, &valueType, (LPBYTE) &val, &bufSize); - if (ret == ERROR_SUCCESS) { - bias = (LONG) val; - } - (void) RegCloseKey(hKey); - } - - // If we can't get the ActiveTimeBias value, use Bias of TimeZoneInformation. - // Note: Bias doesn't reflect current daylight saving. - if (ret != ERROR_SUCCESS) { - TIME_ZONE_INFORMATION tzi; - if (GetTimeZoneInformation(&tzi) != TIME_ZONE_ID_INVALID) { - bias = tzi.Bias; - } - } - - customZoneName(bias, zonename); - return _strdup(zonename); -} +extern char* getGMTOffsetID(); /* * Detects the platform time zone which maps to a Java time zone ID. * * The following function only differs from the original by calling a custom parsing - * function called "matchJavaTZ_Java11" that accepts a data buffer. + * function called "SVM_matchJavaTZ" that accepts a data buffer. * - * This function expects its argument to contain the whole tzmappings data as an argument. - * The original function expected a buffer whose content was the path to JAVA_HOME + * This function expects its argument to contain the whole tzmappings data and length as an arguments. + * The original function expected a char array whose content was the path to JAVA_HOME */ -char *customFindJavaTZmd(const char *tzmappings) +char *SVM_FindJavaTZmd(const char *tzmappings, int length) { char winZoneName[MAX_ZONE_CHAR]; char winMapID[MAX_MAPID_LENGTH]; @@ -608,10 +568,10 @@ char *customFindJavaTZmd(const char *tzmappings) if (result == VALUE_GMTOFFSET) { std_timezone = _strdup(winZoneName); } else { - std_timezone = matchJavaTZ_Java11(tzmappings, result, - winZoneName, winMapID); + std_timezone = SVM_matchJavaTZ(tzmappings, result, + winZoneName, winMapID, length); if (std_timezone == NULL) { - std_timezone = customGetGMTOffsetID(); + std_timezone = getGMTOffsetID(); } } } @@ -619,7 +579,19 @@ char *customFindJavaTZmd(const char *tzmappings) } #else extern char* findJavaTZ_md(const char *); -char *customFindJavaTZmd(const char *tzmappings) { - return findJavaTZ_md(""); + +char *SVM_FindJavaTZmd(const char *tzmappings, int length) { + + /* + * For POSIX operating systems the original function + * does not need the JAVA_HOME nor tzmappings. Except + * for AIX (which is currently not supported in native image) + * + * We can safely call the original JDK function with java home set to + * NULL. Note the JNI wrapper of the below function, checks JAVA_HOME + * is not NULL and returns NULL if it is, stoppings us from directly + * calling this function from java without some type of substitution. + */ + return findJavaTZ_md((void *) 0); } #endif // _WIN64 From e03b903ec8646932eef4471766059fcd8288f399 Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Fri, 27 Mar 2020 13:16:58 -0700 Subject: [PATCH 12/13] [GR-21741] Implements simple topological sorting native libraries and avoid linking failures --- .../org.graalvm.nativeimage/snapshot.sigtest | 1 + .../nativeimage/c/function/CLibrary.java | 7 + .../src/com/oracle/svm/core/LibCHelper.java | 3 +- .../svm/core/jdk/TimeZoneSubstitutions.java | 18 +-- .../oracle/svm/hosted/c/NativeLibraries.java | 121 +++++++++++++++++- 5 files changed, 135 insertions(+), 15 deletions(-) diff --git a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest index 95f3e0a62e8e..919f385e3be8 100644 --- a/sdk/src/org.graalvm.nativeimage/snapshot.sigtest +++ b/sdk/src/org.graalvm.nativeimage/snapshot.sigtest @@ -483,6 +483,7 @@ CLSS public abstract interface !annotation org.graalvm.nativeimage.c.function.CL anno 0 java.lang.annotation.Target(java.lang.annotation.ElementType[] value=[TYPE, METHOD]) intf java.lang.annotation.Annotation meth public abstract !hasdefault boolean requireStatic() +meth public abstract !hasdefault java.lang.String[] dependsOn() meth public abstract java.lang.String value() CLSS public abstract interface !annotation org.graalvm.nativeimage.c.function.CMacroInfo diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/function/CLibrary.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/function/CLibrary.java index 18c9da039553..ecdb513abfce 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/function/CLibrary.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/function/CLibrary.java @@ -68,4 +68,11 @@ * @since 19.1.0 */ boolean requireStatic() default false; + + /** + * Specifies the name of the libraries this library depends on. + * + * @since 20.0.1 + */ + String[] dependsOn() default {}; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java index 860eecae4d84..c91ae4d9ca5f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/LibCHelper.java @@ -30,7 +30,7 @@ import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CCharPointerPointer; -@CLibrary(value = "libchelper", requireStatic = true) +@CLibrary(value = "libchelper", requireStatic = true, dependsOn = "java") public class LibCHelper { @CFunction(transition = Transition.NO_TRANSITION) public static native CCharPointerPointer getEnviron(); @@ -39,5 +39,4 @@ public class LibCHelper { // Checkstyle: stop public static native CCharPointer SVM_FindJavaTZmd(CCharPointer tzMappings, int length); // Checkstyle: start - } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java index 7bd6641d2810..591cb4741f4d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -51,26 +51,28 @@ import java.util.TimeZone; /** - * The following classes aim to provide full support for time zones for native-image. This is - * substitution is necessary due to the reliance in JAVA_HOME in the JDK. + * The following classes aim to provide full support for time zones for native-image. This + * substitution is necessary due to the reliance on JAVA_HOME in the JDK. * * In summary in the JDK time zone data is extracted from the underlying platform via native - * methods, later the JDK code process that data to create time zone objects exposed via java apis. + * methods, later the JDK code processes that data to create time zone objects exposed via JAVA + * APIs. * * Luckily JAVA_HOME is only really necessary for the Windows operating system. Posix operating * systems rely on system calls independent of JAVA_HOME (except for null checks done in the JNI - * function wrapping the native time zone JDK functions). Thus for Posix operating this + * function wrapping the native time zone JDK functions). Thus for Posix operating systems this * implementation, simply, by-passes the null check(in native image JAVA_HOME is null) and calls * into the original native functions by substituting the JNI method * TimeZone.getSystemTimeZoneID(String). * - * In windows, the JRE contains a special file called tzmappings, (see + * In Windows, the JRE contains a special file called tzmappings, (see * <a href="https://docs.oracle.com/javase/9/troubleshoot/time-zone-settings-jre.htm#JSTGD359">time * zones in the jre</a>), representing the mapping between Windows and Java time zones. The * tzmappings file is read and parsed in the native JDK code. Thus for windows, - * {@link TimeZoneFeature} reads such file and stores in the image heap at build-time. At run-time - * the contents of the file are passed to a special version of the time zone native functions that - * parse data from the buffer and not from a file. + * {@link TimeZoneFeature} reads such file and stores it in the image heap at build-time. At + * run-time the contents of the file are passed to a custom implementation of the JDK time zones + * logic. The only difference in the custom implementation is reading and parsing time zone mappings + * from a buffer as opposed to a file. */ @TargetClass(java.util.TimeZone.class) @SuppressWarnings("unused") diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/NativeLibraries.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/NativeLibraries.java index 1fe588f117dd..db86ca35a058 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/NativeLibraries.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/c/NativeLibraries.java @@ -34,10 +34,14 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -101,7 +105,7 @@ public final class NativeLibraries { private final LinkedHashSet<CLibrary> annotated; private final List<String> libraries; - private final List<String> staticLibraries; + private final DependencyGraph dependencyGraph; private final LinkedHashSet<String> libraryPaths; private final List<CInterfaceError> errors; @@ -118,6 +122,103 @@ public final class NativeLibraries { */ private static final Path CUSTOM_LIBC_STATIC_DIST_PATH = Paths.get("svm", "static-libs"); + public static final class DependencyGraph { + + private static final class Dependency { + private final String name; + private final Set<Dependency> dependencies; + + Dependency(String name, Set<Dependency> dependencies) { + assert dependencies != null; + this.name = name; + this.dependencies = dependencies; + } + + public String getName() { + return name; + } + + public Set<Dependency> getDependencies() { + return dependencies; + } + + @Override + public String toString() { + String depString = dependencies.stream().map(Dependency::getName).collect(Collectors.joining()); + return "Dependency{" + + "name='" + name + '\'' + + ", dependencies=[" + depString + + "]}"; + } + } + + private final Map<String, Dependency> allDependencies; + + public DependencyGraph() { + allDependencies = new ConcurrentHashMap<>(); + } + + public void add(String library, String... dependencies) { + UserError.guarantee(library != null, "The library name must be not null and not empty"); + + Dependency libraryDependency = putWhenAbsent(library, new Dependency(library, new HashSet<>())); + Set<Dependency> collectedDependencies = libraryDependency.getDependencies(); + if (dependencies == null) { + return; + } + + for (String dependency : dependencies) { + collectedDependencies.add(putWhenAbsent( + dependency, new Dependency(dependency, new HashSet<>()))); + } + } + + public List<String> sort() { + final Set<Dependency> discovered = new HashSet<>(); + final Set<Dependency> processed = new LinkedHashSet<>(); + + for (Dependency dep : allDependencies.values()) { + visit(dep, discovered, processed); + } + + LinkedList<String> names = new LinkedList<>(); + processed.forEach(n -> names.push(n.getName())); + return names; + } + + private Dependency putWhenAbsent(String libName, Dependency dep) { + if (!allDependencies.containsKey(libName)) { + allDependencies.put(libName, dep); + } + return allDependencies.get(libName); + } + + private void visit(Dependency dep, Set<Dependency> discovered, Set<Dependency> processed) { + if (processed.contains(dep)) { + return; + } + if (discovered.contains(dep)) { + String message = String.format("While building list of static libraries dependencies a cycle was discovered for dependency: %s ", dep.getName()); + UserError.abort(message); + } + + discovered.add(dep); + dep.getDependencies().forEach(d -> visit(d, discovered, processed)); + processed.add(dep); + } + + @Override + public String toString() { + String depsStr = allDependencies.values() + .stream() + .map(Dependency::toString) + .collect(Collectors.joining("\n")); + return "DependencyGraph{\n" + + depsStr + + '}'; + } + } + public NativeLibraries(ConstantReflectionProvider constantReflection, MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflection, TargetDescription target, ClassInitializationSupport classInitializationSupport, Path tempDirectory, DebugContext debug) { this.metaAccess = metaAccess; @@ -152,7 +253,7 @@ public NativeLibraries(ConstantReflectionProvider constantReflection, MetaAccess * libraries that have cyclic dependencies. */ libraries = Collections.synchronizedList(new ArrayList<>()); - staticLibraries = Collections.synchronizedList(new ArrayList<>()); + dependencyGraph = new DependencyGraph(); libraryPaths = initCLibraryPath(); @@ -270,7 +371,15 @@ public void addAnnotated(CLibrary library) { } public void addLibrary(String library, boolean requireStatic) { - (requireStatic ? staticLibraries : libraries).add(library); + addLibrary(library, requireStatic, null); + } + + public void addLibrary(String library, boolean requireStatic, String[] dependencies) { + if (requireStatic) { + dependencyGraph.add(library, dependencies); + } else { + libraries.add(library); + } } public Collection<String> getLibraries() { @@ -280,7 +389,9 @@ public Collection<String> getLibraries() { public Collection<Path> getStaticLibraries() { Map<Path, Path> allStaticLibs = getAllStaticLibs(); List<Path> staticLibs = new ArrayList<>(); - for (String staticLibraryName : staticLibraries) { + List<String> sortedList = dependencyGraph.sort(); + + for (String staticLibraryName : sortedList) { Path libraryPath = getStaticLibraryPath(allStaticLibs, staticLibraryName); if (libraryPath == null) { continue; @@ -444,7 +555,7 @@ public boolean processAnnotated() { return false; } for (CLibrary lib : annotated) { - addLibrary(lib.value(), lib.requireStatic()); + addLibrary(lib.value(), lib.requireStatic(), lib.dependsOn()); } annotated.clear(); return true; From d9992035ab8598263434be43ce44a555fc7287b3 Mon Sep 17 00:00:00 2001 From: Esteban Ginez <esteban.ginez@oracle.com> Date: Mon, 6 Apr 2020 14:33:23 -0700 Subject: [PATCH 13/13] [GR-21741] Avoids data on posix oses --- .../src/org/graalvm/nativeimage/c/function/CLibrary.java | 2 +- .../src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/function/CLibrary.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/function/CLibrary.java index ecdb513abfce..de00d235a5cf 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/function/CLibrary.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/c/function/CLibrary.java @@ -72,7 +72,7 @@ /** * Specifies the name of the libraries this library depends on. * - * @since 20.0.1 + * @since 20.1.0 */ String[] dependsOn() default {}; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java index 591cb4741f4d..1fadd645f47a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/TimeZoneSubstitutions.java @@ -86,9 +86,8 @@ private static String getSystemTimeZoneID(String javaHome) { int contentLen = 0; PinnedObject pinnedContent = null; try { - TimeZoneSupport timeZoneSupport = ImageSingletons.lookup(TimeZoneSupport.class); - byte[] content = timeZoneSupport.getTzMappingsContent(); - if (content != null) { + if (ImageSingletons.contains(TimeZoneSupport.class)) { + byte[] content = ImageSingletons.lookup(TimeZoneSupport.class).getTzMappingsContent(); contentLen = content.length; pinnedContent = PinnedObject.create(content); tzMappingsPtr = pinnedContent.addressOfArrayElement(0); @@ -168,7 +167,6 @@ private static byte[] cleanCR(byte[] buffer) { public void afterRegistration(AfterRegistrationAccess access) { if (OS.getCurrent() != OS.WINDOWS) { - ImageSingletons.add(TimeZoneSupport.class, new TimeZoneSupport(null)); return; }