From e8cb13fda96d566458914d10cff98f8c2d68129e Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 14 Oct 2024 12:07:10 +0200 Subject: [PATCH 1/4] Introduced a plugin-API and show usage with rhino-xml --- rhino-xml/src/main/java/module-info.java | 3 + .../mozilla/javascript/xmlimpl/XmlPlugin.java | 25 ++++++++ .../services/org.mozilla.javascript.Plugin | 1 + rhino/src/main/java/module-info.java | 2 + .../mozilla/javascript/CompilerEnvirons.java | 1 + .../java/org/mozilla/javascript/Context.java | 14 ----- .../mozilla/javascript/ContextFactory.java | 63 ++++++++----------- .../mozilla/javascript/LazilyLoadedCtor.java | 2 +- .../java/org/mozilla/javascript/Plugin.java | 11 ++++ .../org/mozilla/javascript/ScriptRuntime.java | 11 +--- .../org/mozilla/javascript/xml/XMLLib.java | 22 ------- 11 files changed, 73 insertions(+), 82 deletions(-) create mode 100644 rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java create mode 100644 rhino-xml/src/main/resources/META-INF/services/org.mozilla.javascript.Plugin create mode 100644 rhino/src/main/java/org/mozilla/javascript/Plugin.java diff --git a/rhino-xml/src/main/java/module-info.java b/rhino-xml/src/main/java/module-info.java index f88109a202..297371af28 100644 --- a/rhino-xml/src/main/java/module-info.java +++ b/rhino-xml/src/main/java/module-info.java @@ -3,4 +3,7 @@ requires transitive org.mozilla.rhino; requires transitive java.xml; + + provides org.mozilla.javascript.Plugin with + org.mozilla.javascript.xmlimpl.XmlPlugin; } diff --git a/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java b/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java new file mode 100644 index 0000000000..4e2742ce5b --- /dev/null +++ b/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java @@ -0,0 +1,25 @@ +package org.mozilla.javascript.xmlimpl; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.LazilyLoadedCtor; +import org.mozilla.javascript.Plugin; +import org.mozilla.javascript.ScriptableObject; + +/** + * Registers the XML objects in the scope. + * + * @author Roland Praml, Foconis Analytics GmbH + */ +public class XmlPlugin implements Plugin { + + @Override + public void initSafeStandardObjects(Context cx, ScriptableObject scope, boolean sealed) { + if (cx.hasFeature(Context.FEATURE_E4X)) { + String xmlImpl = XMLLibImpl.class.getName(); + new LazilyLoadedCtor(scope, "XML", xmlImpl, sealed, true); + new LazilyLoadedCtor(scope, "XMLList", xmlImpl, sealed, true); + new LazilyLoadedCtor(scope, "Namespace", xmlImpl, sealed, true); + new LazilyLoadedCtor(scope, "QName", xmlImpl, sealed, true); + } + } +} diff --git a/rhino-xml/src/main/resources/META-INF/services/org.mozilla.javascript.Plugin b/rhino-xml/src/main/resources/META-INF/services/org.mozilla.javascript.Plugin new file mode 100644 index 0000000000..14876704cd --- /dev/null +++ b/rhino-xml/src/main/resources/META-INF/services/org.mozilla.javascript.Plugin @@ -0,0 +1 @@ +org.mozilla.javascript.xmlimpl.XmlPlugin diff --git a/rhino/src/main/java/module-info.java b/rhino/src/main/java/module-info.java index 90cad354c6..55dee1d258 100644 --- a/rhino/src/main/java/module-info.java +++ b/rhino/src/main/java/module-info.java @@ -14,4 +14,6 @@ requires java.compiler; requires jdk.dynalink; requires transitive java.desktop; + + uses org.mozilla.javascript.Plugin; } diff --git a/rhino/src/main/java/org/mozilla/javascript/CompilerEnvirons.java b/rhino/src/main/java/org/mozilla/javascript/CompilerEnvirons.java index 491eb647e5..c23e40aea6 100644 --- a/rhino/src/main/java/org/mozilla/javascript/CompilerEnvirons.java +++ b/rhino/src/main/java/org/mozilla/javascript/CompilerEnvirons.java @@ -42,6 +42,7 @@ public void initFromContext(Context cx) { // Observer code generation in compiled code : generateObserverCount = cx.isGenerateObserverCount(); + } public final ErrorReporter getErrorReporter() { diff --git a/rhino/src/main/java/org/mozilla/javascript/Context.java b/rhino/src/main/java/org/mozilla/javascript/Context.java index ca9e460a92..5fe59d9b82 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Context.java +++ b/rhino/src/main/java/org/mozilla/javascript/Context.java @@ -2226,20 +2226,6 @@ public boolean hasFeature(int featureIndex) { return f.hasFeature(this, featureIndex); } - /** - * Returns an object which specifies an E4X implementation to use within this Context - * . Note that the XMLLib.Factory interface should be considered experimental. - * - *

The default implementation uses the implementation provided by this Context's - * {@link ContextFactory}. - * - * @return An XMLLib.Factory. Should not return null if {@link #FEATURE_E4X} is - * enabled. See {@link #hasFeature}. - */ - public XMLLib.Factory getE4xImplementationFactory() { - return getFactory().getE4xImplementationFactory(); - } - /** * Get threshold of executed instructions counter that triggers call to * observeInstructionCount(). When the threshold is zero, instruction counting is diff --git a/rhino/src/main/java/org/mozilla/javascript/ContextFactory.java b/rhino/src/main/java/org/mozilla/javascript/ContextFactory.java index 8f5939f47e..3aa23e5276 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ContextFactory.java +++ b/rhino/src/main/java/org/mozilla/javascript/ContextFactory.java @@ -10,6 +10,10 @@ import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ServiceLoader; /** * Factory class that Rhino runtime uses to create new {@link Context} instances. A @@ -114,6 +118,13 @@ public class ContextFactory { private volatile Object listeners; private boolean disabledListening; private ClassLoader applicationClassLoader; + private List plugins = getPluginsFromServiceLoader(); + + private List getPluginsFromServiceLoader() { + List result = new ArrayList(); + ServiceLoader.load(Plugin.class).forEach(result::add); + return Collections.unmodifiableList(result); + } /** Listener of {@link Context} creation and release events. */ public interface Listener { @@ -295,42 +306,6 @@ protected boolean hasFeature(Context cx, int featureIndex) { throw new IllegalArgumentException(String.valueOf(featureIndex)); } - private static boolean isDom3Present() { - Class nodeClass = Kit.classOrNull("org.w3c.dom.Node"); - if (nodeClass == null) return false; - // Check to see whether DOM3 is present; use a new method defined in - // DOM3 that is vital to our implementation - try { - nodeClass.getMethod("getUserData", String.class); - return true; - } catch (NoSuchMethodException e) { - return false; - } - } - - /** - * Provides a default {@link org.mozilla.javascript.xml.XMLLib.Factory XMLLib.Factory} to be - * used by the Context instances produced by this factory. See {@link - * Context#getE4xImplementationFactory} for details. - * - *

May return null, in which case E4X functionality is not supported in Rhino. - * - *

The default implementation now prefers the DOM3 E4X implementation. - */ - protected org.mozilla.javascript.xml.XMLLib.Factory getE4xImplementationFactory() { - // Must provide default implementation, rather than abstract method, - // so that past implementors of ContextFactory do not fail at runtime - // upon invocation of this method. - // Note that the default implementation returns null if we - // neither have XMLBeans nor a DOM3 implementation present. - - if (isDom3Present()) { - return org.mozilla.javascript.xml.XMLLib.Factory.create( - "org.mozilla.javascript.xmlimpl.XMLLibImpl"); - } - return null; - } - /** * Create class loader for generated classes. This method creates an instance of the default * implementation of {@link GeneratedClassLoader}. Rhino uses this interface to load generated @@ -552,4 +527,20 @@ public final void exit() { public final Context enterContext(Context cx) { return Context.enter(cx, this); } + + /** Returns a list of plugins. */ + public List getPlugins() { + return plugins; + } + + /** + * Sets a list of plugins, that are used by this factory. Note: by default, the plugins are + * loaded by ServiceLoader. + */ + public void setPlugins(List plugins) { + checkNotSealed(); + synchronized (plugins) { + this.plugins = Collections.unmodifiableList(plugins); + } + } } diff --git a/rhino/src/main/java/org/mozilla/javascript/LazilyLoadedCtor.java b/rhino/src/main/java/org/mozilla/javascript/LazilyLoadedCtor.java index b069bcfafc..602e953b32 100644 --- a/rhino/src/main/java/org/mozilla/javascript/LazilyLoadedCtor.java +++ b/rhino/src/main/java/org/mozilla/javascript/LazilyLoadedCtor.java @@ -35,7 +35,7 @@ public LazilyLoadedCtor( this(scope, propertyName, className, sealed, false); } - LazilyLoadedCtor( + public LazilyLoadedCtor( ScriptableObject scope, String propertyName, String className, diff --git a/rhino/src/main/java/org/mozilla/javascript/Plugin.java b/rhino/src/main/java/org/mozilla/javascript/Plugin.java new file mode 100644 index 0000000000..d12f163a7b --- /dev/null +++ b/rhino/src/main/java/org/mozilla/javascript/Plugin.java @@ -0,0 +1,11 @@ +package org.mozilla.javascript; + +/** + * Plugins may be loaded with the serviceLocator. + * + * @author Roland Praml, Foconis Analytics GmbH + */ +public interface Plugin { + + default void initSafeStandardObjects(Context cx, ScriptableObject scope, boolean sealed) {} +} diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index 0b5389d7f5..ef25c4cb39 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -192,21 +192,14 @@ public static ScriptableObject initSafeStandardObjects( NativeJavaObject.init(scope, sealed); NativeJavaMap.init(scope, sealed); - boolean withXml = - cx.hasFeature(Context.FEATURE_E4X) && cx.getE4xImplementationFactory() != null; - // define lazy-loaded properties using their class name new LazilyLoadedCtor( scope, "RegExp", "org.mozilla.javascript.regexp.NativeRegExp", sealed, true); new LazilyLoadedCtor( scope, "Continuation", "org.mozilla.javascript.NativeContinuation", sealed, true); - if (withXml) { - String xmlImpl = cx.getE4xImplementationFactory().getImplementationClassName(); - new LazilyLoadedCtor(scope, "XML", xmlImpl, sealed, true); - new LazilyLoadedCtor(scope, "XMLList", xmlImpl, sealed, true); - new LazilyLoadedCtor(scope, "Namespace", xmlImpl, sealed, true); - new LazilyLoadedCtor(scope, "QName", xmlImpl, sealed, true); + for (Plugin plugin : cx.getFactory().getPlugins()) { + plugin.initSafeStandardObjects(cx, scope, sealed); } if (((cx.getLanguageVersion() >= Context.VERSION_1_8) diff --git a/rhino/src/main/java/org/mozilla/javascript/xml/XMLLib.java b/rhino/src/main/java/org/mozilla/javascript/xml/XMLLib.java index ff1116c6c4..31c07c4537 100644 --- a/rhino/src/main/java/org/mozilla/javascript/xml/XMLLib.java +++ b/rhino/src/main/java/org/mozilla/javascript/xml/XMLLib.java @@ -15,28 +15,6 @@ public abstract class XMLLib { private static final Object XML_LIB_KEY = new Object(); - /** - * An object which specifies an XMLLib implementation to be used at runtime. - * - *

This interface should be considered experimental. It may be better (and certainly more - * flexible) to write an interface that returns an XMLLib object rather than a class name, for - * example. But that would cause many more ripple effects in the code, all the way back to - * {@link ScriptRuntime}. - */ - public abstract static class Factory { - - public static Factory create(final String className) { - return new Factory() { - @Override - public String getImplementationClassName() { - return className; - } - }; - } - - public abstract String getImplementationClassName(); - } - public static XMLLib extractFromScopeOrNull(Scriptable scope) { ScriptableObject so = ScriptRuntime.getLibraryScopeOrNull(scope); if (so == null) { From 1dd463bb666b5568461d05581ae2efc91f831fea Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 14 Oct 2024 12:19:47 +0200 Subject: [PATCH 2/4] Add some documentation and moved FEATURE_E4X dependency to XmlPlugin --- .../mozilla/javascript/xmlimpl/XmlPlugin.java | 28 ++++++++++++------- .../mozilla/javascript/CompilerEnvirons.java | 3 +- .../java/org/mozilla/javascript/Context.java | 3 +- .../java/org/mozilla/javascript/Plugin.java | 7 +++++ .../org/mozilla/javascript/ScriptRuntime.java | 3 ++ 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java b/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java index 4e2742ce5b..897d9d4353 100644 --- a/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java +++ b/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java @@ -1,5 +1,6 @@ package org.mozilla.javascript.xmlimpl; +import org.mozilla.javascript.CompilerEnvirons; import org.mozilla.javascript.Context; import org.mozilla.javascript.LazilyLoadedCtor; import org.mozilla.javascript.Plugin; @@ -12,14 +13,21 @@ */ public class XmlPlugin implements Plugin { - @Override - public void initSafeStandardObjects(Context cx, ScriptableObject scope, boolean sealed) { - if (cx.hasFeature(Context.FEATURE_E4X)) { - String xmlImpl = XMLLibImpl.class.getName(); - new LazilyLoadedCtor(scope, "XML", xmlImpl, sealed, true); - new LazilyLoadedCtor(scope, "XMLList", xmlImpl, sealed, true); - new LazilyLoadedCtor(scope, "Namespace", xmlImpl, sealed, true); - new LazilyLoadedCtor(scope, "QName", xmlImpl, sealed, true); - } - } + @Override + public void initSafeStandardObjects(Context cx, ScriptableObject scope, boolean sealed) { + if (cx.hasFeature(Context.FEATURE_E4X)) { + String xmlImpl = XMLLibImpl.class.getName(); + new LazilyLoadedCtor(scope, "XML", xmlImpl, sealed, true); + new LazilyLoadedCtor(scope, "XMLList", xmlImpl, sealed, true); + new LazilyLoadedCtor(scope, "Namespace", xmlImpl, sealed, true); + new LazilyLoadedCtor(scope, "QName", xmlImpl, sealed, true); + } + } + + @Override + public void initCompilerEnvirons(Context cx, CompilerEnvirons compilerEnvirons) { + if (cx.hasFeature(Context.FEATURE_E4X)) { + compilerEnvirons.setXmlAvailable(true); + } + } } diff --git a/rhino/src/main/java/org/mozilla/javascript/CompilerEnvirons.java b/rhino/src/main/java/org/mozilla/javascript/CompilerEnvirons.java index c23e40aea6..d87b609de5 100644 --- a/rhino/src/main/java/org/mozilla/javascript/CompilerEnvirons.java +++ b/rhino/src/main/java/org/mozilla/javascript/CompilerEnvirons.java @@ -33,7 +33,6 @@ public void initFromContext(Context cx) { allowMemberExprAsFunctionName = cx.hasFeature(Context.FEATURE_MEMBER_EXPR_AS_FUNCTION_NAME); strictMode = cx.hasFeature(Context.FEATURE_STRICT_MODE); warningAsError = cx.hasFeature(Context.FEATURE_WARNING_AS_ERROR); - xmlAvailable = cx.hasFeature(Context.FEATURE_E4X); optimizationLevel = cx.getOptimizationLevel(); @@ -42,7 +41,7 @@ public void initFromContext(Context cx) { // Observer code generation in compiled code : generateObserverCount = cx.isGenerateObserverCount(); - + cx.getFactory().getPlugins().forEach(plugin -> plugin.initCompilerEnvirons(cx, this)); } public final ErrorReporter getErrorReporter() { diff --git a/rhino/src/main/java/org/mozilla/javascript/Context.java b/rhino/src/main/java/org/mozilla/javascript/Context.java index 5fe59d9b82..b738870c1b 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Context.java +++ b/rhino/src/main/java/org/mozilla/javascript/Context.java @@ -174,8 +174,9 @@ public class Context implements Closeable { * {@link #VERSION_DEFAULT} or is at least {@link #VERSION_1_6}. * * @since 1.6 Release 1 + * @deprecated This flag may be removed later. */ - public static final int FEATURE_E4X = 6; + @Deprecated public static final int FEATURE_E4X = 6; /** * Control if dynamic scope should be used for name access. If hasFeature(FEATURE_DYNAMIC_SCOPE) diff --git a/rhino/src/main/java/org/mozilla/javascript/Plugin.java b/rhino/src/main/java/org/mozilla/javascript/Plugin.java index d12f163a7b..3336caefde 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Plugin.java +++ b/rhino/src/main/java/org/mozilla/javascript/Plugin.java @@ -7,5 +7,12 @@ */ public interface Plugin { + /** Initializes the safe standard objects. */ default void initSafeStandardObjects(Context cx, ScriptableObject scope, boolean sealed) {} + + /** Initializes the (unsafe) standard objects. */ + default void initStandardObjects(Context cx, ScriptableObject scope, boolean sealed) {} + + /** Initialize the compiler environmnt. */ + default void initCompilerEnvirons(Context cx, CompilerEnvirons compilerEnvirons) {} } diff --git a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java index ef25c4cb39..96354b62f1 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java +++ b/rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java @@ -296,6 +296,9 @@ public static ScriptableObject initStandardObjects( Context cx, ScriptableObject scope, boolean sealed) { ScriptableObject s = initSafeStandardObjects(cx, scope, sealed); + for (Plugin plugin : cx.getFactory().getPlugins()) { + plugin.initStandardObjects(cx, s, sealed); + } new LazilyLoadedCtor( s, "Packages", "org.mozilla.javascript.NativeJavaTopPackage", sealed, true); new LazilyLoadedCtor( From 852e01feac55ce0fa962c9ea8701de42e67f066d Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 21 Oct 2024 14:16:16 +0200 Subject: [PATCH 3/4] Added A/B test --- .../javascript/tests/XMLPresentTest.java | 32 +++++++++++++++++ .../javascript/tests/XMLPresentTest.java | 36 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 rhino-xml/src/test/java/org/mozilla/javascript/tests/XMLPresentTest.java create mode 100644 rhino/src/test/java/org/mozilla/javascript/tests/XMLPresentTest.java diff --git a/rhino-xml/src/test/java/org/mozilla/javascript/tests/XMLPresentTest.java b/rhino-xml/src/test/java/org/mozilla/javascript/tests/XMLPresentTest.java new file mode 100644 index 0000000000..49608aa461 --- /dev/null +++ b/rhino-xml/src/test/java/org/mozilla/javascript/tests/XMLPresentTest.java @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.Scriptable; + +/** + * Test if XML is present. This test exists in "rhino-xml" where XML is present and in "rhino", + * where XML is not on classpath. + * + * @author Roland Praml + */ +public class XMLPresentTest { + + @Test + public void testXMLPresent() { + try (Context cx = ContextFactory.getGlobal().enterContext()) { + Scriptable scope = cx.initStandardObjects(); + Object result = + cx.evaluateString( + scope, "new XML('').toXMLString();", "source", 1, null); + assertEquals("", Context.toString(result)); + } + } +} diff --git a/rhino/src/test/java/org/mozilla/javascript/tests/XMLPresentTest.java b/rhino/src/test/java/org/mozilla/javascript/tests/XMLPresentTest.java new file mode 100644 index 0000000000..41daea65dd --- /dev/null +++ b/rhino/src/test/java/org/mozilla/javascript/tests/XMLPresentTest.java @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.javascript.tests; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.EcmaError; +import org.mozilla.javascript.Scriptable; + +/** + * Test if XML is present. This test exists in "rhino-xml" where XML is present and in "rhino", + * where XML is not on classpath. + * + * @author Roland Praml + */ +public class XMLPresentTest { + + @Test + public void testXMLPresent() { + try (Context cx = ContextFactory.getGlobal().enterContext()) { + Scriptable scope = cx.initStandardObjects(); + Object result = + cx.evaluateString( + scope, "new XML('').toXMLString();", "source", 1, null); + fail("not expected"); + } catch (EcmaError e) { + assertEquals("ReferenceError: \"XML\" is not defined. (source#1)", e.getMessage()); + } + } +} From 99bfb306dee39c24a9d125d060227f203fdbedd2 Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Mon, 28 Oct 2024 08:45:43 +0100 Subject: [PATCH 4/4] use different loading mechanism --- .../mozilla/javascript/xmlimpl/XmlPlugin.java | 5 +++ .../mozilla/javascript/ContextFactory.java | 41 ++++++++++++------- .../java/org/mozilla/javascript/Plugin.java | 3 ++ 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java b/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java index 897d9d4353..4e7bfabf63 100644 --- a/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java +++ b/rhino-xml/src/main/java/org/mozilla/javascript/xmlimpl/XmlPlugin.java @@ -13,6 +13,11 @@ */ public class XmlPlugin implements Plugin { + @Override + public String getName() { + return "xml"; + } + @Override public void initSafeStandardObjects(Context cx, ScriptableObject scope, boolean sealed) { if (cx.hasFeature(Context.FEATURE_E4X)) { diff --git a/rhino/src/main/java/org/mozilla/javascript/ContextFactory.java b/rhino/src/main/java/org/mozilla/javascript/ContextFactory.java index 3aa23e5276..81eee1de4c 100644 --- a/rhino/src/main/java/org/mozilla/javascript/ContextFactory.java +++ b/rhino/src/main/java/org/mozilla/javascript/ContextFactory.java @@ -118,12 +118,24 @@ public class ContextFactory { private volatile Object listeners; private boolean disabledListening; private ClassLoader applicationClassLoader; - private List plugins = getPluginsFromServiceLoader(); + private final List plugins; - private List getPluginsFromServiceLoader() { + /** returns a list of plugins found by the ServiceLoader */ + public static List getDefaultPluginsFromServiceLoader() { List result = new ArrayList(); - ServiceLoader.load(Plugin.class).forEach(result::add); - return Collections.unmodifiableList(result); + ServiceLoader.load(Plugin.class) + .forEach( + plugin -> { + String disabled = + SecurityUtilities.getSystemProperty( + "rhino.plugin." + plugin.getName() + ".disabled"); + if ("1".equals(disabled) || "true".equals(disabled)) { + // the plugin is disabled + } else { + result.add(plugin); + } + }); + return result; } /** Listener of {@link Context} creation and release events. */ @@ -138,6 +150,16 @@ public interface Listener { public void contextReleased(Context cx); } + /** Constructs a new ContextFactory with the plugins found by serviceLoader. */ + public ContextFactory() { + this(getDefaultPluginsFromServiceLoader()); + } + + /** Constructs a new ContextFactory with a given list of plugins. */ + public ContextFactory(List plugins) { + this.plugins = Collections.unmodifiableList(plugins); + } + /** * Get global ContextFactory. * @@ -532,15 +554,4 @@ public final Context enterContext(Context cx) { public List getPlugins() { return plugins; } - - /** - * Sets a list of plugins, that are used by this factory. Note: by default, the plugins are - * loaded by ServiceLoader. - */ - public void setPlugins(List plugins) { - checkNotSealed(); - synchronized (plugins) { - this.plugins = Collections.unmodifiableList(plugins); - } - } } diff --git a/rhino/src/main/java/org/mozilla/javascript/Plugin.java b/rhino/src/main/java/org/mozilla/javascript/Plugin.java index 3336caefde..df593a38e1 100644 --- a/rhino/src/main/java/org/mozilla/javascript/Plugin.java +++ b/rhino/src/main/java/org/mozilla/javascript/Plugin.java @@ -7,6 +7,9 @@ */ public interface Plugin { + /** The name of the plugin. */ + String getName(); + /** Initializes the safe standard objects. */ default void initSafeStandardObjects(Context cx, ScriptableObject scope, boolean sealed) {}