Skip to content

Commit 2382af5

Browse files
committed
Check undefined system properties
Many system properties are undefined in native image. Getting such properties with System.getProperty returns null. It may result unexpected behaviors. This patch checks the usage of such properties at native image build, and throw exception at run time if -R:+ReportUndefinedSystemPropertyError option is set.
1 parent 1e281f3 commit 2382af5

File tree

3 files changed

+253
-22
lines changed

3 files changed

+253
-22
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemPropertiesSupport.java

Lines changed: 145 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,21 @@
2424
*/
2525
package com.oracle.svm.core.jdk;
2626

27+
import java.util.ArrayList;
28+
import java.util.Arrays;
2729
import java.util.Collections;
2830
import java.util.HashMap;
31+
import java.util.HashSet;
32+
import java.util.List;
2933
import java.util.Map;
3034
import java.util.Properties;
35+
import java.util.Set;
36+
import java.util.concurrent.ConcurrentHashMap;
3137
import java.util.function.Supplier;
3238

39+
import com.oracle.svm.core.option.RuntimeOptionKey;
40+
import org.graalvm.compiler.options.Option;
41+
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
3342
import org.graalvm.nativeimage.ImageInfo;
3443
import org.graalvm.nativeimage.Platform;
3544
import org.graalvm.nativeimage.Platforms;
@@ -48,25 +57,126 @@
4857
*/
4958
public abstract class SystemPropertiesSupport {
5059

60+
public static class SystemPropertyFeatureOptions{
61+
@Option(help = "Report the usage of undefined property as exception when it is not explicitly set)")//
62+
public static final RuntimeOptionKey<Boolean> ReportUndefinedSystemPropertyError = new RuntimeOptionKey<>(false);
63+
}
64+
65+
private enum ChangedType{
66+
Removed, Added
67+
}
68+
69+
private static class PropertyInfo {
70+
public PropertyInfo(String name, int version, ChangedType changed) {
71+
this(name, version, changed, false, null);
72+
}
73+
74+
public PropertyInfo(String name, int version, ChangedType changed, boolean sameAsHosted){
75+
this(name, version, changed, sameAsHosted, null);
76+
}
77+
78+
public PropertyInfo(String name, int version, ChangedType changed, boolean sameAsHosted, String initValue) {
79+
this.name = name;
80+
this.version = version;
81+
this.changed = changed;
82+
this.sameAsHosted = sameAsHosted;
83+
this.initValue = initValue;
84+
}
85+
86+
public String name;
87+
public int version;
88+
public ChangedType changed;
89+
public boolean sameAsHosted;
90+
public String initValue;
91+
}
92+
5193
/** System properties that are taken from the VM hosting the image generator. */
52-
private static final String[] HOSTED_PROPERTIES = {
53-
"java.version",
54-
ImageInfo.PROPERTY_IMAGE_KIND_KEY,
55-
/*
56-
* We do not support cross-compilation for now. Separator might also be cached
57-
* in other classes, so changing them would be tricky.
58-
*/
59-
"line.separator", "path.separator", "file.separator",
60-
/* For our convenience for now. */
61-
"file.encoding", "sun.jnu.encoding",
62-
"java.class.version",
63-
"java.specification.name",
64-
"java.specification.vendor",
65-
"java.specification.version",
66-
"java.vm.specification.name",
67-
"java.vm.specification.vendor",
68-
"java.vm.specification.version"
69-
};
94+
private static final Set<String> HOSTED_PROPERTIES = new HashSet<>(Arrays.asList(
95+
"java.version",
96+
ImageInfo.PROPERTY_IMAGE_KIND_KEY,
97+
/*
98+
* We do not support cross-compilation for now. Separator might also be cached
99+
* in other classes, so changing them would be tricky.
100+
*/
101+
"line.separator", "path.separator", "file.separator",
102+
/* For our convenience for now. */
103+
"file.encoding", "sun.jnu.encoding",
104+
"java.class.version",
105+
"java.specification.name",
106+
"java.specification.vendor",
107+
"java.specification.version",
108+
"java.vm.specification.name",
109+
"java.vm.specification.vendor",
110+
"java.vm.specification.version",
111+
"java.runtime.version",
112+
"sun.cpu.endian",
113+
"sun.cpu.isalist",
114+
"sun.io.unicode.encoding"));
115+
116+
/** Undefined properties that are not changed since JDK8.
117+
* This collection is used as baseline*/
118+
private static final String[] UNDEFINED_UNCHANGED_PROPERTIES_8 = {
119+
"awt.toolkit",
120+
"java.awt.graphicsenv",
121+
"java.awt.printerjob",
122+
"java.home",
123+
"sun.java.command",
124+
"sun.java.launcher",
125+
"sun.os.patch.level",
126+
"user.country",
127+
"user.language",
128+
"user.timezone"};
129+
130+
/** Properties are removed or added since JDK8*/
131+
private static final List<PropertyInfo> CHANGED_PROPERTIES_SINCE_8 = new ArrayList<>(
132+
Arrays.asList(
133+
// http://openjdk.java.net/jeps/261 http://hg.openjdk.java.net/jdk9/jdk9/hotspot/rev/c558850fac57
134+
new PropertyInfo("sun.boot.class.path", 9, ChangedType.Removed, false, ""),
135+
// http://hg.openjdk.java.net/jdk9/jdk9/jdk/rev/e336cbd8b15e
136+
new PropertyInfo("java.ext.dirs", 9, ChangedType.Removed, false, ""),
137+
new PropertyInfo("java.endorsed.dirs", 9, ChangedType.Removed, false, ""),
138+
// http://hg.openjdk.java.net/jdk9/jdk9/hotspot/rev/3b241fb72b89
139+
new PropertyInfo("java.vm.compressedOopsMode", 9, ChangedType.Added),
140+
// http://hg.openjdk.java.net/jdk9/jdk9/hotspot/rev/39c579b50006
141+
new PropertyInfo("jdk.debug", 9, ChangedType.Added),
142+
// https://openjdk.java.net/jeps/322
143+
new PropertyInfo("java.version.date", 10, ChangedType.Added, true),
144+
new PropertyInfo("java.vendor.version", 10, ChangedType.Added, true),
145+
// https://github.com/openjdk/jdk/commit/d4941f14af150fb81db7cff5394192637be701dd
146+
new PropertyInfo("file.encoding.pkg", 11, ChangedType.Removed, true)
147+
)
148+
);
149+
150+
/** Properties can be initiated at build time and are available at build time's JDK version */
151+
private static final List<PropertyInfo> CUR_VERSION_INIT_PROPERTIES = new ArrayList<>();
152+
153+
/** Undefined properties for JDK version got from JavaVersionUtil.JAVA_SPEC */
154+
private static final Set<String> UNDEFINED_PROPERTIES = ConcurrentHashMap.newKeySet();
155+
156+
static {
157+
UNDEFINED_PROPERTIES.addAll(Arrays.asList(UNDEFINED_UNCHANGED_PROPERTIES_8));
158+
159+
for (PropertyInfo propertyInfo : CHANGED_PROPERTIES_SINCE_8) {
160+
// Add properties introduced at current and lower JDK versions
161+
if (JavaVersionUtil.JAVA_SPEC >= propertyInfo.version && propertyInfo.changed == ChangedType.Added) {
162+
addProperty(propertyInfo);
163+
}
164+
// Add back properties that are removed in higher JDK version
165+
if (JavaVersionUtil.JAVA_SPEC < propertyInfo.version && propertyInfo.changed == ChangedType.Removed) {
166+
addProperty(propertyInfo);
167+
}
168+
}
169+
}
170+
171+
private static void addProperty(PropertyInfo propertyInfo) {
172+
if (!propertyInfo.sameAsHosted && propertyInfo.initValue == null) {
173+
UNDEFINED_PROPERTIES.add(propertyInfo.name);
174+
} else if (propertyInfo.sameAsHosted) {
175+
HOSTED_PROPERTIES.add(propertyInfo.name);
176+
} else {
177+
CUR_VERSION_INIT_PROPERTIES.add(propertyInfo);
178+
}
179+
}
70180

71181
/** System properties that are lazily computed at run time on first access. */
72182
private final Map<String, Supplier<String>> lazyRuntimeValues;
@@ -95,9 +205,10 @@ protected SystemPropertiesSupport() {
95205
initializeProperty("java.vendor.url", "https://www.graalvm.org/");
96206

97207
initializeProperty("java.class.path", "");
98-
initializeProperty("java.endorsed.dirs", "");
99-
initializeProperty("java.ext.dirs", "");
100-
initializeProperty("java.library.path", "");
208+
initializeProperty("java.runtime.name", "Substrate VM Runtime Environment");
209+
initializeProperty("sun.management.compiler", "GraalVM Compiler");
210+
initializeProperty("java.vendor.url.bug", "https://github.com/oracle/graal/issues");
211+
initializeProperty("java.vm.info", "static mode");
101212
initializeProperty("sun.arch.data.model", Integer.toString(ConfigurationValues.getTarget().wordJavaKind.getBitCount()));
102213

103214
String targetName = System.getProperty("svm.targetName");
@@ -107,6 +218,10 @@ protected SystemPropertiesSupport() {
107218

108219
initializeProperty(ImageInfo.PROPERTY_IMAGE_CODE_KEY, ImageInfo.PROPERTY_IMAGE_CODE_VALUE_RUNTIME);
109220

221+
for(PropertyInfo propertyInfo: CUR_VERSION_INIT_PROPERTIES){
222+
initializeProperty(propertyInfo.name, propertyInfo.initValue);
223+
}
224+
110225
lazyRuntimeValues = new HashMap<>();
111226
lazyRuntimeValues.put("user.name", this::userName);
112227
lazyRuntimeValues.put("user.home", this::userHome);
@@ -141,7 +256,15 @@ public Properties getProperties() {
141256

142257
protected String getProperty(String key) {
143258
initializeLazyValue(key);
144-
return properties.getProperty(key);
259+
String ret = properties.getProperty(key);
260+
if (ret == null && isUndefined(key) && SystemPropertyFeatureOptions.ReportUndefinedSystemPropertyError.getValue()) {
261+
throw new UndefinedSystemPropertyException("Java system property " + key + " is incompatible in native image. Please explicitly assign the expected value via -D" + key + "=, or avoid using this property.");
262+
}
263+
return ret;
264+
}
265+
266+
public static boolean isUndefined(String key) {
267+
return UNDEFINED_PROPERTIES.contains(key);
145268
}
146269

147270
public void setProperties(Properties props) {
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
package com.oracle.svm.core.jdk;
27+
28+
public class UndefinedSystemPropertyException extends RuntimeException{
29+
public UndefinedSystemPropertyException(String message){
30+
super(message);
31+
}
32+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2020 Alibaba Group Holding Limited. All Rights Reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
package com.oracle.svm.hosted;
27+
28+
import com.oracle.svm.core.annotate.AutomaticFeature;
29+
import com.oracle.svm.core.graal.GraalFeature;
30+
import com.oracle.svm.core.jdk.SystemPropertiesSupport;
31+
import jdk.vm.ci.meta.ResolvedJavaMethod;
32+
import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
33+
import org.graalvm.compiler.nodes.ValueNode;
34+
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
35+
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
36+
import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
37+
import org.graalvm.compiler.phases.util.Providers;
38+
39+
import java.util.Set;
40+
import java.util.concurrent.ConcurrentHashMap;
41+
42+
@AutomaticFeature
43+
public class SystemPropertyFeature implements GraalFeature {
44+
45+
private final Set<String> systemPropertyCalls = ConcurrentHashMap.newKeySet();
46+
47+
@Override
48+
public void afterImageWrite(AfterImageWriteAccess access) {
49+
if (!systemPropertyCalls.isEmpty()) {
50+
StringBuilder sb = new StringBuilder("\n\nNative image undefined system properties are used in the program:\n");
51+
for (String s : systemPropertyCalls) {
52+
sb.append(" ").append(s).append("\n");
53+
}
54+
sb.append("The returned value of calling System.getProperty with these keys will be null in native image.\n");
55+
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");
56+
System.out.println(sb.toString());
57+
}
58+
}
59+
60+
@Override
61+
public void registerInvocationPlugins(Providers providers, SnippetReflectionProvider snippetReflection, InvocationPlugins invocationPlugins, boolean analysis, boolean hosted) {
62+
InvocationPlugins.Registration r = new InvocationPlugins.Registration(invocationPlugins, System.class);
63+
r.register1("getProperty", String.class, new InvocationPlugin() {
64+
@Override
65+
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode name) {
66+
if (name.isConstant()) {
67+
String propertyKey = snippetReflection.asObject(String.class, name.asJavaConstant());
68+
if (SystemPropertiesSupport.isUndefined(propertyKey)) {
69+
systemPropertyCalls.add("Undefined property: " + propertyKey + " is used at " + b.getCode().asStackTraceElement(b.bci()));
70+
}
71+
}
72+
return false;
73+
}
74+
});
75+
}
76+
}

0 commit comments

Comments
 (0)