diff --git a/rhino-tools/src/main/java/org/mozilla/javascript/tools/shell/Main.java b/rhino-tools/src/main/java/org/mozilla/javascript/tools/shell/Main.java
index cb87deeeae..60cb08f00b 100644
--- a/rhino-tools/src/main/java/org/mozilla/javascript/tools/shell/Main.java
+++ b/rhino-tools/src/main/java/org/mozilla/javascript/tools/shell/Main.java
@@ -32,7 +32,6 @@
import org.mozilla.javascript.JavaScriptException;
import org.mozilla.javascript.Kit;
import org.mozilla.javascript.NativeArray;
-import org.mozilla.javascript.RhinoConfig;
import org.mozilla.javascript.RhinoException;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
@@ -40,6 +39,7 @@
import org.mozilla.javascript.SecurityController;
import org.mozilla.javascript.commonjs.module.ModuleScope;
import org.mozilla.javascript.commonjs.module.Require;
+import org.mozilla.javascript.config.RhinoConfig;
import org.mozilla.javascript.tools.SourceReader;
import org.mozilla.javascript.tools.ToolErrorReporter;
@@ -133,7 +133,7 @@ public void quit(Context cx, int exitCode) {
*/
public static void main(String args[]) {
try {
- if (RhinoConfig.DEFAULT.useJavaPolicySecurity()) {
+ if (RhinoConfig.get("rhino.use_java_policy_security", false)) {
initJavaPolicySecuritySupport();
}
} catch (SecurityException ex) {
diff --git a/rhino/src/main/java/module-info.java b/rhino/src/main/java/module-info.java
index 90cad354c6..c7e94dd700 100644
--- a/rhino/src/main/java/module-info.java
+++ b/rhino/src/main/java/module-info.java
@@ -10,6 +10,7 @@
exports org.mozilla.javascript.serialize;
exports org.mozilla.javascript.typedarrays;
exports org.mozilla.javascript.xml;
+ exports org.mozilla.javascript.config;
requires java.compiler;
requires jdk.dynalink;
diff --git a/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java b/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java
deleted file mode 100644
index 7d26780703..0000000000
--- a/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java
+++ /dev/null
@@ -1,237 +0,0 @@
-package org.mozilla.javascript;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.security.AccessController;
-import java.security.PrivilegedAction;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * Utility class to read current rhino configuration.
- *
- *
Rhino properties typically begins with "rhino." (properties) or "RHINO_" (env)
- *
- *
The configuration is read from these locations:
- *
- *
- * - rhino.config file from current class' classpath
- *
- rhino.config file from current threas's classpath
- *
- rhino.config file from current directory
- *
- System-properties starting with "rhino."
- *
- env variables starting with "RHINO_" (underscores are replaced by '.' and string is
- *
- *
- * The config files are in UTF-8 format and all keys in this configuration are case-insensitive
- * and dot/underscore-insensitive.
- *
- *
This means, "rhino.use_java_policy_security" is equvalent to "RHINO_USE_JAVA_POLICY_SECURITY"
- *
- * @author Roland Praml, Foconis Analytics GmbH
- */
-public class RhinoConfig {
-
- public static final RhinoConfig DEFAULT =
- AccessController.doPrivileged((PrivilegedAction) () -> init());
-
- private static RhinoConfig init() {
- RhinoConfig config = new RhinoConfig();
- // we parse the following locations
- config.loadFromClasspath(RhinoConfig.class.getClassLoader());
- config.loadFromClasspath(Thread.currentThread().getContextClassLoader());
- config.loadFromFile(new File("rhino.config"));
- config.load(System.getProperties(), "System.properties");
- config.load(System.getenv(), "System.env");
- return config;
- }
-
- private void loadFromFile(File config) {
- if (!config.exists()) {
- return;
- }
- try (InputStream in = new FileInputStream(config)) {
- Properties props = new Properties();
- props.load(new InputStreamReader(in, StandardCharsets.UTF_8));
- load(props, config.getAbsolutePath());
- } catch (IOException e) {
- System.err.println(
- "Error loading rhino.config from "
- + config.getAbsolutePath()
- + ": "
- + e.getMessage());
- }
- }
-
- @SuppressWarnings("unchecked")
- private void loadFromClasspath(ClassLoader cl) {
- if (cl == null) {
- return;
- }
- URL resource = cl.getResource("rhino.config");
- if (resource == null) {
- return;
- }
- try (InputStream in = resource.openStream()) {
- Properties props = new Properties();
- props.load(new InputStreamReader(in, StandardCharsets.UTF_8));
- load(props, resource.toString());
- } catch (IOException e) {
- System.err.println(
- "Error loading rhino.config from " + resource + ": " + e.getMessage());
- }
- }
-
- /** Replacement for {@link System#getProperty(String)}. */
- public static String getProperty(String key) {
- return null;
- }
-
- /** Replacement for {@link System#getProperty(String, String)}. */
- public static String getProperty(String key, String defaultValue) {
- String val = getProperty(key);
- return (val == null) ? defaultValue : val;
- }
-
- /** Replacement for {@link Boolean#getBoolean(String)}. */
- public static boolean getBoolean(String name) {
- boolean result = false;
- try {
- result = Boolean.parseBoolean(getProperty(name));
- } catch (IllegalArgumentException | NullPointerException e) {
- }
- return result;
- }
-
- /** Replacement for {@link Integer#getInteger(String, Integer)}. */
- public static Integer getInteger(String nm, Integer val) {
- String v = null;
- try {
- v = getProperty(nm);
- } catch (IllegalArgumentException | NullPointerException e) {
- }
- if (v != null) {
- try {
- return Integer.decode(v);
- } catch (NumberFormatException e) {
- }
- }
- return val;
- }
-
- /** Replacement for {@link Integer#getInteger(String, int)}. */
- public static Integer getInteger(String nm, int val) {
- Integer result = getInteger(nm, null);
- return (result == null) ? Integer.valueOf(val) : result;
- }
-
- /** Replacement for {@link Integer#getInteger(String)}. */
- public static Integer getInteger(String nm) {
- return getInteger(nm, null);
- }
-
- /** Returns the property as string. */
- private String get(Map map, String property, String defaultValue) {
- Object ret = find(map, property);
- if (ret == null) {
- return defaultValue;
- } else {
- return ret.toString();
- }
- }
-
- /** Returns the property as enum. */
- private > T get(Map map, String property, T defaultValue) {
- Object ret = map.get(property);
- if (ret == null) {
- return defaultValue;
- } else {
- Class enumType = (Class) defaultValue.getClass();
- return Enum.valueOf(enumType, ret.toString().toUpperCase(Locale.ROOT));
- }
- }
-
- /** Returns the property as boolean. */
- private boolean get(Map map, String property, boolean defaultValue) {
- Object ret = map.get(property);
- if (ret == null) {
- return defaultValue;
- } else if (ret instanceof Boolean) {
- return (Boolean) ret;
- } else {
- return "1".equals(ret) || "true".equals(ret);
- }
- }
-
- /**
- * Tries to find the property in the map. It tries the property first, then it tries the camel
- * upper version.
- */
- private Object find(Map map, String property) {
- Object ret = map.get(property);
- if (ret != null) {
- return ret;
- }
- return map.get(toCamelUpper(property));
- }
-
- private String toCamelUpper(String property) {
- String s = property.replace('.', '_');
- StringBuilder sb = new StringBuilder(s.length() + 5);
- for (int i = 0; i < s.length(); ++i) {
- char c = s.charAt(i);
- if (i > 0 && Character.isUpperCase(c) && Character.isLowerCase(s.charAt(i - 1))) {
- sb.append('_');
- }
- sb.append(Character.toUpperCase(c));
- }
- return sb.toString();
- }
-
- private void load(Map map, String location) {
- stackStyle = get(map, "rhino.stack.style", stackStyle);
- useJavaPolicySecurity = get(map, "rhino.use_java_policy_security", useJavaPolicySecurity);
- printTrees = get(map, "rhino.printTrees", printTrees);
- printICodes = get(map, "rhino.printICodes", printICodes);
- debugStack = get(map, "rhino.debugStack", debugStack);
- debugLinker = get(map, "rhino.debugLinker", debugStack);
- }
-
- /** "rhino.stack.style" */
- private StackStyle stackStyle = StackStyle.RHINO;
-
- private boolean useJavaPolicySecurity;
- private boolean printTrees = false;
- private boolean printICodes = false;
- private boolean debugStack = false;
- private boolean debugLinker = false;
-
- public StackStyle stackStyle() {
- return stackStyle;
- }
-
- public boolean useJavaPolicySecurity() {
- return useJavaPolicySecurity;
- }
-
- public boolean printTrees() {
- return printTrees;
- }
-
- public boolean printICodes() {
- return printICodes;
- }
-
- public boolean debugStack() {
- return debugStack;
- }
-
- public boolean debugLinker() {
- return debugLinker;
- }
-}
diff --git a/rhino/src/main/java/org/mozilla/javascript/RhinoException.java b/rhino/src/main/java/org/mozilla/javascript/RhinoException.java
index 5ceb959cc7..a88e800c71 100644
--- a/rhino/src/main/java/org/mozilla/javascript/RhinoException.java
+++ b/rhino/src/main/java/org/mozilla/javascript/RhinoException.java
@@ -14,6 +14,7 @@
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import org.mozilla.javascript.config.RhinoConfig;
/** The class of exceptions thrown by the JavaScript engine. */
public abstract class RhinoException extends RuntimeException {
@@ -374,7 +375,7 @@ public static StackStyle getStackStyle() {
private static final long serialVersionUID = 1883500631321581169L;
// Just for testing!
- private static StackStyle stackStyle = RhinoConfig.DEFAULT.stackStyle();
+ private static StackStyle stackStyle = RhinoConfig.get("rhino.stack.style", StackStyle.RHINO);
private String sourceName;
private int lineNumber;
diff --git a/rhino/src/main/java/org/mozilla/javascript/Token.java b/rhino/src/main/java/org/mozilla/javascript/Token.java
index 4db392687c..128ccca048 100644
--- a/rhino/src/main/java/org/mozilla/javascript/Token.java
+++ b/rhino/src/main/java/org/mozilla/javascript/Token.java
@@ -6,6 +6,8 @@
package org.mozilla.javascript;
+import org.mozilla.javascript.config.RhinoConfig;
+
/**
* This class implements the JavaScript scanner.
*
@@ -24,8 +26,8 @@ public static enum CommentType {
}
// debug flags
- public static final boolean printTrees = RhinoConfig.DEFAULT.printTrees();
- static final boolean printICode = RhinoConfig.DEFAULT.printICodes();
+ public static final boolean printTrees = RhinoConfig.get("rhino.printTrees", false);
+ static final boolean printICode = RhinoConfig.get("rhino.printICode", false);
static final boolean printNames = printTrees || printICode;
/** Token types. These values correspond to JSTokenType values in jsscan.c. */
diff --git a/rhino/src/main/java/org/mozilla/javascript/config/RhinoConfig.java b/rhino/src/main/java/org/mozilla/javascript/config/RhinoConfig.java
new file mode 100644
index 0000000000..aaec0f34f5
--- /dev/null
+++ b/rhino/src/main/java/org/mozilla/javascript/config/RhinoConfig.java
@@ -0,0 +1,60 @@
+package org.mozilla.javascript.config;
+
+import java.util.Locale;
+
+/**
+ * With RhinoConfig, you can access the {@link RhinoProperties} in a typesafe way.
+ *
+ * @author Roland Praml, Foconis Analytics GmbH
+ */
+public class RhinoConfig {
+
+ /** Returns the property as string. */
+ private static String get(String property, String defaultValue) {
+ Object ret = RhinoProperties.get(property);
+ if (ret != null) {
+ return ret.toString();
+ }
+ return defaultValue;
+ }
+
+ /** Returns the property as string with null as default. */
+ public static String get(String property) {
+ return get(property, (String) null);
+ }
+
+ /** Returns the property as enum. Note: default value must be specified */
+ public static > T get(String property, T defaultValue) {
+ Object ret = RhinoProperties.get(property);
+ if (ret != null) {
+ Class enumType = (Class) defaultValue.getClass();
+ // We assume, that enums all are in UPPERCASES
+ return Enum.valueOf(enumType, ret.toString().toUpperCase(Locale.ROOT));
+ }
+ return defaultValue;
+ }
+
+ /** Returns the property as boolean. */
+ public static boolean get(String property, boolean defaultValue) {
+ Object ret = RhinoProperties.get(property);
+ if (ret instanceof Boolean) {
+ return (Boolean) ret;
+ } else {
+ return "1".equals(ret) || "true".equals(ret);
+ }
+ }
+
+ /** Returns the property as integer. */
+ public static int get(String property, int defaultValue) {
+ Object ret = RhinoProperties.get(property);
+ if (ret instanceof Number) {
+ return ((Number) ret).intValue();
+ } else {
+ try {
+ return Integer.decode(ret.toString());
+ } catch (NumberFormatException e) {
+ }
+ }
+ return defaultValue;
+ }
+}
diff --git a/rhino/src/main/java/org/mozilla/javascript/config/RhinoProperties.java b/rhino/src/main/java/org/mozilla/javascript/config/RhinoProperties.java
new file mode 100644
index 0000000000..e872de7ad4
--- /dev/null
+++ b/rhino/src/main/java/org/mozilla/javascript/config/RhinoProperties.java
@@ -0,0 +1,163 @@
+package org.mozilla.javascript.config;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ServiceLoader;
+
+/**
+ * Utility class to read current rhino configuration from various properties.
+ *
+ * Rhino properties typically begins with "rhino." (properties) or "RHINO_" (env)
+ *
+ *
You can override this behaviour and implement a {@link RhinoPropertiesLoader} and register it
+ * as service. If no loader was found, the configuration is read from these locations by default:
+ *
+ *
+ * - rhino.config file from current class' classpath
+ *
- rhino.config file from current threas's classpath
+ *
- rhino.config file from current directory
+ *
- rhino-test.config file from current class' classpath
+ *
- rhino-test.config file from current threas's classpath
+ *
- rhino-test.config file from current directory
+ *
- env variables starting with "RHINO_" (underscores are replaced by '.' and string is
+ *
- System-properties starting with "rhino."
+ *
+ *
+ * (the later config can override previous ones)
+ *
+ *
The config files are in UTF-8 format and all keys in this configuration are case-insensitive
+ * and dot/underscore-insensitive.
+ *
+ *
+ *
+ *
This means, "rhino.use_java_policy_security" is equvalent to "RHINO_USE_JAVA_POLICY_SECURITY"
+ *
+ *
This class contains only the properties. Every plugin etc. can parse its config from there.
+ *
+ * @author Roland Praml, Foconis Analytics GmbH
+ */
+public class RhinoProperties {
+
+ private static String[] CONFIG_FILES = {"rhino.config", "rhino-test.config"};
+ private static final RhinoProperties INSTANCE =
+ AccessController.doPrivileged((PrivilegedAction) () -> init());
+
+ static RhinoProperties init() {
+ RhinoProperties props = new RhinoProperties();
+ Iterator loader =
+ ServiceLoader.load(RhinoPropertiesLoader.class).iterator();
+ if (loader.hasNext()) {
+ while (loader.hasNext()) {
+ loader.next().load(props);
+ }
+ } else {
+ props.loadDefaults();
+ }
+ return props;
+ }
+
+ /** Load properties from the default locations. */
+ public void loadDefaults() {
+ ClassLoader classLoader = RhinoProperties.class.getClassLoader();
+ ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+
+ for (String configFile : CONFIG_FILES) {
+ loadFromClasspath(classLoader, configFile);
+ loadFromClasspath(contextClassLoader, configFile);
+ loadFromFile(new File(configFile));
+ }
+
+ addConfig(System.getenv());
+ addConfig(System.getProperties());
+ }
+
+ private List