diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java index 4a37d4233603..4f94b7092637 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java @@ -28,8 +28,12 @@ import java.util.HashMap; import java.util.Map; import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BooleanSupplier; import java.util.function.Supplier; +import com.oracle.svm.core.option.RuntimeOptionKey; +import org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.ImageInfo; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -41,32 +45,109 @@ * This class maintains the system properties at run time. * * Some of the standard system properties can just be taken from the image generator: - * {@link #HOSTED_PROPERTIES}. Other important system properties need to be computed at run time. + * Other important system properties need to be computed at run time. * However, we want to do the computation lazily to reduce the startup cost. For example, getting * the current working directory is quite expensive. We initialize such a property either when it is * explicitly accessed, or when all properties are accessed. */ public abstract class SystemPropertiesSupport { - /** System properties that are taken from the VM hosting the image generator. */ - private static final String[] HOSTED_PROPERTIES = { - "java.version", - ImageInfo.PROPERTY_IMAGE_KIND_KEY, - /* - * We do not support cross-compilation for now. Separator might also be cached - * in other classes, so changing them would be tricky. - */ - "line.separator", "path.separator", "file.separator", - /* For our convenience for now. */ - "file.encoding", "sun.jnu.encoding", - "java.class.version", - "java.specification.name", - "java.specification.vendor", - "java.specification.version", - "java.vm.specification.name", - "java.vm.specification.vendor", - "java.vm.specification.version" - }; + public static class SystemPropertyFeatureOptions{ + @Option(help = "Report the usage of undefined property as exception when it is not explicitly set)")// + public static final RuntimeOptionKey ReportUndefinedSystemPropertyError = new RuntimeOptionKey<>(false); + } + + private enum InitKind {AsHosted, Runtime, BuildTime, Undefined} + + private static class PropertyInfo { + private String name; + private BooleanSupplier onlyWith; + private InitKind initKind; + private Supplier initValue; + + public PropertyInfo(String name, BooleanSupplier onlyWith, InitKind InitKind, Supplier initValue) { + if (name == null) { + throw new NullPointerException("Property name should never be null!"); + } + this.name = name; + this.onlyWith = onlyWith; + this.initKind = InitKind; + this.initValue = initValue; + } + + } + + private static final Map SYSTEM_PROPERTIES= new ConcurrentHashMap<>(); + + private static final JDK8OrEarlier jdk8OrEarlier = new JDK8OrEarlier(); + private static final JDK11OrLater jdk11OrLater = new JDK11OrLater(); + private static final BooleanSupplier always = ()-> true; + private static final Supplier STRING_SUPPLIER_SUPPLIER = () -> ""; + + static { + //Set properties same as hosted + addAsHostedSystemProperty("file.encoding", always); + // https://github.com/openjdk/jdk/commit/d4941f14af150fb81db7cff5394192637be701dd + addAsHostedSystemProperty("file.encoding.pkg", jdk8OrEarlier); + addAsHostedSystemProperty("file.separator", always); + addAsHostedSystemProperty("java.class.version", always); + addAsHostedSystemProperty("java.runtime.version", always); + addAsHostedSystemProperty("java.specification.name", always); + addAsHostedSystemProperty("java.specification.vendor", always); + addAsHostedSystemProperty("java.specification.version", always); + addAsHostedSystemProperty("java.version", always); + // https://openjdk.java.net/jeps/322 + addAsHostedSystemProperty("java.version.date", jdk11OrLater); + addAsHostedSystemProperty("java.vendor.version", jdk11OrLater); + addAsHostedSystemProperty("java.vm.specification.name", always); + addAsHostedSystemProperty("java.vm.specification.vendor", always); + addAsHostedSystemProperty("java.vm.specification.version", always); + addAsHostedSystemProperty("line.separator", always); + addAsHostedSystemProperty("path.separator", always); + addAsHostedSystemProperty("sun.cpu.endian", always); + addAsHostedSystemProperty("sun.cpu.isalist", always); + addAsHostedSystemProperty("sun.jnu.encoding", always); + addAsHostedSystemProperty("sun.io.unicode.encoding", always); + addAsHostedSystemProperty(ImageInfo.PROPERTY_IMAGE_KIND_KEY, always); + + //Undefined properties + addUndefinedSystemProperty("awt.toolkit", always); + addUndefinedSystemProperty("java.awt.graphicsenv", always); + addUndefinedSystemProperty("java.awt.printerjob", always); + addUndefinedSystemProperty("java.home", always); + addUndefinedSystemProperty("sun.java.command", always); + addUndefinedSystemProperty("sun.java.launcher", always); + addUndefinedSystemProperty("sun.os.patch.level", always); + addUndefinedSystemProperty("user.country", always); + addUndefinedSystemProperty("user.language", always); + addUndefinedSystemProperty("user.timezone", always); + // http://hg.openjdk.java.net/jdk9/jdk9/hotspot/rev/3b241fb72b89 + addUndefinedSystemProperty("java.vm.compressedOopsMode", jdk11OrLater); + // http://hg.openjdk.java.net/jdk9/jdk9/hotspot/rev/39c579b50006 + addUndefinedSystemProperty("jdk.debug", jdk11OrLater); + + // Build time initialized properties + addSystemProperty("java.class.path", always, InitKind.BuildTime, STRING_SUPPLIER_SUPPLIER); + // http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/e336cbd8b15e + addSystemProperty("java.endorsed.dirs", jdk8OrEarlier, InitKind.BuildTime, STRING_SUPPLIER_SUPPLIER); + addSystemProperty("java.ext.dirs", jdk8OrEarlier, InitKind.BuildTime, STRING_SUPPLIER_SUPPLIER); + addSystemProperty("java.runtime.name", always, InitKind.BuildTime, ()->"Substrate VM Runtime Environment"); + addSystemProperty("java.vendor", always, InitKind.BuildTime, ()->"Oracle Corporation"); + addSystemProperty("java.vendor.url.bug", always, InitKind.BuildTime, ()->"https://github.com/oracle/graal/issues"); + addSystemProperty("java.vm.info", always, InitKind.BuildTime, ()->"static mode"); + addSystemProperty("java.vm.name", always, InitKind.BuildTime, () ->"Substrate VM"); + addSystemProperty("java.vm.vendor", always, InitKind.BuildTime, ()->"Oracle Corporation"); + addSystemProperty("java.vendor.url", always, InitKind.BuildTime, ()->"https://www.graalvm.org/"); + String targetArch = System.getProperty("svm.targetArch"); + addSystemProperty("os.arch", always, InitKind.BuildTime, ()->targetArch != null ? targetArch : System.getProperty("os.arch")); + String targetName = System.getProperty("svm.targetName"); + addSystemProperty("os.name", always, InitKind.BuildTime, ()->targetName != null ? targetName : System.getProperty("os.name")); + addSystemProperty("sun.arch.data.model", always, InitKind.BuildTime, ()->Integer.toString(ConfigurationValues.getTarget().wordJavaKind.getBitCount())); + // http://openjdk.java.net/jeps/261 http://hg.openjdk.java.net/jdk9/jdk9/hotspot/rev/c558850fac57 + addSystemProperty("sun.boot.class.path", jdk8OrEarlier, InitKind.BuildTime, STRING_SUPPLIER_SUPPLIER); + addSystemProperty("sun.management.compiler", always, InitKind.BuildTime,()-> "GraalVM Compiler"); + addSystemProperty(ImageInfo.PROPERTY_IMAGE_CODE_KEY, always, InitKind.BuildTime, ()->ImageInfo.PROPERTY_IMAGE_CODE_VALUE_RUNTIME); + } /** System properties that are lazily computed at run time on first access. */ private final Map> lazyRuntimeValues; @@ -83,37 +164,43 @@ protected SystemPropertiesSupport() { savedProperties = new HashMap<>(); readOnlySavedProperties = Collections.unmodifiableMap(savedProperties); - for (String key : HOSTED_PROPERTIES) { - String value = System.getProperty(key); - properties.put(key, value); - savedProperties.put(key, value); - } - - initializeProperty("java.vm.name", "Substrate VM"); - initializeProperty("java.vm.vendor", "Oracle Corporation"); - initializeProperty("java.vendor", "Oracle Corporation"); - initializeProperty("java.vendor.url", "https://www.graalvm.org/"); + // Runtime initialized properties + addSystemProperty("user.name", always, InitKind.Runtime, this::userName); + addSystemProperty("user.home", always, InitKind.Runtime, this::userHome); + addSystemProperty("user.dir", always, InitKind.Runtime, this::userDir); + addSystemProperty("java.io.tmpdir", always, InitKind.Runtime, this::tmpdirValue); + addSystemProperty("os.version", always, InitKind.Runtime, this::osVersionValue); + addSystemProperty("java.vm.version", always, InitKind.Runtime, VM::getVersion); - initializeProperty("java.class.path", ""); - initializeProperty("java.endorsed.dirs", ""); - initializeProperty("java.ext.dirs", ""); - initializeProperty("java.library.path", ""); - initializeProperty("sun.arch.data.model", Integer.toString(ConfigurationValues.getTarget().wordJavaKind.getBitCount())); + lazyRuntimeValues = new HashMap<>(); + SYSTEM_PROPERTIES.forEach((key, propertyInfo) -> { + if (propertyInfo.onlyWith.getAsBoolean()) { + switch (propertyInfo.initKind){ + case AsHosted: + initializeProperty(key, System.getProperty(key)); + break; + case BuildTime: + initializeProperty(key, propertyInfo.initValue.get()); + break; + case Undefined: + break; + case Runtime: + lazyRuntimeValues.put(key, propertyInfo.initValue); + } + } + }); + } - String targetName = System.getProperty("svm.targetName"); - String targetArch = System.getProperty("svm.targetArch"); - initializeProperty("os.name", targetName != null ? targetName : System.getProperty("os.name")); - initializeProperty("os.arch", targetArch != null ? targetArch : System.getProperty("os.arch")); + private static void addSystemProperty(String name, BooleanSupplier onlyWith, InitKind InitKind, Supplier initValue){ + SYSTEM_PROPERTIES.put(name,new PropertyInfo(name, onlyWith, InitKind, initValue)); + } - initializeProperty(ImageInfo.PROPERTY_IMAGE_CODE_KEY, ImageInfo.PROPERTY_IMAGE_CODE_VALUE_RUNTIME); + private static void addUndefinedSystemProperty(String name, BooleanSupplier onlyWith) { + SYSTEM_PROPERTIES.put(name,new PropertyInfo(name, onlyWith, InitKind.Undefined, null)); + } - lazyRuntimeValues = new HashMap<>(); - lazyRuntimeValues.put("user.name", this::userName); - lazyRuntimeValues.put("user.home", this::userHome); - lazyRuntimeValues.put("user.dir", this::userDir); - lazyRuntimeValues.put("java.io.tmpdir", this::tmpdirValue); - lazyRuntimeValues.put("os.version", this::osVersionValue); - lazyRuntimeValues.put("java.vm.version", VM::getVersion); + private static void addAsHostedSystemProperty(String name, BooleanSupplier onlyWith){ + SYSTEM_PROPERTIES.put(name,new PropertyInfo(name, onlyWith, InitKind.AsHosted, null)); } private void ensureFullyInitialized() { @@ -141,7 +228,16 @@ public Properties getProperties() { protected String getProperty(String key) { initializeLazyValue(key); - return properties.getProperty(key); + String ret = properties.getProperty(key); + if (ret == null && isUndefined(key) && SystemPropertyFeatureOptions.ReportUndefinedSystemPropertyError.getValue()) { + throw new UndefinedSystemPropertyException("Java system property " + key + " is undefined in native image. Please explicitly assign the expected value via -D" + key + "=, or avoid using this property."); + } + return ret; + } + + public static boolean isUndefined(String key) { + PropertyInfo propertyInfo = SYSTEM_PROPERTIES.get(key); + return propertyInfo != null && propertyInfo.initKind == InitKind.Undefined; } public void setProperties(Properties props) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UndefinedSystemPropertyException.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UndefinedSystemPropertyException.java new file mode 100644 index 000000000000..1d89937a6f4a --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/UndefinedSystemPropertyException.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 Alibaba Group Holding Limited. 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; + +public class UndefinedSystemPropertyException extends RuntimeException{ + public UndefinedSystemPropertyException(String message){ + super(message); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SystemPropertyFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SystemPropertyFeature.java new file mode 100644 index 000000000000..855608b8a975 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SystemPropertyFeature.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020 Alibaba Group Holding Limited. 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.hosted; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.graal.GraalFeature; +import com.oracle.svm.core.jdk.SystemPropertiesSupport; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; +import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; +import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; +import org.graalvm.compiler.phases.util.Providers; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +@AutomaticFeature +public class SystemPropertyFeature implements GraalFeature { + + private final Set systemPropertyCalls = ConcurrentHashMap.newKeySet(); + + @Override + public void afterImageWrite(AfterImageWriteAccess access) { + if (!systemPropertyCalls.isEmpty()) { + StringBuilder sb = new StringBuilder("\n\nNative image undefined system properties are used in the program:\n"); + for (String s : systemPropertyCalls) { + sb.append(" ").append(s).append("\n"); + } + sb.append("The returned value of calling System.getProperty with these keys will be null in native image.\n"); + sb.append("Please make sure the expected property values are specified with -D when running the native image program or just avoid using them.\n\n"); + System.out.println(sb.toString()); + } + } + + @Override + public void registerInvocationPlugins(Providers providers, SnippetReflectionProvider snippetReflection, InvocationPlugins invocationPlugins, boolean analysis, boolean hosted) { + InvocationPlugins.Registration r = new InvocationPlugins.Registration(invocationPlugins, System.class); + r.register1("getProperty", String.class, new InvocationPlugin() { + @Override + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode name) { + if (name.isConstant()) { + String propertyKey = snippetReflection.asObject(String.class, name.asJavaConstant()); + if (SystemPropertiesSupport.isUndefined(propertyKey)) { + systemPropertyCalls.add("Undefined property: " + propertyKey + " is used at " + b.getCode().asStackTraceElement(b.bci())); + } + } + return false; + } + }); + } +}