зеркало из https://github.com/mozilla/rhino.git
Implement standard Java ScriptEngine
This is not based on the now-removed JDK code but instead does a few things in a more modern way. See the comments for supported parameters (you can set language and optimization level via properties) and built-in functions (only print is supported right now.) This is built into a separate JAR called "rhino-engine" because otherwise, including it in any Java 8 JDK would break scripts that are expecting to see Nashorn instead.
This commit is contained in:
Родитель
cc8160287e
Коммит
d120651524
|
@ -28,7 +28,7 @@ jobs:
|
|||
- ~/.gradle
|
||||
key: v1-dependencies-{{ checksum "build.gradle" }}
|
||||
|
||||
- run: ./gradlew check jar --max-workers=4
|
||||
- run: ./gradlew check jar --max-workers=2
|
||||
|
||||
- store_test_results:
|
||||
path: buildGradle/test-results
|
||||
|
|
86
build.gradle
86
build.gradle
|
@ -112,9 +112,8 @@ idea {
|
|||
task runtimeJar(type: Jar) {
|
||||
dependsOn compileJava
|
||||
archiveBaseName = 'rhino-runtime'
|
||||
from (sourceSets.main.output) {
|
||||
exclude 'org/mozilla/javascript/tools'
|
||||
}
|
||||
from sourceSets.main.output
|
||||
excludes = ["org/mozilla/javascript/tools", "org/mozilla/javascript/engine/**", "META-INF/services/**"]
|
||||
manifest {
|
||||
attributes(
|
||||
"Manifest-Version": "1.0",
|
||||
|
@ -132,9 +131,30 @@ task runtimeJar(type: Jar) {
|
|||
}
|
||||
}
|
||||
|
||||
task engineJar(type: Jar) {
|
||||
dependsOn compileJava
|
||||
archiveBaseName = 'rhino-engine'
|
||||
from (sourceSets.main.output) {
|
||||
include 'org/mozilla/javascript/engine/**'
|
||||
include 'META-INF/services/**'
|
||||
}
|
||||
manifest {
|
||||
attributes(
|
||||
"Manifest-Version": "1.0",
|
||||
"Implementation-Version": project.version,
|
||||
"Implementation-Title": "Mozilla Rhino ScriptEngine",
|
||||
"Implementation-Vendor": "Mozilla Foundation",
|
||||
"Implementation-URL": "http://www.mozilla.org/rhino",
|
||||
"Built-Date": new Date().format("yyyy-MM-dd"),
|
||||
"Built-Time": new Date().format("HH:mm:ss"),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
jar {
|
||||
dependsOn runtimeJar
|
||||
dependsOn runtimeJar, engineJar
|
||||
from "LICENSE.txt"
|
||||
excludes = ["org/mozilla/javascript/engine/**", "META-INF/services/**"]
|
||||
manifest {
|
||||
attributes(
|
||||
"Manifest-Version": "1.0",
|
||||
|
@ -167,7 +187,13 @@ task javadocJar(type: Jar) {
|
|||
task runtimeJavadocJar(type: Jar) {
|
||||
classifier = 'javadoc'
|
||||
from javadoc
|
||||
exclude 'org/mozilla/javascript/tools'
|
||||
exclude 'org/mozilla/javascript/tools', 'org/mozilla/javascript/engine'
|
||||
}
|
||||
|
||||
task engineJavadocJar(type: Jar) {
|
||||
classifier = 'javadoc'
|
||||
from javadoc
|
||||
include 'org/mozilla/javascript/engine/**'
|
||||
}
|
||||
|
||||
task sourceJar(type: Jar) {
|
||||
|
@ -177,7 +203,13 @@ task sourceJar(type: Jar) {
|
|||
task runtimeSourceJar(type: Jar) {
|
||||
classifier 'sources'
|
||||
from sourceSets.main.allJava
|
||||
exclude 'org/mozilla/javascript/tools'
|
||||
exclude 'org/mozilla/javascript/tools', 'org/mozilla/javascript/engine'
|
||||
}
|
||||
|
||||
task engineSourceJar(type: Jar) {
|
||||
classifier 'sources'
|
||||
from sourceSets.main.allJava
|
||||
include 'org/mozilla/javascript/engine/**'
|
||||
}
|
||||
|
||||
publishing {
|
||||
|
@ -257,6 +289,46 @@ publishing {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
rhinoengine(MavenPublication) {
|
||||
groupId 'org.mozilla'
|
||||
artifactId 'rhino-engine'
|
||||
artifacts = [engineJar, engineSourceJar, engineJavadocJar]
|
||||
|
||||
pom.withXml {
|
||||
def root = asNode()
|
||||
|
||||
root.appendNode('description', """
|
||||
Rhino is an open-source implementation of JavaScript written entirely in Java.
|
||||
It is typically embedded into Java applications to provide scripting to end users.
|
||||
""")
|
||||
root.appendNode("url", "https://developer.mozilla.org/en/Rhino")
|
||||
|
||||
def p = root.appendNode("parent")
|
||||
p.appendNode("groupId", "org.sonatype.oss")
|
||||
p.appendNode("artifactId", "oss-parent")
|
||||
p.appendNode("version", "7")
|
||||
|
||||
def l = root.appendNode("licenses").appendNode("license")
|
||||
l.appendNode("name", "Mozilla Public License, Version 2.0")
|
||||
l.appendNode("url", "http://www.mozilla.org/MPL/2.0/index.txt")
|
||||
|
||||
def scm = root.appendNode("scm")
|
||||
scm.appendNode("connection", "scm:git:git@github.com:mozilla/rhino.git")
|
||||
scm.appendNode("developerConnection", "scm:git:git@github.com:mozilla/rhino.git")
|
||||
scm.appendNode("url", "git@github.com:mozilla/rhino.git")
|
||||
|
||||
def o = root.appendNode("organization")
|
||||
o.appendNode("name", "The Mozilla Foundation")
|
||||
o.appendNode("url", "http://www.mozilla.org")
|
||||
|
||||
def deps = root.appendNode("dependencies")
|
||||
def rhino = deps.appendNode("dependency")
|
||||
rhino.appendNode("groupId", "org.mozilla")
|
||||
rhino.appendNode("artifactId", "rhino")
|
||||
rhino.appendNode("version", getVersion())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (project.hasProperty("mavenPassword")) {
|
||||
|
@ -291,7 +363,7 @@ jacocoTestReport {
|
|||
|
||||
checkstyle {
|
||||
configFile = file("${projectDir}/checkstyle.xml")
|
||||
sourceSets = [project.sourceSets.main]
|
||||
sourceSets = [ project.sourceSets.main ]
|
||||
}
|
||||
|
||||
distributions {
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
org.mozilla.javascript.engine.RhinoScriptEngineFactory
|
|
@ -0,0 +1,60 @@
|
|||
/* 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.engine;
|
||||
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
import javax.script.Bindings;
|
||||
|
||||
/**
|
||||
* This class makes the Bindings object into a Scriptable. That way, we can query and modify
|
||||
* the contents of the Bindings on demand.
|
||||
*/
|
||||
public class BindingsObject
|
||||
extends ScriptableObject {
|
||||
private final Bindings bindings;
|
||||
|
||||
BindingsObject(Bindings bindings) {
|
||||
if (bindings == null) {
|
||||
throw new IllegalArgumentException("Bindings must not be null");
|
||||
}
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getClassName() {
|
||||
return "BindingsObject";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String name, Scriptable start) {
|
||||
Object ret = bindings.get(name);
|
||||
if (ret == null) {
|
||||
return Scriptable.NOT_FOUND;
|
||||
}
|
||||
return Context.jsToJava(ret, Object.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(String name, Scriptable start, Object value) {
|
||||
bindings.put(name, Context.javaToJS(value, start));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String name) {
|
||||
bindings.remove(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(String name, Scriptable start) {
|
||||
return bindings.containsKey(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getIds() {
|
||||
return bindings.keySet().toArray();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/* 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.engine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import javax.script.ScriptContext;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.Function;
|
||||
import org.mozilla.javascript.ScriptRuntime;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This class defines the following built-in functions for the RhinoScriptEngine.
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>print(arg, arg, ...): Write each argument, concatenated to the ScriptEngine's
|
||||
* "standard output" as a string.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class Builtins {
|
||||
|
||||
static final Object BUILTIN_KEY = new Object();
|
||||
|
||||
private Writer stdout;
|
||||
|
||||
void register(Context cx, ScriptableObject scope, ScriptContext sc) {
|
||||
if (sc.getWriter() == null) {
|
||||
stdout = new OutputStreamWriter(System.out);
|
||||
} else {
|
||||
stdout = sc.getWriter();
|
||||
}
|
||||
|
||||
scope.defineFunctionProperties(new String[]{"print"},
|
||||
Builtins.class,
|
||||
ScriptableObject.PERMANENT | ScriptableObject.DONTENUM);
|
||||
}
|
||||
|
||||
public static void print(Context cx, Scriptable thisObj, Object[] args, Function f)
|
||||
throws IOException {
|
||||
Builtins self = getSelf(thisObj);
|
||||
for (Object arg : args) {
|
||||
self.stdout.write(ScriptRuntime.toString(arg));
|
||||
}
|
||||
self.stdout.write('\n');
|
||||
}
|
||||
|
||||
private static Builtins getSelf(Scriptable scope) {
|
||||
// Since this class is invoked as a set of anonymous functions, "this"
|
||||
// in JavaScript does not point to "this" in Java. We set a key on the
|
||||
// top-level scope to address this.
|
||||
return (Builtins) ScriptableObject.getTopScopeValue(scope, BUILTIN_KEY);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/* 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.engine;
|
||||
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptException;
|
||||
import org.mozilla.javascript.Script;
|
||||
|
||||
public class RhinoCompiledScript
|
||||
extends CompiledScript {
|
||||
|
||||
private final RhinoScriptEngine engine;
|
||||
private final Script script;
|
||||
|
||||
RhinoCompiledScript(RhinoScriptEngine engine, Script script) {
|
||||
this.engine = engine;
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(ScriptContext context) throws ScriptException {
|
||||
return engine.eval(script, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngine getEngine() {
|
||||
return engine;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/* 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.engine;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public class RhinoInvocationHandler
|
||||
implements InvocationHandler {
|
||||
|
||||
private final Object thiz;
|
||||
private final RhinoScriptEngine engine;
|
||||
|
||||
RhinoInvocationHandler(RhinoScriptEngine engine, Object thiz) {
|
||||
this.engine = engine;
|
||||
this.thiz = thiz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
return engine.invokeMethodRaw(thiz, method.getName(), method.getReturnType(), args);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,363 @@
|
|||
/* 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.engine;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import javax.script.AbstractScriptEngine;
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptException;
|
||||
import javax.script.SimpleBindings;
|
||||
import org.mozilla.javascript.Callable;
|
||||
import org.mozilla.javascript.Context;
|
||||
import org.mozilla.javascript.ContextFactory;
|
||||
import org.mozilla.javascript.RhinoException;
|
||||
import org.mozilla.javascript.Script;
|
||||
import org.mozilla.javascript.Scriptable;
|
||||
import org.mozilla.javascript.ScriptableObject;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This is the implementation of the standard ScriptEngine interface for Rhino.
|
||||
* </p>
|
||||
* <p>
|
||||
* An instance of the Rhino ScriptEngine is fully self-contained. Bindings at the GLOBAL_SCOPE may
|
||||
* be set, but there is nothing special about them -- if both global and ENGINE_SCOPE bindings are
|
||||
* set then the "engine" bindings override the global ones.
|
||||
* </p>
|
||||
* <p>
|
||||
* The Rhino engine is not thread safe. Rhino does no synchronization of ScriptEngine instances and
|
||||
* no synchronization of Bindings instances. It is up to the caller to ensure that the ScriptEngine
|
||||
* and all its Bindings are used by a single thread at a time.
|
||||
* </p>
|
||||
* <p>
|
||||
* The Rhino script engine includes some top-level built-in functions. See the Builtins class for
|
||||
* more documentation.
|
||||
* </p>
|
||||
* <p>
|
||||
* The engine supports a few configuration parameters that may be set at the "engine scope". Both
|
||||
* are numbers that may be set to a String or Number object.
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>javax.script.language_version: The version of the JavaScript language supported,
|
||||
* which is an integer defined in the Context class. The default is the latest "ES6"
|
||||
* version, defined as 200.</li>
|
||||
* <li>org.mozilla.javascript.optimization_level: The level of optimization Rhino performs
|
||||
* on the generated bytecode. Default is 9, which is the most. Set to -1 to use interpreted
|
||||
* mode.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class RhinoScriptEngine
|
||||
extends AbstractScriptEngine
|
||||
implements Compilable, Invocable {
|
||||
|
||||
/**
|
||||
* Reserved key for the Rhino optimization level. Default is "9," for optimized and compiled code.
|
||||
* Set this to "-1" to run Rhino in interpreted mode -- this is much much slower but the only
|
||||
* option on platforms like Android that don't support class files.
|
||||
*/
|
||||
public static final String OPTIMIZATION_LEVEL = "org.mozilla.javascript.optimization_level";
|
||||
|
||||
static final int DEFAULT_LANGUAGE_VERSION = Context.VERSION_ES6;
|
||||
private static final int DEFAULT_OPT = 9;
|
||||
private static final boolean DEFAULT_DEBUG = true;
|
||||
private static final String DEFAULT_FILENAME = "eval";
|
||||
|
||||
private static final CtxFactory ctxFactory = new CtxFactory();
|
||||
|
||||
private final RhinoScriptEngineFactory factory;
|
||||
private final Builtins builtins;
|
||||
private ScriptableObject topLevelScope = null;
|
||||
|
||||
RhinoScriptEngine(RhinoScriptEngineFactory factory) {
|
||||
this.factory = factory;
|
||||
this.builtins = new Builtins();
|
||||
}
|
||||
|
||||
private Scriptable initScope(Context cx, ScriptContext sc) throws ScriptException {
|
||||
configureContext(cx);
|
||||
|
||||
if (topLevelScope == null) {
|
||||
topLevelScope = cx.initStandardObjects();
|
||||
// We need to stash this away so that the built in functions can find
|
||||
// this engine's specific stuff that they need to work.
|
||||
topLevelScope.associateValue(Builtins.BUILTIN_KEY, builtins);
|
||||
builtins.register(cx, topLevelScope, sc);
|
||||
}
|
||||
|
||||
Scriptable engineScope = new BindingsObject(
|
||||
sc.getBindings(ScriptContext.ENGINE_SCOPE));
|
||||
engineScope.setParentScope(null);
|
||||
engineScope.setPrototype(topLevelScope);
|
||||
|
||||
if (sc.getBindings(ScriptContext.GLOBAL_SCOPE) != null) {
|
||||
Scriptable globalScope = new BindingsObject(
|
||||
sc.getBindings(ScriptContext.GLOBAL_SCOPE));
|
||||
globalScope.setParentScope(null);
|
||||
globalScope.setPrototype(topLevelScope);
|
||||
engineScope.setPrototype(globalScope);
|
||||
}
|
||||
|
||||
return engineScope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(String script, ScriptContext context) throws ScriptException {
|
||||
Context cx = ctxFactory.enterContext();
|
||||
try {
|
||||
Scriptable scope = initScope(cx, context);
|
||||
Object ret = cx.evaluateString(scope, script, getFilename(), 0, null);
|
||||
return Context.jsToJava(ret, Object.class);
|
||||
} catch (RhinoException re) {
|
||||
throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
|
||||
re.columnNumber());
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object eval(Reader reader, ScriptContext context) throws ScriptException {
|
||||
Context cx = ctxFactory.enterContext();
|
||||
try {
|
||||
Scriptable scope = initScope(cx, context);
|
||||
Object ret = cx.evaluateReader(scope, reader, getFilename(), 0, null);
|
||||
return Context.jsToJava(ret, Object.class);
|
||||
} catch (RhinoException re) {
|
||||
throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
|
||||
re.columnNumber());
|
||||
} catch (IOException ioe) {
|
||||
throw new ScriptException(ioe);
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompiledScript compile(String script) throws ScriptException {
|
||||
Context cx = ctxFactory.enterContext();
|
||||
try {
|
||||
configureContext(cx);
|
||||
Script s =
|
||||
cx.compileString(script, getFilename(), 1, null);
|
||||
return new RhinoCompiledScript(this, s);
|
||||
} catch (RhinoException re) {
|
||||
throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
|
||||
re.columnNumber());
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompiledScript compile(Reader script) throws ScriptException {
|
||||
Context cx = ctxFactory.enterContext();
|
||||
try {
|
||||
configureContext(cx);
|
||||
Script s =
|
||||
cx.compileReader(script, getFilename(), 1, null);
|
||||
return new RhinoCompiledScript(this, s);
|
||||
} catch (RhinoException re) {
|
||||
throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
|
||||
re.columnNumber());
|
||||
} catch (IOException ioe) {
|
||||
throw new ScriptException(ioe);
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
Object eval(Script script, ScriptContext sc) throws ScriptException {
|
||||
Context cx = ctxFactory.enterContext();
|
||||
try {
|
||||
Scriptable scope = initScope(cx, sc);
|
||||
Object ret = script.exec(cx, scope);
|
||||
return Context.jsToJava(ret, Object.class);
|
||||
} catch (RhinoException re) {
|
||||
throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
|
||||
re.columnNumber());
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invokeFunction(String name, Object... args)
|
||||
throws ScriptException, NoSuchMethodException {
|
||||
return invokeMethod(null, name, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invokeMethod(Object thiz, String name, Object... args)
|
||||
throws ScriptException, NoSuchMethodException {
|
||||
return invokeMethodRaw(thiz, name, Object.class, args);
|
||||
}
|
||||
|
||||
Object invokeMethodRaw(Object thiz, String name, Class<?> returnType, Object... args)
|
||||
throws ScriptException, NoSuchMethodException {
|
||||
Context cx = ctxFactory.enterContext();
|
||||
try {
|
||||
Scriptable scope = initScope(cx, context);
|
||||
|
||||
Scriptable localThis;
|
||||
if (thiz == null) {
|
||||
localThis = scope;
|
||||
} else {
|
||||
localThis = Context.toObject(thiz, scope);
|
||||
}
|
||||
|
||||
Object f = ScriptableObject.getProperty(localThis, name);
|
||||
if (f == Scriptable.NOT_FOUND) {
|
||||
throw new NoSuchMethodException(name);
|
||||
}
|
||||
if (!(f instanceof Callable)) {
|
||||
throw new ScriptException("\"" + name + "\" is not a function");
|
||||
}
|
||||
Callable func = (Callable) f;
|
||||
|
||||
if (args != null) {
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
args[i] = Context.javaToJS(args[i], scope);
|
||||
}
|
||||
}
|
||||
|
||||
Object ret = func.call(cx, scope, localThis, args);
|
||||
if (returnType == Void.TYPE) {
|
||||
return null;
|
||||
}
|
||||
return Context.jsToJava(ret, returnType);
|
||||
|
||||
} catch (RhinoException re) {
|
||||
throw new ScriptException(re.getMessage(), re.sourceName(), re.lineNumber(),
|
||||
re.columnNumber());
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getInterface(Class<T> clasz) {
|
||||
if ((clasz == null) || !clasz.isInterface()) {
|
||||
throw new IllegalArgumentException("Not an interface");
|
||||
}
|
||||
Context cx = ctxFactory.enterContext();
|
||||
try {
|
||||
Scriptable scope = initScope(cx, context);
|
||||
if (methodsMissing(scope, clasz)) {
|
||||
return null;
|
||||
}
|
||||
} catch (ScriptException se) {
|
||||
return null;
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
|
||||
new Class<?>[]{clasz}, new RhinoInvocationHandler(this, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getInterface(Object thiz, Class<T> clasz) {
|
||||
if ((clasz == null) || !clasz.isInterface()) {
|
||||
throw new IllegalArgumentException("Not an interface");
|
||||
}
|
||||
Context cx = ctxFactory.enterContext();
|
||||
try {
|
||||
Scriptable scope = initScope(cx, context);
|
||||
Scriptable thisObj = Context.toObject(thiz, scope);
|
||||
if (methodsMissing(thisObj, clasz)) {
|
||||
return null;
|
||||
}
|
||||
} catch (ScriptException se) {
|
||||
return null;
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
|
||||
new Class<?>[]{clasz}, new RhinoInvocationHandler(this, thiz));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bindings createBindings() {
|
||||
return new SimpleBindings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngineFactory getFactory() {
|
||||
return factory;
|
||||
}
|
||||
|
||||
private void configureContext(Context cx) throws ScriptException {
|
||||
Object lv = get(ScriptEngine.LANGUAGE_VERSION);
|
||||
if (lv != null) {
|
||||
cx.setLanguageVersion(parseInteger(lv));
|
||||
}
|
||||
Object ol = get(OPTIMIZATION_LEVEL);
|
||||
if (ol != null) {
|
||||
cx.setOptimizationLevel(parseInteger(ol));
|
||||
}
|
||||
}
|
||||
|
||||
private int parseInteger(Object v) throws ScriptException {
|
||||
if (v instanceof String) {
|
||||
try {
|
||||
return Integer.parseInt((String) v);
|
||||
} catch (NumberFormatException nfe) {
|
||||
throw new ScriptException("Invalid number " + v);
|
||||
}
|
||||
} else if (v instanceof Integer) {
|
||||
return (Integer) v;
|
||||
} else {
|
||||
throw new ScriptException("Value must be a string or number");
|
||||
}
|
||||
}
|
||||
|
||||
private String getFilename() {
|
||||
Object fn = get(ScriptEngine.FILENAME);
|
||||
if (fn instanceof String) {
|
||||
return (String) fn;
|
||||
}
|
||||
return DEFAULT_FILENAME;
|
||||
}
|
||||
|
||||
private boolean methodsMissing(Scriptable scope, Class<?> clasz) {
|
||||
for (Method m : clasz.getMethods()) {
|
||||
if (m.getDeclaringClass() == Object.class) {
|
||||
continue;
|
||||
}
|
||||
Object methodObj = ScriptableObject.getProperty(scope, m.getName());
|
||||
if (!(methodObj instanceof Callable)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static final class CtxFactory
|
||||
extends ContextFactory {
|
||||
|
||||
@Override
|
||||
protected boolean hasFeature(Context cx, int featureIndex) {
|
||||
if (featureIndex == Context.FEATURE_INTEGER_WITHOUT_DECIMAL_PLACE) {
|
||||
return true;
|
||||
}
|
||||
return super.hasFeature(cx, featureIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onContextCreated(Context cx) {
|
||||
cx.setLanguageVersion(Context.VERSION_ES6);
|
||||
cx.setOptimizationLevel(DEFAULT_OPT);
|
||||
cx.setGeneratingDebug(DEFAULT_DEBUG);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,140 @@
|
|||
/* 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.engine;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import org.mozilla.javascript.Context;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This is an implementation of the standard Java "ScriptEngine" for Rhino. If the Rhino engine
|
||||
* (typically in the form of the "rhino-engine" JAR) is in the classpath, then this script
|
||||
* engine will be activated.
|
||||
* </p>
|
||||
* <p>
|
||||
* See the list of constants in this class for the list of language names, file extensions, and
|
||||
* MIME types that this engine supports. This list is essentially the same as the list supported
|
||||
* in the Nashorn script engine that was included in Java 8.
|
||||
* </p>
|
||||
* <p>
|
||||
* Since this engine and Nashorn support the same language and file extensions, then unless
|
||||
* you are sure you are running in an environment that has Nashorn, the best way to get this
|
||||
* engine is to call ScriptEngine.getEngineByName("rhino") to ask for Rhino directly.
|
||||
* </p>
|
||||
*/
|
||||
public class RhinoScriptEngineFactory
|
||||
implements ScriptEngineFactory {
|
||||
|
||||
public static final String NAME = "rhino";
|
||||
public static final String LANGUAGE = "javascript";
|
||||
public static final List<String> NAMES =
|
||||
Arrays.asList("rhino", "Rhino", "javascript", "JavaScript");
|
||||
public static final List<String> EXTENSIONS =
|
||||
Collections.singletonList("js");
|
||||
public static final List<String> MIME_TYPES =
|
||||
Arrays.asList("application/javascript", "application/ecmascript",
|
||||
"text/javascript", "text/ecmascript");
|
||||
public static final String LANGUAGE_VERSION =
|
||||
String.valueOf(RhinoScriptEngine.DEFAULT_LANGUAGE_VERSION);
|
||||
|
||||
@Override
|
||||
public String getEngineName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEngineVersion() {
|
||||
Context cx = Context.enter();
|
||||
try {
|
||||
String v = cx.getImplementationVersion();
|
||||
return (v == null ? "unknown" : v);
|
||||
} finally {
|
||||
Context.exit();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getExtensions() {
|
||||
return EXTENSIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getMimeTypes() {
|
||||
return MIME_TYPES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getNames() {
|
||||
return NAMES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageName() {
|
||||
return LANGUAGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLanguageVersion() {
|
||||
return LANGUAGE_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getParameter(String key) {
|
||||
switch (key) {
|
||||
case ScriptEngine.ENGINE:
|
||||
return getEngineName();
|
||||
case ScriptEngine.ENGINE_VERSION:
|
||||
return getEngineVersion();
|
||||
case ScriptEngine.LANGUAGE:
|
||||
return getLanguageName();
|
||||
case ScriptEngine.LANGUAGE_VERSION:
|
||||
return getLanguageVersion();
|
||||
case ScriptEngine.NAME:
|
||||
return NAME;
|
||||
case "THREADING":
|
||||
// Engines are explicitly not thread-safe
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodCallSyntax(String obj, String m, String... args) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(obj).append('.').append(m).append('(');
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(',');
|
||||
}
|
||||
sb.append(args[i]);
|
||||
}
|
||||
sb.append(");");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOutputStatement(String toDisplay) {
|
||||
return "print('" + toDisplay + "');";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProgram(String... statements) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String stmt : statements) {
|
||||
sb.append(stmt).append(";\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptEngine getScriptEngine() {
|
||||
return new RhinoScriptEngine(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package org.mozilla.javascript.tests.scriptengine;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
import javax.script.SimpleScriptContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class BuiltinsTest {
|
||||
|
||||
private static ScriptEngineManager manager;
|
||||
|
||||
private ScriptEngine engine;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() {
|
||||
manager = new ScriptEngineManager();
|
||||
manager.registerEngineName("rhino", new RhinoScriptEngineFactory());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
engine = manager.getEngineByName("rhino");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrintStdout() throws ScriptException {
|
||||
engine.eval("print('Hello, World!');");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrintWriter() throws ScriptException {
|
||||
StringWriter sw = new StringWriter();
|
||||
ScriptContext sc = new SimpleScriptContext();
|
||||
sc.setWriter(sw);
|
||||
engine.eval("print('one', 2, true);", sc);
|
||||
assertEquals(sw.toString(), "one2true\n");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrintWriterGeneric() throws ScriptException {
|
||||
StringWriter sw = new StringWriter();
|
||||
engine.getContext().setWriter(sw);
|
||||
engine.eval(engine.getFactory().getOutputStatement("Display This!"));
|
||||
assertEquals(sw.toString(), "Display This!\n");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package org.mozilla.javascript.tests.scriptengine;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import org.junit.Test;
|
||||
import org.mozilla.javascript.engine.RhinoScriptEngine;
|
||||
import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/*
|
||||
* A series of tests that depend on us having our engine registered with the
|
||||
* ScriptEngineManager by default.
|
||||
*/
|
||||
public class FactoryTest {
|
||||
|
||||
@Test
|
||||
public void findRhinoFactory() {
|
||||
ScriptEngineManager manager = new ScriptEngineManager();
|
||||
for (ScriptEngineFactory factory : manager.getEngineFactories()) {
|
||||
if (factory instanceof RhinoScriptEngineFactory) {
|
||||
assertEquals("rhino", factory.getEngineName());
|
||||
assertEquals("rhino", factory.getParameter(ScriptEngine.ENGINE));
|
||||
assertEquals("rhino", factory.getParameter(ScriptEngine.NAME));
|
||||
// This could be "unknown" if we're not running from a regular JAR
|
||||
assertFalse(factory.getEngineVersion().isEmpty());
|
||||
assertEquals("javascript", factory.getLanguageName());
|
||||
assertEquals("javascript", factory.getParameter(ScriptEngine.LANGUAGE));
|
||||
assertEquals("200", factory.getLanguageVersion());
|
||||
assertEquals("200", factory.getParameter(ScriptEngine.LANGUAGE_VERSION));
|
||||
assertNull(factory.getParameter("THREADING"));
|
||||
assertTrue(factory.getExtensions().contains("js"));
|
||||
assertTrue(factory.getMimeTypes().contains("application/javascript"));
|
||||
assertTrue(factory.getMimeTypes().contains("application/ecmascript"));
|
||||
assertTrue(factory.getMimeTypes().contains("text/javascript"));
|
||||
assertTrue(factory.getMimeTypes().contains("text/ecmascript"));
|
||||
assertTrue(factory.getNames().contains("rhino"));
|
||||
assertTrue(factory.getNames().contains("Rhino"));
|
||||
assertTrue(factory.getNames().contains("javascript"));
|
||||
assertTrue(factory.getNames().contains("JavaScript"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
fail("Expected to find Rhino script engine");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRhinoFactory() {
|
||||
// This will always uniquely return our engine.
|
||||
// In Java 8, other ways to find it may return Nashorn.
|
||||
ScriptEngine engine = new ScriptEngineManager().getEngineByName("rhino");
|
||||
assertTrue(engine instanceof RhinoScriptEngine);
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package org.mozilla.javascript.tests.scriptengine;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import javax.script.Invocable;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class InvocableTest {
|
||||
|
||||
private static ScriptEngineManager manager;
|
||||
|
||||
private ScriptEngine engine;
|
||||
private Invocable iEngine;
|
||||
|
||||
@BeforeClass
|
||||
public static void init() {
|
||||
manager = new ScriptEngineManager();
|
||||
manager.registerEngineName("rhino", new RhinoScriptEngineFactory());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
engine = manager.getEngineByName("rhino");
|
||||
iEngine = (Invocable) engine;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeFunctionTest() throws ScriptException, NoSuchMethodException {
|
||||
engine.eval("function foo(a, b) { return a + b; }");
|
||||
Object result = iEngine.invokeFunction("foo", 2, 2);
|
||||
assertEquals(result, 4L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeScriptFunctionTest() throws ScriptException, NoSuchMethodException {
|
||||
Object scriptObj = engine.eval("let o = {};\n"
|
||||
+ "o.test = function(x) { return x + 2; }\n"
|
||||
+ "o;");
|
||||
assertEquals(4L, iEngine.invokeMethod(scriptObj, "test", 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeGenericFunctionTest() throws ScriptException, NoSuchMethodException {
|
||||
engine.eval("let o = {};\n"
|
||||
+ "o.test = function(x) { return x + 2; }\n");
|
||||
Object result = engine.eval(engine.getFactory().getMethodCallSyntax("o", "test", "1"));
|
||||
assertEquals(3L, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeGenericFunctionTest2() throws ScriptException, NoSuchMethodException {
|
||||
engine.eval("let o = {};\n"
|
||||
+ "o.test = function(x, y) { return x + y; }\n");
|
||||
Object result = engine.eval(engine.getFactory().getMethodCallSyntax("o", "test", "1", "7"));
|
||||
assertEquals(8L, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeMethodTest()
|
||||
throws ScriptException, NoSuchMethodException, FileNotFoundException {
|
||||
engine.eval(new FileReader("testsrc/assert.js"));
|
||||
engine.eval("function FooObj() { this.x = 0; }\n"
|
||||
+ "FooObj.prototype.set = function(a, b) { this.x = a + b; }");
|
||||
engine.eval("let f = new FooObj();\n"
|
||||
+ "assertEquals(f.x, 0);\n"
|
||||
+ "f.set(2, 2);\n"
|
||||
+ "assertEquals(f.x, 4);");
|
||||
|
||||
Object fooObj = engine.eval("let y = new FooObj(); y");
|
||||
assertNotNull(fooObj);
|
||||
iEngine.invokeMethod(fooObj, "set", 3, 3);
|
||||
Object result = engine.eval("y.x");
|
||||
assertEquals(result, 6L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interfaceFunctionTest()
|
||||
throws ScriptException, FileNotFoundException {
|
||||
engine.eval(new FileReader("testsrc/assert.js"));
|
||||
engine.eval("var foo = 'initialized';\n"
|
||||
+ "function setFoo(v) { foo = v; }\n"
|
||||
+ "function getFoo() { return foo; }\n"
|
||||
+ "function addItUp(a, b) { return a + b; }");
|
||||
I tester = iEngine.getInterface(I.class);
|
||||
assertEquals(tester.getFoo(), "initialized");
|
||||
tester.setFoo("tested");
|
||||
assertEquals(tester.getFoo(), "tested");
|
||||
assertEquals(tester.addItUp(100, 1), 101);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interfaceMethodTest()
|
||||
throws ScriptException, FileNotFoundException {
|
||||
engine.eval(new FileReader("testsrc/assert.js"));
|
||||
Object foo = engine.eval("function Foo() { this.foo = 'initialized' }\n"
|
||||
+ "Foo.prototype.setFoo = function(v) { this.foo = v; };\n"
|
||||
+ "Foo.prototype.getFoo = function() { return this.foo; };\n"
|
||||
+ "Foo.prototype.addItUp = function(a, b) { return a + b; };\n"
|
||||
+ "new Foo();");
|
||||
I tester = iEngine.getInterface(foo, I.class);
|
||||
assertEquals(tester.getFoo(), "initialized");
|
||||
tester.setFoo("tested");
|
||||
assertEquals(tester.getFoo(), "tested");
|
||||
assertEquals(tester.addItUp(100, 1), 101);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interfaceFunctionMissingTest() {
|
||||
I tester = iEngine.getInterface(I.class);
|
||||
assertNull(tester);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void interfaceMethodMissingTest()
|
||||
throws ScriptException {
|
||||
// Functions defined, but not on the right object
|
||||
Object foo = engine.eval("var foo = 'initialized';\n"
|
||||
+ "function setFoo(v) { foo = v; }\n"
|
||||
+ "function getFoo() { return foo; }\n"
|
||||
+ "function addItUp(a, b) { return a + b; }\n"
|
||||
+ "function Foo() {}\n"
|
||||
+ "new Foo();");
|
||||
I tester = iEngine.getInterface(foo, I.class);
|
||||
assertNull(tester);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeNotFoundTest() {
|
||||
assertThrows(NoSuchMethodException.class, () -> {
|
||||
iEngine.invokeFunction("foo", 2, 2);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invokeNotFunctionTest() {
|
||||
assertThrows(ScriptException.class, () -> {
|
||||
engine.eval("foo = 'bar';");
|
||||
iEngine.invokeFunction("foo", 2, 2);
|
||||
});
|
||||
}
|
||||
|
||||
interface I {
|
||||
|
||||
void setFoo(String v);
|
||||
|
||||
String getFoo();
|
||||
|
||||
int addItUp(int a, int b);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,276 @@
|
|||
package org.mozilla.javascript.tests.scriptengine;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import javax.script.Bindings;
|
||||
import javax.script.Compilable;
|
||||
import javax.script.CompiledScript;
|
||||
import javax.script.ScriptContext;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
import javax.script.SimpleBindings;
|
||||
import javax.script.SimpleScriptContext;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.mozilla.javascript.engine.RhinoScriptEngine;
|
||||
import org.mozilla.javascript.engine.RhinoScriptEngineFactory;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class ScriptEngineTest {
|
||||
|
||||
private static ScriptEngineManager manager;
|
||||
private ScriptEngine engine;
|
||||
private Compilable cEngine;
|
||||
|
||||
@BeforeClass
|
||||
public static void initManager() {
|
||||
manager = new ScriptEngineManager();
|
||||
manager.registerEngineName("rhino", new RhinoScriptEngineFactory());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
engine = manager.getEngineByName("rhino");
|
||||
cEngine = (Compilable) engine;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHello() throws ScriptException {
|
||||
Object result = engine.eval("'Hello, World!';");
|
||||
assertEquals(result, "Hello, World!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHelloInterpreted() throws ScriptException {
|
||||
engine.put(RhinoScriptEngine.OPTIMIZATION_LEVEL, -1);
|
||||
Object result = engine.eval("'Hello, World!';");
|
||||
assertEquals(result, "Hello, World!");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testHelloReader() throws ScriptException {
|
||||
String src = "1 + 1;";
|
||||
StringReader sr = new StringReader(src);
|
||||
Object result = engine.eval(sr);
|
||||
assertEquals(result, 2L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGenericStatements() throws ScriptException {
|
||||
Object result = engine.eval(engine.getFactory().getProgram(
|
||||
"let x = 1;",
|
||||
"let y = 2",
|
||||
"x + y"
|
||||
));
|
||||
assertEquals(3L, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testThrows() {
|
||||
assertThrows(ScriptException.class, () -> {
|
||||
engine.eval("throw 'This is an error'");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEngineBindings() throws IOException, ScriptException {
|
||||
engine.put("string", "Hello");
|
||||
engine.put("integer", 123);
|
||||
engine.put("a", "a");
|
||||
engine.put("b", "b");
|
||||
engine.put("c", "c");
|
||||
|
||||
// Ensure that stuff we just stuck in bindings made it to a global
|
||||
engine.eval(new FileReader("testsrc/assert.js"));
|
||||
engine.eval("assertEquals(string, 'Hello');\n"
|
||||
+ "assertEquals(integer, 123);\n"
|
||||
+ "string = 'Goodbye';\n"
|
||||
+ "assertEquals(string, 'Goodbye');");
|
||||
assertEquals(engine.get("string"), "Goodbye");
|
||||
|
||||
// Make sure we can delete
|
||||
engine.getBindings(ScriptContext.ENGINE_SCOPE).remove("string");
|
||||
// This will throw because string is undefined
|
||||
assertThrows(ScriptException.class, () -> {
|
||||
engine.eval("let failing = string + '123';");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEngineScope() throws IOException, ScriptException {
|
||||
engine.put("string", "Hello");
|
||||
engine.put("integer", 123);
|
||||
engine.eval(new FileReader("testsrc/assert.js"));
|
||||
engine.eval("assertEquals(string, 'Hello');"
|
||||
+ "assertEquals(integer, 123);");
|
||||
|
||||
// Additional things added to the context but old stuff still there
|
||||
engine.put("second", true);
|
||||
engine.put("integer", 99);
|
||||
engine.eval("assertEquals(string, 'Hello');"
|
||||
+ "assertEquals(integer, 99);"
|
||||
+ "assertTrue(second);");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScopedBindings() throws IOException, ScriptException {
|
||||
ScriptContext sc = new SimpleScriptContext();
|
||||
|
||||
// We treat engine and global scope the same -- if the user actually
|
||||
// uses both, then engine scope overrides global scope.
|
||||
Bindings eb = new SimpleBindings();
|
||||
sc.setBindings(eb, ScriptContext.ENGINE_SCOPE);
|
||||
eb.put("engine", Boolean.TRUE);
|
||||
eb.put("level", 2);
|
||||
|
||||
Bindings gb = new SimpleBindings();
|
||||
sc.setBindings(gb, ScriptContext.GLOBAL_SCOPE);
|
||||
gb.put("global", Boolean.TRUE);
|
||||
gb.put("level", 0);
|
||||
|
||||
engine.eval(new FileReader("testsrc/assert.js"), sc);
|
||||
engine.eval("assertTrue(engine);"
|
||||
+ "assertTrue(global);"
|
||||
+ "assertEquals(level, 2);", sc);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReservedBindings() throws ScriptException {
|
||||
engine.put(ScriptEngine.ENGINE, "engine");
|
||||
engine.put(ScriptEngine.ENGINE_VERSION, "123");
|
||||
engine.put(ScriptEngine.LANGUAGE, "foo");
|
||||
engine.put(ScriptEngine.NAME, "nothing");
|
||||
|
||||
// Can't actually test for those invalid property names -- but
|
||||
// at least they didn't break the script.
|
||||
assertEquals(engine.eval("'success'"), "success");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompiled() throws ScriptException, IOException {
|
||||
CompiledScript asserts =
|
||||
cEngine.compile(new FileReader("testsrc/assert.js"));
|
||||
CompiledScript tests =
|
||||
cEngine.compile("assertEquals(compiled, true);");
|
||||
|
||||
// Fails because asserts have not been loaded
|
||||
assertThrows(ScriptException.class, tests::eval);
|
||||
|
||||
asserts.eval();
|
||||
// Fails because value has not been set
|
||||
assertThrows(ScriptException.class, tests::eval);
|
||||
|
||||
engine.put("compiled", Boolean.TRUE);
|
||||
tests.eval();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompiled2() throws ScriptException, IOException {
|
||||
CompiledScript asserts =
|
||||
cEngine.compile(new FileReader("testsrc/assert.js"));
|
||||
CompiledScript init =
|
||||
cEngine.compile("value = 0;");
|
||||
CompiledScript tests =
|
||||
cEngine.compile("assertEquals(value, expectedValue);"
|
||||
+ "value += 1;");
|
||||
|
||||
asserts.eval();
|
||||
init.eval();
|
||||
for (int i = 0; i <= 10; i++) {
|
||||
engine.put("expectedValue", i);
|
||||
tests.eval();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCompiledThrows() throws ScriptException {
|
||||
engine.put(ScriptEngine.FILENAME, "throws1.js");
|
||||
CompiledScript throw1 = cEngine.compile("throw 'one';");
|
||||
engine.put(ScriptEngine.FILENAME, "throws2.js");
|
||||
CompiledScript throw2 = cEngine.compile("throw 'two';");
|
||||
|
||||
try {
|
||||
throw1.eval();
|
||||
fail("Expected a throw");
|
||||
} catch (ScriptException se) {
|
||||
assertTrue(se.getMessage().startsWith("one"));
|
||||
assertEquals("throws1.js", se.getFileName());
|
||||
assertEquals(1, se.getLineNumber());
|
||||
}
|
||||
|
||||
try {
|
||||
throw2.eval();
|
||||
fail("Expected a throw");
|
||||
} catch (ScriptException se) {
|
||||
assertTrue(se.getMessage().startsWith("two"));
|
||||
assertEquals("throws2.js", se.getFileName());
|
||||
assertEquals(1, se.getLineNumber());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCantCompile() {
|
||||
assertThrows(ScriptException.class, () -> {
|
||||
cEngine.compile("This is not JavaScript at all!");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLanguageVersion() throws ScriptException {
|
||||
// Default language version is modernish
|
||||
ScriptEngine newEngine = manager.getEngineByName("rhino");
|
||||
assertEquals(newEngine.eval("Symbol() == Symbol()"), Boolean.FALSE);
|
||||
|
||||
// Older language versions
|
||||
ScriptEngine oldEngine = manager.getEngineByName("rhino");
|
||||
oldEngine.put(ScriptEngine.LANGUAGE_VERSION, 120);
|
||||
assertThrows(ScriptException.class, () -> {
|
||||
oldEngine.eval("Symbol() == Symbol()");
|
||||
});
|
||||
|
||||
// The same with a string
|
||||
ScriptEngine olderEngine = manager.getEngineByName("rhino");
|
||||
olderEngine.put(ScriptEngine.LANGUAGE_VERSION, "100");
|
||||
assertThrows(ScriptException.class, () -> {
|
||||
olderEngine.eval("Symbol() == Symbol()");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadLanguageVersion() {
|
||||
assertThrows(ScriptException.class, () -> {
|
||||
engine.put(ScriptEngine.LANGUAGE_VERSION, "Not a number");
|
||||
engine.eval("print('Hi!');");
|
||||
});
|
||||
assertThrows(ScriptException.class, () -> {
|
||||
engine.put(ScriptEngine.LANGUAGE_VERSION, 3.14);
|
||||
engine.eval("print('Hi!');");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilename() {
|
||||
engine.put(ScriptEngine.FILENAME, "test.js");
|
||||
try {
|
||||
engine.eval("throw 'This is an exception';");
|
||||
} catch (ScriptException se) {
|
||||
assertEquals(se.getFileName(), "test.js");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJavaObject() throws ScriptException {
|
||||
File f = new File("testsrc/assert.js");
|
||||
String absVal = f.getAbsolutePath();
|
||||
engine.put("file", f);
|
||||
Object result = engine.eval("file.getAbsolutePath();");
|
||||
assertEquals(absVal, result);
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче