Skip to content
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

Introduced a plugin-API and show usage with rhino-xml #1699

Open
wants to merge 6 commits 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
6 changes: 3 additions & 3 deletions rhino-xml/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
module org.mozilla.javascript.xml {
exports org.mozilla.javascript.xmlimpl;

provides org.mozilla.javascript.xml.XMLLoader with
org.mozilla.javascript.xmlimpl.XMLLoaderImpl;

requires transitive org.mozilla.rhino;
requires transitive java.xml;

provides org.mozilla.javascript.Plugin with
org.mozilla.javascript.xmlimpl.XmlPlugin;
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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;
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 String getName() {
return "xml";
}

@Override
public boolean isSafe() {
return true;
}

@Override
public void init(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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.mozilla.javascript.xmlimpl.XmlPlugin

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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('<a></a>').toXMLString();", "source", 1, null);
assertEquals("<a/>", Context.toString(result));
}
}
}
2 changes: 1 addition & 1 deletion rhino/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
exports org.mozilla.javascript.xml;

uses org.mozilla.javascript.RegExpProxy;
uses org.mozilla.javascript.xml.XMLLoader;
uses org.mozilla.javascript.Plugin;

provides org.mozilla.javascript.RegExpProxy with
org.mozilla.javascript.regexp.RegExpImpl;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to remove all XML-Lib dependency to rhino-xml. There is still org.mozilla.javascript.xml.XMLLib and org.mozilla.javascript.xml.XMLObject in rhino base module. They are a bit difficult to remove


interpretedMode = cx.isInterpretedMode();

Expand All @@ -42,6 +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() {
Expand Down
11 changes: 4 additions & 7 deletions rhino/src/main/java/org/mozilla/javascript/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import org.mozilla.javascript.debug.DebuggableScript;
import org.mozilla.javascript.debug.Debugger;
import org.mozilla.javascript.xml.XMLLib;
import org.mozilla.javascript.xml.XMLLoader;

/**
* This class represents the runtime context of an executing script.
Expand Down Expand Up @@ -199,7 +198,8 @@ public class Context implements Closeable {

/**
* Control if support for E4X(ECMAScript for XML) extension is available. If
* hasFeature(FEATURE_E4X) returns true, the XML syntax is available.
* hasFeature(FEATURE_E4X) returns true and rhino-xml is on classpath, the XML syntax is
* available.
*
* <p>By default {@link #hasFeature(int)} returns true if the current JS version is set to
* {@link #VERSION_DEFAULT} or is at least {@link #VERSION_1_6}.
Expand Down Expand Up @@ -2327,11 +2327,8 @@ public boolean hasFeature(int featureIndex) {
*/
@Deprecated
public XMLLib.Factory getE4xImplementationFactory() {
XMLLoader loader = ScriptRuntime.loadOneServiceImplementation(XMLLoader.class);
if (loader != null) {
return loader.getFactory();
}
return null;
throw new UnsupportedOperationException(
"getE4xImplementationFactory is no longer supported");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the intention of this method? It can only provide the implementation class name.
It would help, if I can remove this completely (as it can only provide the implementation class name)
According to javadoc, it is experimental

}

/**
Expand Down
39 changes: 39 additions & 0 deletions rhino/src/main/java/org/mozilla/javascript/ContextFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>
Expand Down Expand Up @@ -114,6 +118,26 @@ public class ContextFactory {
private volatile Object listeners;
private boolean disabledListening;
private ClassLoader applicationClassLoader;
private final List<Plugin> plugins;

/** returns a list of plugins found by the ServiceLoader */
public static List<Plugin> getDefaultPluginsFromServiceLoader() {
List<Plugin> result = new ArrayList<Plugin>();
ServiceLoader.load(Plugin.class)
.forEach(
plugin -> {
// TODO: use RhinoConfig here!
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

waiting for other PR

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. */
public interface Listener {
Expand All @@ -127,6 +151,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<Plugin> plugins) {
this.plugins = Collections.unmodifiableList(plugins);
}

/**
* Get global ContextFactory.
*
Expand Down Expand Up @@ -516,4 +550,9 @@ public final void exit() {
public final Context enterContext(Context cx) {
return Context.enter(cx, this);
}

/** Returns a list of plugins. */
public List<Plugin> getPlugins() {
return plugins;
}
}
26 changes: 26 additions & 0 deletions rhino/src/main/java/org/mozilla/javascript/Plugin.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.mozilla.javascript;

/**
* Plugins may be loaded with the serviceLocator.
*
* @author Roland Praml, Foconis Analytics GmbH
*/
public interface Plugin {

/**
* defines, if this plugin should be initialized in safe standard objects or only in standard
* objects
*/
default boolean isSafe() {
return false;
}

/** The name of the plugin. */
String getName();

/** Initializes the safe standard objects. */
default void init(Context cx, ScriptableObject scope, boolean sealed) {}

/** Initialize the compiler environmnt. */
default void initCompilerEnvirons(Context cx, CompilerEnvirons compilerEnvirons) {}
}
13 changes: 8 additions & 5 deletions rhino/src/main/java/org/mozilla/javascript/ScriptRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
import org.mozilla.javascript.v8dtoa.DoubleConversion;
import org.mozilla.javascript.v8dtoa.FastDtoa;
import org.mozilla.javascript.xml.XMLLib;
import org.mozilla.javascript.xml.XMLLoader;
import org.mozilla.javascript.xml.XMLObject;

/**
Expand Down Expand Up @@ -201,10 +200,9 @@ public static ScriptableObject initSafeStandardObjects(
new LazilyLoadedCtor(
scope, "Continuation", "org.mozilla.javascript.NativeContinuation", sealed, true);

if (cx.hasFeature(Context.FEATURE_E4X)) {
XMLLoader loader = loadOneServiceImplementation(XMLLoader.class);
if (loader != null) {
loader.load(scope, sealed);
for (Plugin plugin : cx.getFactory().getPlugins()) {
if (plugin.isSafe()) {
plugin.init(cx, scope, sealed);
}
}

Expand Down Expand Up @@ -312,6 +310,11 @@ public static ScriptableObject initStandardObjects(
Context cx, ScriptableObject scope, boolean sealed) {
ScriptableObject s = initSafeStandardObjects(cx, scope, sealed);

for (Plugin plugin : cx.getFactory().getPlugins()) {
if (!plugin.isSafe()) {
plugin.init(cx, scope, sealed);
}
}
new LazilyLoadedCtor(
s, "Packages", "org.mozilla.javascript.NativeJavaTopPackage", sealed, true);
new LazilyLoadedCtor(
Expand Down
13 changes: 3 additions & 10 deletions rhino/src/main/java/org/mozilla/javascript/xml/XMLLib.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,11 @@ public abstract class XMLLib {
* 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}.
*
* @deprecated required for Context.getE4xImplementationFactory().
*/
@Deprecated
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();
}

Expand Down
10 changes: 0 additions & 10 deletions rhino/src/main/java/org/mozilla/javascript/xml/XMLLoader.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -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('<a></a>').toXMLString();", "source", 1, null);
fail("not expected");
} catch (EcmaError e) {
assertEquals("ReferenceError: \"XML\" is not defined. (source#1)", e.getMessage());
}
}
}