Skip to content

Report warning for unsupported system properties #2834

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Boolean> 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<String> initValue;

public PropertyInfo(String name, BooleanSupplier onlyWith, InitKind InitKind, Supplier<String> 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<String, PropertyInfo> 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> 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<String, Supplier<String>> lazyRuntimeValues;
Expand All @@ -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<String> 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() {
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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<String> 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;
}
});
}
}