blob: bba4ce50240db59a64f9bb0e714da1b966c461b8 [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.api.scripting.test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail;
import java.util.Objects;
import java.util.function.Function;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.script.SimpleScriptContext;
import org.testng.Assert;
import org.testng.annotations.Test;
/**
* Tests for javax.script.Invocable implementation of nashorn.
*/
@SuppressWarnings("javadoc")
public class InvocableTest {
private static void log(final String msg) {
org.testng.Reporter.log(msg, true);
}
@Test
public void invokeMethodTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("var Example = function() { this.hello = function() { return 'Hello World!'; };}; myExample = new Example();");
final Object obj = e.get("myExample");
final Object res = ((Invocable) e).invokeMethod(obj, "hello");
assertEquals(res, "Hello World!");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* Check that we can call invokeMethod on an object that we got by
* evaluating script with different Context set.
*/
public void invokeMethodDifferentContextTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
// define an object with method on it
final Object obj = e.eval("({ hello: function() { return 'Hello World!'; } })");
final ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
e.setContext(ctxt);
// invoke 'func' on obj - but with current script context changed
final Object res = ((Invocable) e).invokeMethod(obj, "hello");
assertEquals(res, "Hello World!");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* Check that invokeMethod throws NPE on null method name.
*/
public void invokeMethodNullNameTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object obj = e.eval("({})");
((Invocable) e).invokeMethod(obj, null);
fail("should have thrown NPE");
} catch (final Exception exp) {
if (!(exp instanceof NullPointerException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that invokeMethod throws NoSuchMethodException on missing method.
*/
public void invokeMethodMissingTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object obj = e.eval("({})");
((Invocable) e).invokeMethod(obj, "nonExistentMethod");
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (!(exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling method on non-script object 'thiz' results in
* IllegalArgumentException.
*/
public void invokeMethodNonScriptObjectThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable) e).invokeMethod(new Object(), "toString");
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling method on null 'thiz' results in
* IllegalArgumentException.
*/
public void invokeMethodNullThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable) e).invokeMethod(null, "toString");
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling method on mirror created by another engine results in
* IllegalArgumentException.
*/
public void invokeMethodMixEnginesTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine engine1 = m.getEngineByName("nashorn");
final ScriptEngine engine2 = m.getEngineByName("nashorn");
try {
final Object obj = engine1.eval("({ run: function() {} })");
// pass object from engine1 to engine2 as 'thiz' for invokeMethod
((Invocable) engine2).invokeMethod(obj, "run");
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
public void getInterfaceTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final Invocable inv = (Invocable) e;
// try to get interface from global functions
try {
e.eval("function run() { print('run'); };");
final Runnable runnable = inv.getInterface(Runnable.class);
runnable.run();
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
// try interface on specific script object
try {
e.eval("var obj = { run: function() { print('run from obj'); } };");
final Object obj = e.get("obj");
final Runnable runnable = inv.getInterface(obj, Runnable.class);
runnable.run();
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
public interface Foo {
public void bar();
}
public interface Foo2 extends Foo {
public void bar2();
}
@Test
public void getInterfaceMissingTest() {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
// don't define any function.
try {
engine.eval("");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
Runnable runnable = ((Invocable) engine).getInterface(Runnable.class);
if (runnable != null) {
fail("runnable is not null!");
}
// now define "run"
try {
engine.eval("function run() { print('this is run function'); }");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
runnable = ((Invocable) engine).getInterface(Runnable.class);
// should not return null now!
runnable.run();
// define only one method of "Foo2"
try {
engine.eval("function bar() { print('bar function'); }");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
Foo2 foo2 = ((Invocable) engine).getInterface(Foo2.class);
if (foo2 != null) {
throw new RuntimeException("foo2 is not null!");
}
// now define other method of "Foo2"
try {
engine.eval("function bar2() { print('bar2 function'); }");
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
foo2 = ((Invocable) engine).getInterface(Foo2.class);
foo2.bar();
foo2.bar2();
}
@Test
/**
* Try passing non-interface Class object for interface implementation.
*/
public void getNonInterfaceGetInterfaceTest() {
final ScriptEngineManager manager = new ScriptEngineManager();
final ScriptEngine engine = manager.getEngineByName("nashorn");
try {
log(Objects.toString(((Invocable) engine).getInterface(Object.class)));
fail("Should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
fail("IllegalArgumentException expected, got " + exp);
}
}
}
@Test
/**
* Check that we can get interface out of a script object even after
* switching to use different ScriptContext.
*/
public void getInterfaceDifferentContext() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
final Object obj = e.eval("({ run: function() { } })");
// change script context
final ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
e.setContext(ctxt);
final Runnable r = ((Invocable) e).getInterface(obj, Runnable.class);
r.run();
} catch (final Exception exp) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
@Test
/**
* Check that getInterface on non-script object 'thiz' results in
* IllegalArgumentException.
*/
public void getInterfaceNonScriptObjectThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable) e).getInterface(new Object(), Runnable.class);
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that getInterface on null 'thiz' results in
* IllegalArgumentException.
*/
public void getInterfaceNullThizTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable) e).getInterface(null, Runnable.class);
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that calling getInterface on mirror created by another engine
* results in IllegalArgumentException.
*/
public void getInterfaceMixEnginesTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine engine1 = m.getEngineByName("nashorn");
final ScriptEngine engine2 = m.getEngineByName("nashorn");
try {
final Object obj = engine1.eval("({ run: function() {} })");
// pass object from engine1 to engine2 as 'thiz' for getInterface
((Invocable) engine2).getInterface(obj, Runnable.class);
fail("should have thrown IllegalArgumentException");
} catch (final Exception exp) {
if (!(exp instanceof IllegalArgumentException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* check that null function name results in NPE.
*/
public void invokeFunctionNullNameTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable)e).invokeFunction(null);
fail("should have thrown NPE");
} catch (final Exception exp) {
if (!(exp instanceof NullPointerException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that attempt to call missing function results in
* NoSuchMethodException.
*/
public void invokeFunctionMissingTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
((Invocable)e).invokeFunction("NonExistentFunc");
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (!(exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
/**
* Check that invokeFunction calls functions only from current context's
* Bindings.
*/
public void invokeFunctionDifferentContextTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
// define an object with method on it
e.eval("function hello() { return 'Hello World!'; }");
final ScriptContext ctxt = new SimpleScriptContext();
ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
// change engine's current context
e.setContext(ctxt);
((Invocable) e).invokeFunction("hello"); // no 'hello' in new context!
fail("should have thrown NoSuchMethodException");
} catch (final Exception exp) {
if (!(exp instanceof NoSuchMethodException)) {
exp.printStackTrace();
fail(exp.getMessage());
}
}
}
@Test
public void invokeFunctionExceptionTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("function func() { throw new TypeError(); }");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
try {
((Invocable) e).invokeFunction("func");
fail("should have thrown exception");
} catch (final ScriptException se) {
// ECMA TypeError property wrapped as a ScriptException
log("got " + se + " as expected");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
}
@Test
public void invokeMethodExceptionTest() {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
try {
e.eval("var sobj = {}; sobj.foo = function func() { throw new TypeError(); }");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
try {
final Object sobj = e.get("sobj");
((Invocable) e).invokeMethod(sobj, "foo");
fail("should have thrown exception");
} catch (final ScriptException se) {
// ECMA TypeError property wrapped as a ScriptException
log("got " + se + " as expected");
} catch (final Throwable t) {
t.printStackTrace();
fail(t.getMessage());
}
}
@Test
/**
* Tests whether invocation of a JavaScript method through a variable arity
* Java method will pass the vararg array. Both non-vararg and vararg
* JavaScript methods are tested.
*
* @throws ScriptException
*/
public void variableArityInterfaceTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
e.eval(
"function test1(i, strings) {"
+ " return 'i == ' + i + ', strings instanceof java.lang.String[] == ' + (strings instanceof Java.type('java.lang.String[]')) + ', strings == ' + java.util.Arrays.toString(strings)"
+ "}"
+ "function test2() {"
+ " return 'arguments[0] == ' + arguments[0] + ', arguments[1] instanceof java.lang.String[] == ' + (arguments[1] instanceof Java.type('java.lang.String[]')) + ', arguments[1] == ' + java.util.Arrays.toString(arguments[1])"
+ "}");
final VariableArityTestInterface itf = ((Invocable) e).getInterface(VariableArityTestInterface.class);
Assert.assertEquals(itf.test1(42, "a", "b"), "i == 42, strings instanceof java.lang.String[] == true, strings == [a, b]");
Assert.assertEquals(itf.test2(44, "c", "d", "e"), "arguments[0] == 44, arguments[1] instanceof java.lang.String[] == true, arguments[1] == [c, d, e]");
}
@Test
public void defaultMethodTest() throws ScriptException {
final ScriptEngineManager m = new ScriptEngineManager();
final ScriptEngine e = m.getEngineByName("nashorn");
final Invocable inv = (Invocable) e;
final Object obj = e.eval("({ apply: function(arg) { return arg.toUpperCase(); }})");
@SuppressWarnings("unchecked")
final Function<String, String> func = inv.getInterface(obj, Function.class);
assertEquals(func.apply("hello"), "HELLO");
}
}