package com.oracle.truffle.js.scriptengine.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.NoSuchElementException;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine;
import org.graalvm.polyglot.Source;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine;
public class TestEngine {
static final String TESTED_ENGINE_NAME = "JavaScript";
private final ScriptEngineManager manager = new ScriptEngineManager();
@Rule public ExpectedException expectedException = ExpectedException.none();
private ScriptEngine getEngine() {
return manager.getEngineByName(TESTED_ENGINE_NAME);
}
@Test
public void checkName() {
assertEquals(getEngine().getClass().getName(), GraalJSScriptEngine.class.getName());
}
@Test
public void compileAndEval1() throws ScriptException {
assertEquals(true, getEngine().eval("true"));
}
@Test
public void compileAndEval2() throws ScriptException {
assertEquals(true, ((Compilable) getEngine()).compile("true").eval());
}
@Test
public void compileSyntaxError() throws ScriptException {
expectedException.expect(ScriptException.class);
((Compilable) getEngine()).compile(":-(");
}
@Test
public void declareVar() throws ScriptException {
ScriptEngine engine = getEngine();
engine.getBindings(ScriptContext.ENGINE_SCOPE).put("polyglot.js.allowHostAccess", true);
engine.getBindings(ScriptContext.ENGINE_SCOPE).put("polyglot.js.allowHostClassLookup", true);
assertEquals(true, engine.eval(
"var m = new javax.script.ScriptEngineManager();" +
"var engine = m.getEngineByName('Graal.js');" +
"var x;" +
"engine.eval('var x = \"ENGINE\"');" +
"typeof x == 'undefined'"
));
}
@Test
@Ignore("We do not support `engine.class.static.getProperty`")
public void getProperty() throws ScriptException {
assertEquals(true, getEngine().eval(
"var m = new javax.script.ScriptEngineManager();" +
"var engine = m.getEngineByName('Graal.js');" +
"var obj = {prop: 'value'};" +
"engine.class.static.getProperty(obj, 'prop') == 'value';"
));
}
@Test
public void evalWithGlobal() throws ScriptException {
ScriptEngine engine = getEngine();
Bindings bindings = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
bindings.put("x", 42);
boolean result = (boolean) engine.eval("x === 42;");
assertTrue(result);
}
@Test
public void getPolyglotContextEvalWithGlobal() throws IOException, ScriptException {
ScriptEngine engine = getEngine();
Bindings bindings = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
bindings.put("x", 42);
engine.eval("true");
boolean result = ((GraalJSScriptEngine) engine).getPolyglotContext().eval(Source.newBuilder("js", "x === 42;", "src").build()).asBoolean();
assertTrue(result);
}
@Test
public void getPolyglotContextEvalWithGlobalFail() throws IOException {
ScriptEngine engine = getEngine();
Bindings bindings = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
bindings.put("x", 42);
boolean result = ((GraalJSScriptEngine) engine).getPolyglotContext().eval(Source.newBuilder("js", "typeof x === \"undefined\";", "src").build()).asBoolean();
assertTrue(result);
}
@Test
public void getPolyglotContextEvalWithGlobalManualCall() throws IOException {
ScriptEngine engine = getEngine();
Bindings bindings = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
bindings.put("x", 42);
((GraalJSScriptEngine) engine).getPolyglotContext().getBindings("js").getMember("importScriptEngineGlobalBindings").execute(bindings);
boolean result = ((GraalJSScriptEngine) engine).getPolyglotContext().eval(Source.newBuilder("js", "x === 42;", "src").build()).asBoolean();
assertTrue(result);
}
@Test
public void invokeFunctionWithGlobal() throws ScriptException, NoSuchMethodException {
ScriptEngine engine = getEngine();
Bindings bindings = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
bindings.put("x", 42);
engine.eval("function foo() { return x === 42; }");
boolean result = (boolean) ((Invocable) engine).invokeFunction("foo");
assertTrue(result);
}
@Test
public void invokeMethodWithGlobal() throws ScriptException, NoSuchMethodException {
ScriptEngine engine = getEngine();
Bindings bindings = engine.getBindings(ScriptContext.GLOBAL_SCOPE);
bindings.put("x", 42);
engine.eval("var obj = {f: () => { return x === 42; }};");
boolean result = (boolean) ((Invocable) engine).invokeMethod(engine.eval("obj"), "f");
assertTrue(result);
}
@Test
public void factoryGetParameterTest() {
ScriptEngineFactory factory = getEngine().getFactory();
assertEquals(factory.getEngineName(), factory.getParameter(ScriptEngine.ENGINE));
assertEquals(factory.getEngineVersion(), factory.getParameter(ScriptEngine.ENGINE_VERSION));
assertEquals(factory.getLanguageName(), factory.getParameter(ScriptEngine.LANGUAGE));
assertEquals(factory.getLanguageVersion(), factory.getParameter(ScriptEngine.LANGUAGE_VERSION));
assertTrue(factory.getNames().contains(factory.getParameter(ScriptEngine.NAME)));
assertEquals(null, factory.getParameter("THREADING"));
assertEquals(null, factory.getParameter("noValueIsAssignedToThisKey"));
}
@Test
public void twoNashornEngines() throws ScriptException {
assertEquals("foo", TestUtil.getEngineNashornCompat(manager).eval("'foo'"));
assertEquals("bar", TestUtil.getEngineNashornCompat(manager).eval("'bar'"));
}
@Test
public void moduleWithDependency() throws IOException, ScriptException {
ScriptEngine engine = getEngine();
engine.getBindings(ScriptContext.ENGINE_SCOPE).put("polyglot.js.allowIO", true);
File dependency = File.createTempFile("dependency", ".mjs");
dependency.deleteOnExit();
try (FileWriter writer = new FileWriter(dependency)) {
writer.append("export let answer = 42;");
}
String mainModuleName = new File(dependency.getParent(), "main.mjs").getAbsolutePath();
String mainModule = "import { answer } from '" + dependency.getName() + "'; answer;";
engine.getContext().setAttribute(ScriptEngine.FILENAME, mainModuleName, ScriptContext.ENGINE_SCOPE);
Object result = engine.eval(mainModule);
assertSame(42, ((Number) result).intValue());
}
@Test
public void unicodeOutput() throws ScriptException {
String text = "Tu\u010d\u0148\u00e1\u010d\u010d\u00ed \ud83d\udca9!";
ScriptEngine engine = getEngine();
StringWriter output = new StringWriter();
engine.getContext().setWriter(output);
engine.eval("print('" + text + "');");
assertEquals(text + '\n', output.toString());
}
@Test
public void unicodeInput() throws ScriptException {
String text = "Tu\u010d\u0148\u00e1\u010d\u010d\u00ed \ud83d\udca9!";
ScriptEngine engine = GraalJSScriptEngine.create(
Engine.newBuilder().build(),
Context.newBuilder("js").allowExperimentalOptions(true).option("js.shell", "true"));
StringReader input = new StringReader(text);
engine.getContext().setReader(input);
Object result = engine.eval("readline()");
assertEquals(text, result);
}
@Test
public void exceptionInCauseChain() {
try {
TestUtil.getEngineNashornCompat(manager).eval("new java.util.ArrayList().iterator().next()");
fail("ScriptException/NoSuchElementException expected");
} catch (ScriptException ex) {
boolean foundNoSuchElementException = false;
Throwable cause = ex.getCause();
while (cause != null) {
if (cause instanceof NoSuchElementException) {
foundNoSuchElementException = true;
break;
}
cause = cause.getCause();
}
assertTrue(foundNoSuchElementException);
}
}
@Test
public void noNullInNashornEngine() {
ScriptEngine engine = manager.getEngineByName("nashorn");
if (engine != null) {
ScriptEngineFactory factory = engine.getFactory();
assertEquals(-1, factory.getNames().indexOf(null));
assertEquals(-1, factory.getMimeTypes().indexOf(null));
assertEquals(-1, factory.getExtensions().indexOf(null));
}
}
}