From 9558aca82d3f4f14e050e0fc1ce97dcf9a9a7e81 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Fri, 8 Nov 2024 10:09:31 +0100 Subject: [PATCH 1/3] Draft for reading configuration from config files --- .../mozilla/javascript/tools/shell/Main.java | 3 +- .../org/mozilla/javascript/RhinoConfig.java | 167 ++++++++++++++++++ .../mozilla/javascript/RhinoException.java | 26 ++- tests/rhino.config | 1 + tests/src/test/resources/rhino.config | 1 + 5 files changed, 181 insertions(+), 17 deletions(-) create mode 100644 rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java create mode 100644 tests/rhino.config create mode 100644 tests/src/test/resources/rhino.config 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 1393d17ef6..70a9aab04a 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,6 +32,7 @@ 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; @@ -132,7 +133,7 @@ public void quit(Context cx, int exitCode) { */ public static void main(String args[]) { try { - if (Boolean.getBoolean("rhino.use_java_policy_security")) { + if (RhinoConfig.getBoolean("rhino.use_java_policy_security")) { initJavaPolicySecuritySupport(); } } catch (SecurityException ex) { diff --git a/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java b/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java new file mode 100644 index 0000000000..8d170325b7 --- /dev/null +++ b/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java @@ -0,0 +1,167 @@ +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.nio.charset.StandardCharsets; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Collections; +import java.util.Comparator; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.TreeMap; + +/** + * Utility class to read current rhino configuration. + * + *

Rhino properties typically begins with "rhino." (properties) or "RHINO_" (env) + * + *

The configuration is read from these locations: + * + *

    + *
  1. rhino.config file from current class' classpath + *
  2. rhino.config file from current threas's classpath + *
  3. rhino.config file from current directory + *
  4. System-properties starting with "rhino." + *
  5. 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 { + private static final Map PROPERTIES = + AccessController.doPrivileged((PrivilegedAction>) () -> init()); + + private static Map init() { + // we have to add a comparator at least for environment: TODO: can this done simpler + Comparator comparator = + (s1, s2) -> { + s1 = s1.toLowerCase(Locale.ROOT).replace('_', '.'); + s2 = s2.toLowerCase(Locale.ROOT).replace('_', '.'); + return s1.compareTo(s2); + }; + Map map = new TreeMap<>(comparator); + + // load from classpaths + map.putAll(loadFromClasspath(RhinoConfig.class.getClassLoader())); + map.putAll(loadFromClasspath(Thread.currentThread().getContextClassLoader())); + map.putAll(loadFromFile(new File("rhino.config"))); + copyMap(System.getProperties(), map); + copyMap(System.getenv(), map); + System.out.println("Current config: " + map); + return map; + } + + /** Copies all rhino relevant properties. */ + private static void copyMap(Map src, Map dst) { + for (Map.Entry entry : src.entrySet()) { + if (entry.getKey() instanceof String && entry.getValue() instanceof String) { + String key = (String) entry.getKey(); + if (key.startsWith("rhino.") || key.startsWith("RHINO_")) { + dst.put(key, (String) entry.getValue()); + } + } + } + } + + @SuppressWarnings("unchecked") + private static Map loadFromFile(File config) { + if (config.exists()) { + try (InputStream in = new FileInputStream(config)) { + if (in != null) { + Properties props = new Properties(); + props.load(new InputStreamReader(in, StandardCharsets.UTF_8)); + System.out.println( + "Loaded rhino.config from " + + config.getAbsolutePath()); // TODO: remove these prints + return (Map) props; + } + } catch (IOException e) { + System.err.println( + "Error loading rhino.config from " + + config.getAbsolutePath() + + ": " + + e.getMessage()); + } + } + return Collections.emptyMap(); + } + + @SuppressWarnings("unchecked") + private static Map loadFromClasspath(ClassLoader cl) { + if (cl != null) { + try (InputStream in = cl.getResourceAsStream("rhino.config")) { + if (in != null) { + Properties props = new Properties(); + props.load(new InputStreamReader(in, StandardCharsets.UTF_8)); + System.out.println( + "Loaded " + + props.size() + + " proerties from rhino.config in classpath"); // TODO: remove + // these prints + return (Map) props; + } + } catch (IOException e) { + System.err.println("Error loading rhino.config from classpath: " + e.getMessage()); + } + } + return Collections.emptyMap(); + } + + /** Replacement for {@link System#getProperty(String)}. */ + public static String getProperty(String key) { + return PROPERTIES.get(key); + } + + /** 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); + } +} diff --git a/rhino/src/main/java/org/mozilla/javascript/RhinoException.java b/rhino/src/main/java/org/mozilla/javascript/RhinoException.java index 8bccbf6c2e..8b0f2d6175 100644 --- a/rhino/src/main/java/org/mozilla/javascript/RhinoException.java +++ b/rhino/src/main/java/org/mozilla/javascript/RhinoException.java @@ -10,7 +10,6 @@ import java.io.FilenameFilter; import java.io.PrintStream; import java.io.PrintWriter; -import java.security.AccessControlException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; @@ -221,11 +220,11 @@ static String formatStackTrace(ScriptStackElement[] stack, String message) { /** * Get a string representing the script stack of this exception. * - * @deprecated the filter argument is ignored as we are able to recognize script stack elements - * by our own. Use #getScriptStackTrace() instead. * @param filter ignored * @return a script stack dump * @since 1.6R6 + * @deprecated the filter argument is ignored as we are able to recognize script stack elements + * by our own. Use #getScriptStackTrace() instead. */ @Deprecated public String getScriptStackTrace(FilenameFilter filter) { @@ -387,20 +386,15 @@ public static StackStyle getStackStyle() { // Allow us to override default stack style for debugging. static { - try { - String style = System.getProperty("rhino.stack.style"); - if (style != null) { - if ("Rhino".equalsIgnoreCase(style)) { - stackStyle = StackStyle.RHINO; - } else if ("Mozilla".equalsIgnoreCase(style)) { - stackStyle = StackStyle.MOZILLA; - } else if ("V8".equalsIgnoreCase(style)) { - stackStyle = StackStyle.V8; - } + String style = RhinoConfig.getProperty("rhino.stack.style"); + if (style != null) { + if ("Rhino".equalsIgnoreCase(style)) { + stackStyle = StackStyle.RHINO; + } else if ("Mozilla".equalsIgnoreCase(style)) { + stackStyle = StackStyle.MOZILLA; + } else if ("V8".equalsIgnoreCase(style)) { + stackStyle = StackStyle.V8; } - } catch (AccessControlException ace) { - // ignore. We will land here, if SecurityManager is in place and error is lazily - // initialized } } } diff --git a/tests/rhino.config b/tests/rhino.config new file mode 100644 index 0000000000..6bdef9fb3f --- /dev/null +++ b/tests/rhino.config @@ -0,0 +1 @@ +rhino.helloFromFile=true diff --git a/tests/src/test/resources/rhino.config b/tests/src/test/resources/rhino.config new file mode 100644 index 0000000000..e8c42e5aa6 --- /dev/null +++ b/tests/src/test/resources/rhino.config @@ -0,0 +1 @@ +rhino.helloFromClasspath=true \ No newline at end of file From 659ebc5ea932c0d65820e79184fa58d052218b7f Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Fri, 13 Dec 2024 11:48:44 +0100 Subject: [PATCH 2/3] parse all inputs on initialization --- .../mozilla/javascript/tools/shell/Main.java | 2 +- .../org/mozilla/javascript/RhinoConfig.java | 226 ++++++++++++------ .../mozilla/javascript/RhinoException.java | 16 +- .../java/org/mozilla/javascript/Token.java | 4 +- .../javascript/optimizer/DefaultLinker.java | 11 +- 5 files changed, 154 insertions(+), 105 deletions(-) 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 70a9aab04a..adae6971ce 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 @@ -133,7 +133,7 @@ public void quit(Context cx, int exitCode) { */ public static void main(String args[]) { try { - if (RhinoConfig.getBoolean("rhino.use_java_policy_security")) { + if (RhinoConfig.DEFAULT.useJavaPolicySecurity()) { initJavaPolicySecuritySupport(); } } catch (SecurityException ex) { diff --git a/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java b/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java index 8d170325b7..b1cf7d95f7 100644 --- a/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java +++ b/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java @@ -5,15 +5,13 @@ 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.Collections; -import java.util.Comparator; import java.util.Locale; import java.util.Map; import java.util.Properties; -import java.util.TreeMap; /** * Utility class to read current rhino configuration. @@ -30,96 +28,68 @@ *

  • 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. + *

    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 { - private static final Map PROPERTIES = - AccessController.doPrivileged((PrivilegedAction>) () -> init()); - - private static Map init() { - // we have to add a comparator at least for environment: TODO: can this done simpler - Comparator comparator = - (s1, s2) -> { - s1 = s1.toLowerCase(Locale.ROOT).replace('_', '.'); - s2 = s2.toLowerCase(Locale.ROOT).replace('_', '.'); - return s1.compareTo(s2); - }; - Map map = new TreeMap<>(comparator); - - // load from classpaths - map.putAll(loadFromClasspath(RhinoConfig.class.getClassLoader())); - map.putAll(loadFromClasspath(Thread.currentThread().getContextClassLoader())); - map.putAll(loadFromFile(new File("rhino.config"))); - copyMap(System.getProperties(), map); - copyMap(System.getenv(), map); - System.out.println("Current config: " + map); - return map; - } - - /** Copies all rhino relevant properties. */ - private static void copyMap(Map src, Map dst) { - for (Map.Entry entry : src.entrySet()) { - if (entry.getKey() instanceof String && entry.getValue() instanceof String) { - String key = (String) entry.getKey(); - if (key.startsWith("rhino.") || key.startsWith("RHINO_")) { - dst.put(key, (String) entry.getValue()); - } - } + + 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 static Map loadFromFile(File config) { - if (config.exists()) { - try (InputStream in = new FileInputStream(config)) { - if (in != null) { - Properties props = new Properties(); - props.load(new InputStreamReader(in, StandardCharsets.UTF_8)); - System.out.println( - "Loaded rhino.config from " - + config.getAbsolutePath()); // TODO: remove these prints - return (Map) props; - } - } 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; } - return Collections.emptyMap(); - } - - @SuppressWarnings("unchecked") - private static Map loadFromClasspath(ClassLoader cl) { - if (cl != null) { - try (InputStream in = cl.getResourceAsStream("rhino.config")) { - if (in != null) { - Properties props = new Properties(); - props.load(new InputStreamReader(in, StandardCharsets.UTF_8)); - System.out.println( - "Loaded " - + props.size() - + " proerties from rhino.config in classpath"); // TODO: remove - // these prints - return (Map) props; - } - } catch (IOException e) { - System.err.println("Error loading rhino.config from classpath: " + e.getMessage()); - } + 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()); } - return Collections.emptyMap(); } /** Replacement for {@link System#getProperty(String)}. */ public static String getProperty(String key) { - return PROPERTIES.get(key); + return null; } /** Replacement for {@link System#getProperty(String, String)}. */ @@ -164,4 +134,104 @@ public static Integer getInteger(String nm, int val) { 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; + + 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 8b0f2d6175..5ceb959cc7 100644 --- a/rhino/src/main/java/org/mozilla/javascript/RhinoException.java +++ b/rhino/src/main/java/org/mozilla/javascript/RhinoException.java @@ -374,7 +374,7 @@ public static StackStyle getStackStyle() { private static final long serialVersionUID = 1883500631321581169L; // Just for testing! - private static StackStyle stackStyle = StackStyle.RHINO; + private static StackStyle stackStyle = RhinoConfig.DEFAULT.stackStyle(); private String sourceName; private int lineNumber; @@ -383,18 +383,4 @@ public static StackStyle getStackStyle() { Object interpreterStackInfo; int[] interpreterLineData; - - // Allow us to override default stack style for debugging. - static { - String style = RhinoConfig.getProperty("rhino.stack.style"); - if (style != null) { - if ("Rhino".equalsIgnoreCase(style)) { - stackStyle = StackStyle.RHINO; - } else if ("Mozilla".equalsIgnoreCase(style)) { - stackStyle = StackStyle.MOZILLA; - } else if ("V8".equalsIgnoreCase(style)) { - stackStyle = StackStyle.V8; - } - } - } } diff --git a/rhino/src/main/java/org/mozilla/javascript/Token.java b/rhino/src/main/java/org/mozilla/javascript/Token.java index 2fac729e63..4db392687c 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Token.java +++ b/rhino/src/main/java/org/mozilla/javascript/Token.java @@ -24,8 +24,8 @@ public static enum CommentType { } // debug flags - public static final boolean printTrees = false; - static final boolean printICode = false; + public static final boolean printTrees = RhinoConfig.DEFAULT.printTrees(); + static final boolean printICode = RhinoConfig.DEFAULT.printICodes(); 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/optimizer/DefaultLinker.java b/rhino/src/main/java/org/mozilla/javascript/optimizer/DefaultLinker.java index 57e0697a98..1b11403bb6 100644 --- a/rhino/src/main/java/org/mozilla/javascript/optimizer/DefaultLinker.java +++ b/rhino/src/main/java/org/mozilla/javascript/optimizer/DefaultLinker.java @@ -11,6 +11,7 @@ import jdk.dynalink.linker.LinkerServices; import org.mozilla.javascript.Callable; import org.mozilla.javascript.Context; +import org.mozilla.javascript.RhinoConfig; import org.mozilla.javascript.ScriptRuntime; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Token; @@ -22,15 +23,7 @@ */ @SuppressWarnings("AndroidJdkLibsChecker") class DefaultLinker implements GuardingDynamicLinker { - static final boolean DEBUG; - - static { - String debugVal = System.getProperty("RHINO_DEBUG_LINKER"); - if (debugVal == null) { - debugVal = System.getenv("RHINO_DEBUG_LINKER"); - } - DEBUG = Boolean.parseBoolean(debugVal); - } + static final boolean DEBUG = RhinoConfig.DEFAULT.debugLinker(); @Override public GuardedInvocation getGuardedInvocation(LinkRequest req, LinkerServices svc) From 2d3e39db44bc987e575c226b4f04bcf92ac10d55 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Fri, 13 Dec 2024 12:05:26 +0100 Subject: [PATCH 3/3] fix NPE in StackStyle --- rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java b/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java index b1cf7d95f7..7d26780703 100644 --- a/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java +++ b/rhino/src/main/java/org/mozilla/javascript/RhinoConfig.java @@ -203,7 +203,7 @@ private void load(Map map, String location) { } /** "rhino.stack.style" */ - private StackStyle stackStyle; + private StackStyle stackStyle = StackStyle.RHINO; private boolean useJavaPolicySecurity; private boolean printTrees = false;