blob: 12505bf3354ee6332b9db0913466db176e3dd281 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.libs.graalsdk;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.script.Bindings;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.Assume;
import static org.junit.Assume.assumeNotNull;
import org.junit.BeforeClass;
import org.junit.Test;
import org.netbeans.api.scripting.Scripting;
public final class GraalEnginesTest {
@BeforeClass
public static void skipIfNoPolyglotFound() {
try {
Class.forName("org.graalvm.polyglot.Engine").getMethod("create").invoke(null);
} catch (ClassNotFoundException ex) {
Assume.assumeNoException("Skip if no Engine is found", ex);
} catch (ReflectiveOperationException ex) {
Assume.assumeNoException("Error when initializing Engine", ex);
}
}
@Test
public void invokeEngineViaGeneratedScriptEngine() {
ScriptEngineManager man = Scripting.createManager();
ScriptEngine llvm = man.getEngineByName("GraalVM:llvm");
assumeNotNull("Need llvm. Found: " + man.getEngineFactories(), llvm);
ScriptEngineFactory jsFactory = null;
ScriptEngineFactory graalvmJsFactory = null;
ScriptEngineFactory llvmFactory = null;
StringBuilder log = new StringBuilder();
for (ScriptEngineFactory factory : man.getEngineFactories()) {
final List<String> engineNames = factory.getNames();
final List<String> types = factory.getMimeTypes();
log.append("\nclass: ").append(factory.getClass().getSimpleName())
.append("\nnames: ").append(engineNames)
.append("\ntypes: ").append(types);
if (engineNames.contains("LLVM")) {
llvmFactory = factory;
}
if (types.contains("text/javascript")) {
if (factory.getEngineName().startsWith("GraalVM:")) {
assertNull("No previous generic GraalVM javascript factory: " + graalvmJsFactory, graalvmJsFactory);
graalvmJsFactory = factory;
} else if (!factory.getEngineName().equalsIgnoreCase("Oracle Nashorn")) {
assertNull("No previous javascript factory: " + jsFactory, jsFactory);
jsFactory = factory;
}
}
}
assertNotNull("llvm factory found: " + log, llvmFactory);
assertNotNull("js factory found: " + log, jsFactory);
assertNotNull("Generic GraalVM js factory found: " + log, graalvmJsFactory);
}
private static final String MUL =
"def mul(x, y):\n"
+ " return x * y\n"
+ "mul\n"
+ "\n";
@Test
public void pythonDirect() throws Exception {
assumeNotNull("Need python", Scripting.createManager().getEngineByMimeType("text/x-python"));
final Context ctx = Context.newBuilder().allowAllAccess(true).build();
Value mul = ctx.eval("python", MUL);
Value fourtyTwo = mul.execute(6, 7);
Assert.assertEquals("Fourty two", 42, fourtyTwo.asInt());
}
public static interface Mul {
public int multiplyTwoNumbers(int x, int y);
}
@Test
public void pythonFn() throws Exception {
ScriptEngine python = Scripting.createManager().getEngineByMimeType("text/x-python");
assumeNotNull("Need python", python);
Object rawMul = python.eval(MUL);
assertNotNull("mul created", rawMul);
assertTrue("Engines are invocable: " + python, python instanceof Invocable);
final Invocable inv = (Invocable)python;
Object res = inv.invokeFunction("mul", 6, 7);
assertNotNull("Expecting non-null", res);
assertTrue("Expecting number: " + res + " type: " + res.getClass(), res instanceof Number);
assertEquals(42, ((Number)res).intValue());
Mul mul = inv.getInterface(rawMul, Mul.class);
assertEquals(42, mul.multiplyTwoNumbers(7, 6));
}
@Test
public void javaScriptFn() throws Exception {
ScriptEngine js = Scripting.createManager().getEngineByName("GraalVM:js");
Object rawFn = js.eval("(function (x, y) { return x * y; })");
assertTrue("Engines are invocable: " + js, js instanceof Invocable);
final Invocable inv = (Invocable)js;
Mul mul = inv.getInterface(rawFn, Mul.class);
assertEquals(42, mul.multiplyTwoNumbers(7, 6));
}
@Test
public void pythonObjAccess() throws Exception {
ScriptEngine python = Scripting.createManager().getEngineByMimeType("text/x-python");
assumeNotNull("Need python", python);
Object rawPoint = python.eval(
"class O:\n" +
" x = 5\n" +
" y = -3\n" +
" def z(self, x):\n" +
" return x * x\n" +
"O()\n"
);
assertNotNull("Object created", rawPoint);
final Invocable inv = (Invocable)python;
Point point = inv.getInterface(rawPoint, Point.class);
assertNotNull("Object wrapped", point);
assertEquals(5, point.x());
assertEquals(-3, point.y(), 0.1);
assertEquals("Power of sqrt(2)", 2, point.z(1.42), 0.1);
}
@Test
public void returnArrayInPython() throws Exception {
ScriptEngine python = Scripting.createManager().getEngineByMimeType("text/x-python");
assumeNotNull("Need python", python);
python.eval("\n"
+ "import math\n"
+ "\n"
+ "def arr(x):\n"
+ " return [ 1, 2, 'a', math.pi, x ]\n"
+ "\n"
);
assertTrue("Engines are invocable: " + python, python instanceof Invocable);
final Invocable inv = (Invocable)python;
Sum sum = new Sum();
Object raw = inv.invokeFunction("arr", sum);
List<?> list = inv.getInterface(raw, List.class);
assertNotNull("List " + list, list);
assertEquals("Length of five", 5, list.size());
List<?> list2 = inv.getInterface(list, List.class);
assertNotNull("List 2 " + list2, list2);
assertEquals("Length 2 of five", 5, list2.size());
assertEquals(1, list.get(0));
assertEquals(2, list.get(1));
assertEquals("a", list.get(2));
assertEquals(Math.PI, list.get(3));
assertEquals(sum, list.get(4));
}
@Test
public void returnArrayInJS() throws Exception {
Assume.assumeFalse("Broken in GraalVM 20.3.0 fixed in GraalVM 21.1.0", "25.272-b10-jvmci-20.3-b06".equals(System.getProperty("java.vm.version")));
ScriptEngine js = Scripting.createManager().getEngineByName("GraalVM:js");
Object fn = js.eval("(function(obj) {\n"
+ " return [ 1, 2, 'a', Math.PI, obj ];\n"
+ "})\n");
assertNotNull(fn);
Sum sum = new Sum();
Object raw = ((Invocable) js).invokeMethod(fn, "call", null, sum);
List<?> list = ((Invocable) js).getInterface(raw, List.class);
assertNotNull("List " + list, list);
assertEquals("Length of five", 5, list.size());
ArrLike like = ((Invocable) js).getInterface(raw, ArrLike.class);
assertNotNull("Array like " + like, like);
assertEquals("Length of five", 5, like.length());
assertEquals(1, list.get(0));
assertEquals(2, list.get(1));
assertEquals("a", list.get(2));
assertEquals(Math.PI, list.get(3));
assertEquals(sum, list.get(4));
}
public interface ArrLike {
public int length();
}
@Test
public void returnMapInJS() throws Exception {
ScriptEngine js = Scripting.createManager().getEngineByName("GraalVM:js");
Object fn = js.eval("(function() {\n"
+ " return {\n"
+ " 'x' : 1,\n"
+ " 'y' : 2,\n"
+ " };\n"
+ "})\n");
assertNotNull(fn);
Object raw = ((Invocable) js).invokeMethod(fn, "call");
Map<?,?> map = ((Invocable) js).getInterface(raw, Map.class);
assertEquals(1, map.get("x"));
assertEquals(2, map.get("y"));
assertEquals("Expecting just two elements: " + map, 2, map.size());
}
public interface Point {
public int x();
public double y();
public double z(double a);
}
@Test
public void accessPolyglotBindings() throws Exception {
ScriptEngineManager man = Scripting.createManager();
ScriptEngine js = man.getEngineByName("GraalVM:js");
ScriptEngine python = man.getEngineByName("GraalVM:python");
assumeNotNull("Need python", python);
List<Integer> scopes = js.getContext().getScopes();
assertEquals(2, scopes.size());
assertEquals(ScriptContext.GLOBAL_SCOPE, scopes.get(1).intValue());
Bindings bindings = js.getBindings(ScriptContext.GLOBAL_SCOPE);
bindings.put("x", 42);
js.eval("\n"
+ "var x = Polyglot.import('x');\n"
+ "Polyglot.export('y', x);\n"
+ ""
);
Object y = python.eval("\n"
+ "import polyglot;\n"
+ "polyglot.import_value('y')"
);
assertTrue("Expecting number, but was: " + y, y instanceof Number);
assertEquals(42, ((Number)y).intValue());
}
public interface TwoNumbers {
public int fourtyTwo();
public int eightyOne();
}
@Test
public void accessPolyglotBindings2() throws Exception {
ScriptEngineManager man = Scripting.createManager();
ScriptEngine python = man.getEngineByName("GraalVM:python");
ScriptEngine js = man.getEngineByName("GraalVM:js");
assumeNotNull("Need python", python);
python.eval("\n"
+ "import polyglot;\n"
+ "@polyglot.export_value\n"
+ "def fourtyTwo():\n"
+ " return 42\n"
+ "\n"
);
js.eval("\n"
+ "Polyglot.export('eightyOne', function() {\n"
+ " return 81;\n"
+ "});\n"
);
TwoNumbers numbers1 = ((Invocable)python).getInterface(TwoNumbers.class);
assertEquals("Fourty two from python", 42, numbers1.fourtyTwo());
assertEquals("Eighty one from python", 81, numbers1.eightyOne());
TwoNumbers numbers2 = ((Invocable)js).getInterface(TwoNumbers.class);
assertEquals("Fourty two from JS", 42, numbers2.fourtyTwo());
assertEquals("Eighty one from JS", 81, numbers2.eightyOne());
}
/**
* Attributes that have been set up as global scope should be accessible as polyglot
* bindings. Polyglot bindings should be visible as global scope attributes.
* @throws Exception
*/
@Test
public void polyglotBindingsAsAttributes() throws Exception {
ScriptEngineManager man = Scripting.newBuilder().build();
ScriptEngine snake = man.getEngineByName("GraalVM:python");
ScriptEngine js = man.getEngineByName("GraalVM:js");
snake.getContext().setAttribute("preSnake", 1111, ScriptContext.GLOBAL_SCOPE);
js.getContext().setAttribute("preJs", 2222, ScriptContext.GLOBAL_SCOPE);
Bindings pythonBindings = snake.getBindings(ScriptContext.GLOBAL_SCOPE);
Bindings jsBindings = js.getBindings(ScriptContext.GLOBAL_SCOPE);
pythonBindings.put("ctxSnake", 3333);
jsBindings.put("ctxJs", 4444);
Object s = js.eval("var s = '' + Polyglot.import('preSnake') + Polyglot.import('preJs') + Polyglot.import('ctxSnake') + Polyglot.import('ctxJs');"
+ "Polyglot.export('s', s); s;");
assertEquals("1111222233334444", s);
assertEquals(s, js.getContext().getAttribute("s"));
assertEquals(s, js.getContext().getAttribute("s", ScriptContext.GLOBAL_SCOPE));
assertEquals(s, snake.getContext().getAttribute("s"));
assertEquals(s, snake.getContext().getAttribute("s", ScriptContext.GLOBAL_SCOPE));
}
@Test
public void hostAccessGlobalAttributeWorks() throws Exception {
ScriptEngineManager man = Scripting.createManager();
ScriptEngine js = man.getEngineByName("GraalVM:js");
js.getContext().setAttribute("allowAllAccess", true, ScriptContext.GLOBAL_SCOPE);
Object o = js.eval("var a = new java.util.ArrayList(); Polyglot.export('a', a); a;");
assertTrue(o instanceof ArrayList);
}
@Test
public void allAccessEnabledBuilder() throws Exception {
ScriptEngineManager man = Scripting.newBuilder().allowAllAccess(true).build();
ScriptEngine js = man.getEngineByName("GraalVM:js");
// consistency of builder / attribute
assertEquals(Boolean.TRUE, js.getContext().getAttribute("allowAllAccess"));
Object o = js.eval("new java.util.ArrayList();");
assertTrue(o instanceof ArrayList);
// consistency after Polyglot init:
assertEquals(Boolean.TRUE, js.getContext().getAttribute("allowAllAccess"));
// should not be exported into the language or polyglot
assertNull(js.get("allowAllAccess"));
// or its bindings
assertNull(js.getBindings(ScriptContext.GLOBAL_SCOPE).get("allowAllAccess"));
}
@Test
public void allAccessEnabledAttribute() throws Exception {
ScriptEngineManager man = Scripting.newBuilder().build();
ScriptEngine js = man.getEngineByName("GraalVM:js");
js.getContext().setAttribute("allowAllAccess", true, ScriptContext.GLOBAL_SCOPE);
Object o = js.eval("new java.util.ArrayList();");
assertTrue(o instanceof ArrayList);
// consistency after Polyglot init:
assertEquals(Boolean.TRUE, js.getContext().getAttribute("allowAllAccess"));
// should not be in engine scope
assertNull(js.get("allowAllAccess"));
// or its bindings
assertNull(js.getBindings(ScriptContext.GLOBAL_SCOPE).get("allowAllAccess"));
}
}