# HG changeset patch # User sundar # Date 1377254437 -19800 # Node ID 976b3bfbd657f12427edaf1c6361d2da85e7efda # Parent 53207b2e3a7d9ed18249c0607be75834f07e75cc 8023631: engine.js init script should be loaded into every global instance created by engines Reviewed-by: attila, hannesw diff -r 53207b2e3a7d -r 976b3bfbd657 nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java --- a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Thu Aug 22 22:32:16 2013 +0530 +++ b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Fri Aug 23 16:10:37 2013 +0530 @@ -80,6 +80,7 @@ */ public static final String NASHORN_GLOBAL = "nashorn.global"; + // commonly used access control context objects private static AccessControlContext createPermAccCtxt(final String permName) { final Permissions perms = new Permissions(); perms.add(new RuntimePermission(permName)); @@ -89,18 +90,23 @@ private static final AccessControlContext CREATE_CONTEXT_ACC_CTXT = createPermAccCtxt(Context.NASHORN_CREATE_CONTEXT); private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(Context.NASHORN_CREATE_GLOBAL); + // the factory that created this engine private final ScriptEngineFactory factory; + // underlying nashorn Context - 1:1 with engine instance private final Context nashornContext; // do we want to share single Nashorn global instance across ENGINE_SCOPEs? private final boolean _global_per_engine; + // This is the initial default Nashorn global object. + // This is used as "shared" global if above option is true. private final ScriptObject global; - // initialized bit late to be made 'final'. Property object for "context" - // property of global object - private Property contextProperty; + // initialized bit late to be made 'final'. + // Property object for "context" property of global object. + private volatile Property contextProperty; // default options passed to Nashorn Options object private static final String[] DEFAULT_OPTIONS = new String[] { "-scripting", "-doe" }; + // Nashorn script engine error message management private static final String MESSAGES_RESOURCE = "jdk.nashorn.api.scripting.resources.Messages"; private static final ResourceBundle MESSAGES_BUNDLE; @@ -108,6 +114,7 @@ MESSAGES_BUNDLE = ResourceBundle.getBundle(MESSAGES_RESOURCE, Locale.getDefault()); } + // helper to get Nashorn script engine error message private static String getMessage(final String msgId, final String... args) { try { return new MessageFormat(MESSAGES_BUNDLE.getString(msgId)).format(args); @@ -116,6 +123,30 @@ } } + // load engine.js and return content as a char[] + private static char[] loadEngineJSSource() { + final String script = "resources/engine.js"; + try { + final InputStream is = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + @Override + public InputStream run() throws Exception { + final URL url = NashornScriptEngine.class.getResource(script); + return url.openStream(); + } + }); + return Source.readFully(new InputStreamReader(is)); + } catch (final PrivilegedActionException | IOException e) { + if (Context.DEBUG) { + e.printStackTrace(); + } + throw new RuntimeException(e); + } + } + + // Source object for engine.js + private static final Source ENGINE_SCRIPT_SRC = new Source(NashornException.ENGINE_SCRIPT_SOURCE_NAME, loadEngineJSSource()); + NashornScriptEngine(final NashornScriptEngineFactory factory, final ClassLoader appLoader) { this(factory, DEFAULT_OPTIONS, appLoader); } @@ -146,19 +177,9 @@ this._global_per_engine = nashornContext.getEnv()._global_per_engine; // create new global object - this.global = createNashornGlobal(); - // set the default engine scope for the default context + this.global = createNashornGlobal(context); + // set the default ENGINE_SCOPE object for the default context context.setBindings(new ScriptObjectMirror(global, global), ScriptContext.ENGINE_SCOPE); - - // evaluate engine initial script - try { - evalEngineScript(); - } catch (final ScriptException e) { - if (Context.DEBUG) { - e.printStackTrace(); - } - throw new RuntimeException(e); - } } @Override @@ -192,8 +213,7 @@ // We use same 'global' for all Bindings. return new SimpleBindings(); } else { - final ScriptObject newGlobal = createNashornGlobal(); - return new ScriptObjectMirror(newGlobal, newGlobal); + return createGlobalMirror(null); } } @@ -230,6 +250,48 @@ return invokeImpl(thiz, name, args); } + @Override + public T getInterface(final Class clazz) { + return getInterfaceInner(null, clazz); + } + + @Override + public T getInterface(final Object thiz, final Class clazz) { + if (thiz == null) { + throw new IllegalArgumentException(getMessage("thiz.cannot.be.null")); + } + return getInterfaceInner(thiz, clazz); + } + + // These are called from the "engine.js" script + + /** + * This hook is used to search js global variables exposed from Java code. + * + * @param self 'this' passed from the script + * @param ctxt current ScriptContext in which name is searched + * @param name name of the variable searched + * @return the value of the named variable + */ + public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) { + if (ctxt != null) { + final int scope = ctxt.getAttributesScope(name); + final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt); + if (scope != -1) { + return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), ctxtGlobal); + } + + if (self == UNDEFINED) { + // scope access and so throw ReferenceError + throw referenceError(ctxtGlobal, "not.defined", name); + } + } + + return UNDEFINED; + } + + // Implementation only below this point + private T getInterfaceInner(final Object thiz, final Class clazz) { if (clazz == null || !clazz.isInterface()) { throw new IllegalArgumentException(getMessage("interface.class.expected")); @@ -297,47 +359,10 @@ } } - @Override - public T getInterface(final Class clazz) { - return getInterfaceInner(null, clazz); - } - - @Override - public T getInterface(final Object thiz, final Class clazz) { - if (thiz == null) { - throw new IllegalArgumentException(getMessage("thiz.cannot.be.null")); - } - return getInterfaceInner(thiz, clazz); - } - - // These are called from the "engine.js" script - - /** - * This hook is used to search js global variables exposed from Java code. - * - * @param self 'this' passed from the script - * @param ctxt current ScriptContext in which name is searched - * @param name name of the variable searched - * @return the value of the named variable - */ - public Object __noSuchProperty__(final Object self, final ScriptContext ctxt, final String name) { - final int scope = ctxt.getAttributesScope(name); - final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt); - if (scope != -1) { - return ScriptObjectMirror.unwrap(ctxt.getAttribute(name, scope), ctxtGlobal); - } - - if (self == UNDEFINED) { - // scope access and so throw ReferenceError - throw referenceError(ctxtGlobal, "not.defined", name); - } - - return UNDEFINED; - } - + // Retrieve nashorn Global object for a given ScriptContext object private ScriptObject getNashornGlobalFrom(final ScriptContext ctxt) { if (_global_per_engine) { - // shared single global for all ENGINE_SCOPE Bindings + // shared single global object for all ENGINE_SCOPE Bindings return global; } @@ -361,11 +386,12 @@ // We didn't find associated nashorn global mirror in the Bindings given! // Create new global instance mirror and associate with the Bindings. - final ScriptObjectMirror mirror = (ScriptObjectMirror)createBindings(); + final ScriptObjectMirror mirror = createGlobalMirror(ctxt); bindings.put(NASHORN_GLOBAL, mirror); return mirror.getScriptObject(); } + // Retrieve nashorn Global object from a given ScriptObjectMirror private ScriptObject globalFromMirror(final ScriptObjectMirror mirror) { ScriptObject sobj = mirror.getScriptObject(); if (sobj instanceof GlobalObject && sobj.isOfContext(nashornContext)) { @@ -375,7 +401,14 @@ return null; } - private ScriptObject createNashornGlobal() { + // Create a new ScriptObjectMirror wrapping a newly created Nashorn Global object + private ScriptObjectMirror createGlobalMirror(final ScriptContext ctxt) { + final ScriptObject newGlobal = createNashornGlobal(ctxt); + return new ScriptObjectMirror(newGlobal, newGlobal); + } + + // Create a new Nashorn Global object + private ScriptObject createNashornGlobal(final ScriptContext ctxt) { final ScriptObject newGlobal = AccessController.doPrivileged(new PrivilegedAction() { @Override public ScriptObject run() { @@ -396,7 +429,7 @@ // current ScriptContext exposed as "context" // "context" is non-writable from script - but script engine still // needs to set it and so save the context Property object - contextProperty = newGlobal.addOwnProperty("context", NON_ENUMERABLE_CONSTANT, UNDEFINED); + contextProperty = newGlobal.addOwnProperty("context", NON_ENUMERABLE_CONSTANT, null); // current ScriptEngine instance exposed as "engine". We added @SuppressWarnings("LeakingThisInConstructor") as // NetBeans identifies this assignment as such a leak - this is a false positive as we're setting this property // in the Global of a Context we just created - both the Context and the Global were just created and can not be @@ -406,38 +439,17 @@ newGlobal.addOwnProperty("arguments", Property.NOT_ENUMERABLE, UNDEFINED); // file name default is null newGlobal.addOwnProperty(ScriptEngine.FILENAME, Property.NOT_ENUMERABLE, null); + // evaluate engine.js initialization script this new global object + try { + evalImpl(compileImpl(ENGINE_SCRIPT_SRC, newGlobal), ctxt, newGlobal); + } catch (final ScriptException exp) { + throw new RuntimeException(exp); + } return newGlobal; } - private void evalEngineScript() throws ScriptException { - final String script = "resources/engine.js"; - final String name = NashornException.ENGINE_SCRIPT_SOURCE_NAME; - try { - final InputStream is = AccessController.doPrivileged( - new PrivilegedExceptionAction() { - @Override - public InputStream run() throws Exception { - final URL url = NashornScriptEngine.class.getResource(script); - return url.openStream(); - } - }); - put(ScriptEngine.FILENAME, name); - try (final InputStreamReader isr = new InputStreamReader(is)) { - eval(isr); - } - } catch (final PrivilegedActionException | IOException e) { - if (Context.DEBUG) { - e.printStackTrace(); - } - throw new ScriptException(e); - } finally { - put(ScriptEngine.FILENAME, null); - } - } - - // scripts should see "context" and "engine" as variables - private void setContextVariables(final ScriptContext ctxt) { - final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt); + // scripts should see "context" and "engine" as variables in the given global object + private void setContextVariables(final ScriptObject ctxtGlobal, final ScriptContext ctxt) { // set "context" global variable via contextProperty - because this // property is non-writable contextProperty.setObjectValue(ctxtGlobal, ctxtGlobal, ctxt, false); @@ -502,18 +514,24 @@ } private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt) throws ScriptException { + return evalImpl(script, ctxt, getNashornGlobalFrom(ctxt)); + } + + private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final ScriptObject ctxtGlobal) throws ScriptException { if (script == null) { return null; } final ScriptObject oldGlobal = Context.getGlobal(); - final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt); final boolean globalChanged = (oldGlobal != ctxtGlobal); try { if (globalChanged) { Context.setGlobal(ctxtGlobal); } - setContextVariables(ctxt); + // set ScriptContext variables if ctxt is non-null + if (ctxt != null) { + setContextVariables(ctxtGlobal, ctxt); + } return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal)); } catch (final Exception e) { throwAsScriptException(e); @@ -563,15 +581,18 @@ } private ScriptFunction compileImpl(final Source source, final ScriptContext ctxt) throws ScriptException { + return compileImpl(source, getNashornGlobalFrom(ctxt)); + } + + private ScriptFunction compileImpl(final Source source, final ScriptObject newGlobal) throws ScriptException { final ScriptObject oldGlobal = Context.getGlobal(); - final ScriptObject ctxtGlobal = getNashornGlobalFrom(ctxt); - final boolean globalChanged = (oldGlobal != ctxtGlobal); + final boolean globalChanged = (oldGlobal != newGlobal); try { if (globalChanged) { - Context.setGlobal(ctxtGlobal); + Context.setGlobal(newGlobal); } - return nashornContext.compileScript(source, ctxtGlobal); + return nashornContext.compileScript(source, newGlobal); } catch (final Exception e) { throwAsScriptException(e); throw new AssertionError("should not reach here"); diff -r 53207b2e3a7d -r 976b3bfbd657 nashorn/src/jdk/nashorn/api/scripting/resources/engine.js --- a/nashorn/src/jdk/nashorn/api/scripting/resources/engine.js Thu Aug 22 22:32:16 2013 +0530 +++ b/nashorn/src/jdk/nashorn/api/scripting/resources/engine.js Fri Aug 23 16:10:37 2013 +0530 @@ -23,10 +23,9 @@ /** * This script file is executed by script engine at the construction - * of the engine. The functions here assume global variables "context" - * of type javax.script.ScriptContext and "engine" of the type + * of the every new Global object. The functions here assume global variables + * "context" of type javax.script.ScriptContext and "engine" of the type * jdk.nashorn.api.scripting.NashornScriptEngine. - * **/ Object.defineProperty(this, "__noSuchProperty__", { @@ -40,7 +39,7 @@ }); function print() { - var writer = context.getWriter(); + var writer = context != null? context.writer : engine.context.writer; if (! (writer instanceof java.io.PrintWriter)) { writer = new java.io.PrintWriter(writer); } diff -r 53207b2e3a7d -r 976b3bfbd657 nashorn/test/src/jdk/nashorn/api/scripting/InvocableTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/src/jdk/nashorn/api/scripting/InvocableTest.java Fri Aug 23 16:10:37 2013 +0530 @@ -0,0 +1,525 @@ +/* + * 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; + +import java.util.Objects; +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 static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; +import org.testng.annotations.Test; + +/** + * Tests for javax.script.Invocable implementation of nashorn. + */ +public class InvocableTest { + + private void log(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() { + ScriptEngineManager m = new ScriptEngineManager(); + ScriptEngine e = m.getEngineByName("nashorn"); + + try { + // define an object with method on it + 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("({})"); + final Object res = ((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("({})"); + final Object res = ((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 { + 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'); } };"); + 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() { + ScriptEngineManager m = new ScriptEngineManager(); + ScriptEngine e = m.getEngineByName("nashorn"); + try { + Object obj = e.eval("({ run: function() { } })"); + + // change script context + ScriptContext ctxt = new SimpleScriptContext(); + ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE); + e.setContext(ctxt); + + 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 { + 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 { + final Object res = ((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 { + final Object res = ((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() { + ScriptEngineManager m = new ScriptEngineManager(); + ScriptEngine e = m.getEngineByName("nashorn"); + + try { + // define an object with method on it + Object obj = 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]"); + } +} diff -r 53207b2e3a7d -r 976b3bfbd657 nashorn/test/src/jdk/nashorn/api/scripting/ScopeTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScopeTest.java Fri Aug 23 16:10:37 2013 +0530 @@ -0,0 +1,248 @@ +/* + * 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; + +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import javax.script.SimpleBindings; +import javax.script.SimpleScriptContext; +import org.testng.Assert; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import org.testng.annotations.Test; + +/** + * Tests for jsr223 Bindings "scope" (engine, global scopes) + */ +public class ScopeTest { + + @Test + public void createBindingsTest() { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + Bindings b = e.createBindings(); + b.put("foo", 42.0); + Object res = null; + try { + res = e.eval("foo == 42.0", b); + } catch (final ScriptException | NullPointerException se) { + se.printStackTrace(); + fail(se.getMessage()); + } + + assertEquals(res, Boolean.TRUE); + } + + @Test + public void engineScopeTest() { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + Bindings engineScope = e.getBindings(ScriptContext.ENGINE_SCOPE); + + // check few ECMA standard built-in global properties + assertNotNull(engineScope.get("Object")); + assertNotNull(engineScope.get("TypeError")); + assertNotNull(engineScope.get("eval")); + + // can access via ScriptEngine.get as well + assertNotNull(e.get("Object")); + assertNotNull(e.get("TypeError")); + assertNotNull(e.get("eval")); + + // Access by either way should return same object + assertEquals(engineScope.get("Array"), e.get("Array")); + assertEquals(engineScope.get("EvalError"), e.get("EvalError")); + assertEquals(engineScope.get("undefined"), e.get("undefined")); + + // try exposing a new variable from scope + engineScope.put("myVar", "foo"); + try { + assertEquals(e.eval("myVar"), "foo"); + } catch (final ScriptException se) { + se.printStackTrace(); + fail(se.getMessage()); + } + + // update "myVar" in script an check the value from scope + try { + e.eval("myVar = 'nashorn';"); + } catch (final ScriptException se) { + se.printStackTrace(); + fail(se.getMessage()); + } + + // now check modified value from scope and engine + assertEquals(engineScope.get("myVar"), "nashorn"); + assertEquals(e.get("myVar"), "nashorn"); + } + + @Test + public void multiGlobalTest() { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final Bindings b = e.createBindings(); + final ScriptContext newCtxt = new SimpleScriptContext(); + newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); + + try { + Object obj1 = e.eval("Object"); + Object obj2 = e.eval("Object", newCtxt); + Assert.assertNotEquals(obj1, obj2); + Assert.assertNotNull(obj1); + Assert.assertNotNull(obj2); + Assert.assertEquals(obj1.toString(), obj2.toString()); + + e.eval("x = 'hello'"); + e.eval("x = 'world'", newCtxt); + Object x1 = e.getContext().getAttribute("x"); + Object x2 = newCtxt.getAttribute("x"); + Assert.assertNotEquals(x1, x2); + Assert.assertEquals(x1, "hello"); + Assert.assertEquals(x2, "world"); + + x1 = e.eval("x"); + x2 = e.eval("x", newCtxt); + Assert.assertNotEquals(x1, x2); + Assert.assertEquals(x1, "hello"); + Assert.assertEquals(x2, "world"); + + final ScriptContext origCtxt = e.getContext(); + e.setContext(newCtxt); + e.eval("y = new Object()"); + e.eval("y = new Object()", origCtxt); + + Object y1 = origCtxt.getAttribute("y"); + Object y2 = newCtxt.getAttribute("y"); + Assert.assertNotEquals(y1, y2); + Assert.assertNotEquals(e.eval("y"), e.eval("y", origCtxt)); + Assert.assertEquals("[object Object]", y1.toString()); + Assert.assertEquals("[object Object]", y2.toString()); + } catch (final ScriptException se) { + se.printStackTrace(); + fail(se.getMessage()); + } + } + + @Test + public void userEngineScopeBindingsTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + e.eval("function func() {}"); + + final ScriptContext newContext = new SimpleScriptContext(); + newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); + // we are using a new bindings - so it should have 'func' defined + Object value = e.eval("typeof func", newContext); + assertTrue(value.equals("undefined")); + } + + @Test + public void userEngineScopeBindingsNoLeakTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final ScriptContext newContext = new SimpleScriptContext(); + newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); + e.eval("function foo() {}", newContext); + + // in the default context's ENGINE_SCOPE, 'foo' shouldn't exist + assertTrue(e.eval("typeof foo").equals("undefined")); + } + + @Test + public void userEngineScopeBindingsRetentionTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final ScriptContext newContext = new SimpleScriptContext(); + newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); + e.eval("function foo() {}", newContext); + + // definition retained with user's ENGINE_SCOPE Binding + assertTrue(e.eval("typeof foo", newContext).equals("function")); + + final Bindings oldBindings = newContext.getBindings(ScriptContext.ENGINE_SCOPE); + // but not in another ENGINE_SCOPE binding + newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); + assertTrue(e.eval("typeof foo", newContext).equals("undefined")); + + // restore ENGINE_SCOPE and check again + newContext.setBindings(oldBindings, ScriptContext.ENGINE_SCOPE); + assertTrue(e.eval("typeof foo", newContext).equals("function")); + } + + @Test + // check that engine.js definitions are visible in all new global instances + public void checkBuiltinsInNewBindingsTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + + // check default global instance has engine.js definitions + final Bindings g = (Bindings) e.eval("this"); + Object value = g.get("__noSuchProperty__"); + assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction()); + value = g.get("print"); + assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction()); + + // check new global instance created has engine.js definitions + Bindings b = e.createBindings(); + value = b.get("__noSuchProperty__"); + assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction()); + value = b.get("print"); + assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction()); + + // put a mapping into GLOBAL_SCOPE + final Bindings globalScope = e.getContext().getBindings(ScriptContext.GLOBAL_SCOPE); + globalScope.put("x", "hello"); + + // GLOBAL_SCOPE mapping should be visible from default ScriptContext eval + assertTrue(e.eval("x").equals("hello")); + + final ScriptContext ctx = new SimpleScriptContext(); + ctx.setBindings(globalScope, ScriptContext.GLOBAL_SCOPE); + ctx.setBindings(b, ScriptContext.ENGINE_SCOPE); + + // GLOBAL_SCOPE mapping should be visible from non-default ScriptContext eval + assertTrue(e.eval("x", ctx).equals("hello")); + + // try some arbitray Bindings for ENGINE_SCOPE + Bindings sb = new SimpleBindings(); + ctx.setBindings(sb, ScriptContext.ENGINE_SCOPE); + + // GLOBAL_SCOPE mapping should be visible from non-default ScriptContext eval + assertTrue(e.eval("x", ctx).equals("hello")); + + // engine.js builtins are still defined even with arbitrary Bindings + assertTrue(e.eval("typeof print", ctx).equals("function")); + assertTrue(e.eval("typeof __noSuchProperty__", ctx).equals("function")); + + // ENGINE_SCOPE definition should 'hide' GLOBAL_SCOPE definition + sb.put("x", "newX"); + assertTrue(e.eval("x", ctx).equals("newX")); + } +} diff -r 53207b2e3a7d -r 976b3bfbd657 nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java --- a/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java Thu Aug 22 22:32:16 2013 +0530 +++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptEngineTest.java Fri Aug 23 16:10:37 2013 +0530 @@ -26,7 +26,6 @@ package jdk.nashorn.api.scripting; import static org.testng.Assert.assertEquals; -import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; @@ -34,22 +33,13 @@ import java.io.StringReader; import java.io.StringWriter; import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; import java.util.concurrent.Callable; -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.ScriptEngineManager; import javax.script.ScriptException; -import javax.script.SimpleBindings; -import javax.script.SimpleScriptContext; -import org.testng.Assert; import org.testng.annotations.Test; /** @@ -240,214 +230,6 @@ } @Test - public void createBindingsTest() { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - Bindings b = e.createBindings(); - b.put("foo", 42.0); - Object res = null; - try { - res = e.eval("foo == 42.0", b); - } catch (final ScriptException | NullPointerException se) { - se.printStackTrace(); - fail(se.getMessage()); - } - - assertEquals(res, Boolean.TRUE); - } - - @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'); } };"); - 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() { - ScriptEngineManager m = new ScriptEngineManager(); - ScriptEngine e = m.getEngineByName("nashorn"); - try { - Object obj = e.eval("({ run: function() { } })"); - - // change script context - ScriptContext ctxt = new SimpleScriptContext(); - ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE); - e.setContext(ctxt); - - 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 { - 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 public void accessGlobalTest() { final ScriptEngineManager m = new ScriptEngineManager(); final ScriptEngine e = m.getEngineByName("nashorn"); @@ -622,88 +404,6 @@ assertEquals(sw.toString().replaceAll("\r", ""), "hello world\n"); } - @SuppressWarnings("unchecked") - @Test - public void reflectionTest() throws ScriptException { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - - e.eval("var obj = { x: 344, y: 'nashorn' }"); - - int count = 0; - Map map = (Map)e.get("obj"); - assertFalse(map.isEmpty()); - assertTrue(map.keySet().contains("x")); - assertTrue(map.containsKey("x")); - assertTrue(map.values().contains("nashorn")); - assertTrue(map.containsValue("nashorn")); - for (final Map.Entry ex : map.entrySet()) { - final Object key = ex.getKey(); - if (key.equals("x")) { - assertTrue(344 == ((Number)ex.getValue()).doubleValue()); - count++; - } else if (key.equals("y")) { - assertEquals(ex.getValue(), "nashorn"); - count++; - } - } - assertEquals(2, count); - assertEquals(2, map.size()); - - // add property - map.put("z", "hello"); - assertEquals(e.eval("obj.z"), "hello"); - assertEquals(map.get("z"), "hello"); - assertTrue(map.keySet().contains("z")); - assertTrue(map.containsKey("z")); - assertTrue(map.values().contains("hello")); - assertTrue(map.containsValue("hello")); - assertEquals(map.size(), 3); - - final Map newMap = new HashMap<>(); - newMap.put("foo", 23.0); - newMap.put("bar", true); - map.putAll(newMap); - - assertEquals(e.eval("obj.foo"), 23.0); - assertEquals(e.eval("obj.bar"), true); - - // remove using map method - map.remove("foo"); - assertEquals(e.eval("typeof obj.foo"), "undefined"); - - count = 0; - e.eval("var arr = [ true, 'hello' ]"); - map = (Map)e.get("arr"); - assertFalse(map.isEmpty()); - assertTrue(map.containsKey("length")); - assertTrue(map.containsValue("hello")); - for (final Map.Entry ex : map.entrySet()) { - final Object key = ex.getKey(); - if (key.equals("0")) { - assertEquals(ex.getValue(), Boolean.TRUE); - count++; - } else if (key.equals("1")) { - assertEquals(ex.getValue(), "hello"); - count++; - } - } - assertEquals(count, 2); - assertEquals(map.size(), 2); - - // add element - map.put("2", "world"); - assertEquals(map.get("2"), "world"); - assertEquals(map.size(), 3); - - // remove all - map.clear(); - assertTrue(map.isEmpty()); - assertEquals(e.eval("typeof arr[0]"), "undefined"); - assertEquals(e.eval("typeof arr[1]"), "undefined"); - assertEquals(e.eval("typeof arr[2]"), "undefined"); - } - @Test public void redefineEchoTest() { final ScriptEngineManager m = new ScriptEngineManager(); @@ -716,150 +416,6 @@ fail(exp.getMessage()); } } - - @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() { - ScriptEngineManager m = new ScriptEngineManager(); - ScriptEngine e = m.getEngineByName("nashorn"); - - try { - // define an object with method on it - 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("({})"); - final Object res = ((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("({})"); - final Object res = ((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 { - 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 noEnumerablePropertiesTest() { final ScriptEngineManager m = new ScriptEngineManager(); @@ -921,308 +477,6 @@ } @Test - public void jsobjectTest() { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - try { - e.eval("var obj = { '1': 'world', func: function() { return this.bar; }, bar: 'hello' }"); - JSObject obj = (JSObject) e.get("obj"); - - // try basic get on existing properties - if (! obj.getMember("bar").equals("hello")) { - fail("obj.bar != 'hello'"); - } - - if (! obj.getSlot(1).equals("world")) { - fail("obj[1] != 'world'"); - } - - if (! obj.call("func", new Object[0]).equals("hello")) { - fail("obj.call('func') != 'hello'"); - } - - // try setting properties - obj.setMember("bar", "new-bar"); - obj.setSlot(1, "new-element-1"); - if (! obj.getMember("bar").equals("new-bar")) { - fail("obj.bar != 'new-bar'"); - } - - if (! obj.getSlot(1).equals("new-element-1")) { - fail("obj[1] != 'new-element-1'"); - } - - // try adding properties - obj.setMember("prop", "prop-value"); - obj.setSlot(12, "element-12"); - if (! obj.getMember("prop").equals("prop-value")) { - fail("obj.prop != 'prop-value'"); - } - - if (! obj.getSlot(12).equals("element-12")) { - fail("obj[12] != 'element-12'"); - } - - // delete properties - obj.removeMember("prop"); - if ("prop-value".equals(obj.getMember("prop"))) { - fail("obj.prop is not deleted!"); - } - - // Simple eval tests - assertEquals(obj.eval("typeof Object"), "function"); - assertEquals(obj.eval("'nashorn'.substring(3)"), "horn"); - } catch (final Exception exp) { - 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 { - final Object res = ((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 { - final Object res = ((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() { - ScriptEngineManager m = new ScriptEngineManager(); - ScriptEngine e = m.getEngineByName("nashorn"); - - try { - // define an object with method on it - Object obj = 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 - public void scriptObjectMirrorToStringTest() { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - try { - Object obj = e.eval("new TypeError('wrong type')"); - assertEquals(obj.toString(), "TypeError: wrong type", "toString returns wrong value"); - } catch (final Throwable t) { - t.printStackTrace(); - fail(t.getMessage()); - } - - try { - Object obj = e.eval("function func() { print('hello'); }"); - assertEquals(obj.toString(), "function func() { print('hello'); }", "toString returns wrong value"); - } catch (final Throwable t) { - t.printStackTrace(); - fail(t.getMessage()); - } - } - - @Test - public void engineScopeTest() { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - Bindings engineScope = e.getBindings(ScriptContext.ENGINE_SCOPE); - - // check few ECMA standard built-in global properties - assertNotNull(engineScope.get("Object")); - assertNotNull(engineScope.get("TypeError")); - assertNotNull(engineScope.get("eval")); - - // can access via ScriptEngine.get as well - assertNotNull(e.get("Object")); - assertNotNull(e.get("TypeError")); - assertNotNull(e.get("eval")); - - // Access by either way should return same object - assertEquals(engineScope.get("Array"), e.get("Array")); - assertEquals(engineScope.get("EvalError"), e.get("EvalError")); - assertEquals(engineScope.get("undefined"), e.get("undefined")); - - // try exposing a new variable from scope - engineScope.put("myVar", "foo"); - try { - assertEquals(e.eval("myVar"), "foo"); - } catch (final ScriptException se) { - se.printStackTrace(); - fail(se.getMessage()); - } - - // update "myVar" in script an check the value from scope - try { - e.eval("myVar = 'nashorn';"); - } catch (final ScriptException se) { - se.printStackTrace(); - fail(se.getMessage()); - } - - // now check modified value from scope and engine - assertEquals(engineScope.get("myVar"), "nashorn"); - assertEquals(e.get("myVar"), "nashorn"); - } - - @Test - public void multiGlobalTest() { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - final Bindings b = e.createBindings(); - final ScriptContext newCtxt = new SimpleScriptContext(); - newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); - - try { - Object obj1 = e.eval("Object"); - Object obj2 = e.eval("Object", newCtxt); - Assert.assertNotEquals(obj1, obj2); - Assert.assertNotNull(obj1); - Assert.assertNotNull(obj2); - Assert.assertEquals(obj1.toString(), obj2.toString()); - - e.eval("x = 'hello'"); - e.eval("x = 'world'", newCtxt); - Object x1 = e.getContext().getAttribute("x"); - Object x2 = newCtxt.getAttribute("x"); - Assert.assertNotEquals(x1, x2); - Assert.assertEquals(x1, "hello"); - Assert.assertEquals(x2, "world"); - - x1 = e.eval("x"); - x2 = e.eval("x", newCtxt); - Assert.assertNotEquals(x1, x2); - Assert.assertEquals(x1, "hello"); - Assert.assertEquals(x2, "world"); - - final ScriptContext origCtxt = e.getContext(); - e.setContext(newCtxt); - e.eval("y = new Object()"); - e.eval("y = new Object()", origCtxt); - - Object y1 = origCtxt.getAttribute("y"); - Object y2 = newCtxt.getAttribute("y"); - Assert.assertNotEquals(y1, y2); - Assert.assertNotEquals(e.eval("y"), e.eval("y", origCtxt)); - Assert.assertEquals("[object Object]", y1.toString()); - Assert.assertEquals("[object Object]", y2.toString()); - } catch (final ScriptException se) { - se.printStackTrace(); - fail(se.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 // check that print function prints arg followed by newline char public void printTest() { final ScriptEngineManager m = new ScriptEngineManager(); @@ -1257,76 +511,4 @@ // dos2unix - fix line endings if running on windows assertEquals(sw.toString().replaceAll("\r", ""), "34 true hello\n"); } - - @Test - public void mirrorNewObjectGlobalFunctionTest() throws ScriptException { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - final ScriptEngine e2 = m.getEngineByName("nashorn"); - - e.eval("function func() {}"); - e2.put("foo", e.get("func")); - final Object e2global = e2.eval("this"); - final Object newObj = ((ScriptObjectMirror)e2global).newObject("foo"); - assertTrue(newObj instanceof ScriptObjectMirror); - } - - @Test - public void mirrorNewObjectInstanceFunctionTest() throws ScriptException { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - final ScriptEngine e2 = m.getEngineByName("nashorn"); - - e.eval("function func() {}"); - e2.put("func", e.get("func")); - final Object e2obj = e2.eval("({ foo: func })"); - final Object newObj = ((ScriptObjectMirror)e2obj).newObject("foo"); - assertTrue(newObj instanceof ScriptObjectMirror); - } - - @Test - public void userEngineScopeBindingsTest() throws ScriptException { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - e.eval("function func() {}"); - - final ScriptContext newContext = new SimpleScriptContext(); - newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); - // we are using a new bindings - so it should have 'func' defined - Object value = e.eval("typeof func", newContext); - assertTrue(value.equals("undefined")); - } - - @Test - public void userEngineScopeBindingsNoLeakTest() throws ScriptException { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - final ScriptContext newContext = new SimpleScriptContext(); - newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); - e.eval("function foo() {}", newContext); - - // in the default context's ENGINE_SCOPE, 'foo' shouldn't exist - assertTrue(e.eval("typeof foo").equals("undefined")); - } - - @Test - public void userEngineScopeBindingsRetentionTest() throws ScriptException { - final ScriptEngineManager m = new ScriptEngineManager(); - final ScriptEngine e = m.getEngineByName("nashorn"); - final ScriptContext newContext = new SimpleScriptContext(); - newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); - e.eval("function foo() {}", newContext); - - // definition retained with user's ENGINE_SCOPE Binding - assertTrue(e.eval("typeof foo", newContext).equals("function")); - - final Bindings oldBindings = newContext.getBindings(ScriptContext.ENGINE_SCOPE); - // but not in another ENGINE_SCOPE binding - newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); - assertTrue(e.eval("typeof foo", newContext).equals("undefined")); - - // restore ENGINE_SCOPE and check again - newContext.setBindings(oldBindings, ScriptContext.ENGINE_SCOPE); - assertTrue(e.eval("typeof foo", newContext).equals("function")); - } } diff -r 53207b2e3a7d -r 976b3bfbd657 nashorn/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/nashorn/test/src/jdk/nashorn/api/scripting/ScriptObjectMirrorTest.java Fri Aug 23 16:10:37 2013 +0530 @@ -0,0 +1,230 @@ +/* + * 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; + +import java.util.HashMap; +import java.util.Map; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; +import static org.testng.Assert.fail; +import org.testng.annotations.Test; + +/** + * Tests to check jdk.nashorn.api.scripting.ScriptObjectMirror API. + */ +public class ScriptObjectMirrorTest { + + @SuppressWarnings("unchecked") + @Test + public void reflectionTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + + e.eval("var obj = { x: 344, y: 'nashorn' }"); + + int count = 0; + Map map = (Map) e.get("obj"); + assertFalse(map.isEmpty()); + assertTrue(map.keySet().contains("x")); + assertTrue(map.containsKey("x")); + assertTrue(map.values().contains("nashorn")); + assertTrue(map.containsValue("nashorn")); + for (final Map.Entry ex : map.entrySet()) { + final Object key = ex.getKey(); + if (key.equals("x")) { + assertTrue(344 == ((Number) ex.getValue()).doubleValue()); + count++; + } else if (key.equals("y")) { + assertEquals(ex.getValue(), "nashorn"); + count++; + } + } + assertEquals(2, count); + assertEquals(2, map.size()); + + // add property + map.put("z", "hello"); + assertEquals(e.eval("obj.z"), "hello"); + assertEquals(map.get("z"), "hello"); + assertTrue(map.keySet().contains("z")); + assertTrue(map.containsKey("z")); + assertTrue(map.values().contains("hello")); + assertTrue(map.containsValue("hello")); + assertEquals(map.size(), 3); + + final Map newMap = new HashMap<>(); + newMap.put("foo", 23.0); + newMap.put("bar", true); + map.putAll(newMap); + + assertEquals(e.eval("obj.foo"), 23.0); + assertEquals(e.eval("obj.bar"), true); + + // remove using map method + map.remove("foo"); + assertEquals(e.eval("typeof obj.foo"), "undefined"); + + count = 0; + e.eval("var arr = [ true, 'hello' ]"); + map = (Map) e.get("arr"); + assertFalse(map.isEmpty()); + assertTrue(map.containsKey("length")); + assertTrue(map.containsValue("hello")); + for (final Map.Entry ex : map.entrySet()) { + final Object key = ex.getKey(); + if (key.equals("0")) { + assertEquals(ex.getValue(), Boolean.TRUE); + count++; + } else if (key.equals("1")) { + assertEquals(ex.getValue(), "hello"); + count++; + } + } + assertEquals(count, 2); + assertEquals(map.size(), 2); + + // add element + map.put("2", "world"); + assertEquals(map.get("2"), "world"); + assertEquals(map.size(), 3); + + // remove all + map.clear(); + assertTrue(map.isEmpty()); + assertEquals(e.eval("typeof arr[0]"), "undefined"); + assertEquals(e.eval("typeof arr[1]"), "undefined"); + assertEquals(e.eval("typeof arr[2]"), "undefined"); + } + + @Test + public void jsobjectTest() { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + try { + e.eval("var obj = { '1': 'world', func: function() { return this.bar; }, bar: 'hello' }"); + JSObject obj = (JSObject) e.get("obj"); + + // try basic get on existing properties + if (!obj.getMember("bar").equals("hello")) { + fail("obj.bar != 'hello'"); + } + + if (!obj.getSlot(1).equals("world")) { + fail("obj[1] != 'world'"); + } + + if (!obj.call("func", new Object[0]).equals("hello")) { + fail("obj.call('func') != 'hello'"); + } + + // try setting properties + obj.setMember("bar", "new-bar"); + obj.setSlot(1, "new-element-1"); + if (!obj.getMember("bar").equals("new-bar")) { + fail("obj.bar != 'new-bar'"); + } + + if (!obj.getSlot(1).equals("new-element-1")) { + fail("obj[1] != 'new-element-1'"); + } + + // try adding properties + obj.setMember("prop", "prop-value"); + obj.setSlot(12, "element-12"); + if (!obj.getMember("prop").equals("prop-value")) { + fail("obj.prop != 'prop-value'"); + } + + if (!obj.getSlot(12).equals("element-12")) { + fail("obj[12] != 'element-12'"); + } + + // delete properties + obj.removeMember("prop"); + if ("prop-value".equals(obj.getMember("prop"))) { + fail("obj.prop is not deleted!"); + } + + // Simple eval tests + assertEquals(obj.eval("typeof Object"), "function"); + assertEquals(obj.eval("'nashorn'.substring(3)"), "horn"); + } catch (final Exception exp) { + exp.printStackTrace(); + fail(exp.getMessage()); + } + } + + @Test + public void scriptObjectMirrorToStringTest() { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + try { + Object obj = e.eval("new TypeError('wrong type')"); + assertEquals(obj.toString(), "TypeError: wrong type", "toString returns wrong value"); + } catch (final Throwable t) { + t.printStackTrace(); + fail(t.getMessage()); + } + + try { + Object obj = e.eval("function func() { print('hello'); }"); + assertEquals(obj.toString(), "function func() { print('hello'); }", "toString returns wrong value"); + } catch (final Throwable t) { + t.printStackTrace(); + fail(t.getMessage()); + } + } + + @Test + public void mirrorNewObjectGlobalFunctionTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final ScriptEngine e2 = m.getEngineByName("nashorn"); + + e.eval("function func() {}"); + e2.put("foo", e.get("func")); + final Object e2global = e2.eval("this"); + final Object newObj = ((ScriptObjectMirror) e2global).newObject("foo"); + assertTrue(newObj instanceof ScriptObjectMirror); + } + + @Test + public void mirrorNewObjectInstanceFunctionTest() throws ScriptException { + final ScriptEngineManager m = new ScriptEngineManager(); + final ScriptEngine e = m.getEngineByName("nashorn"); + final ScriptEngine e2 = m.getEngineByName("nashorn"); + + e.eval("function func() {}"); + e2.put("func", e.get("func")); + final Object e2obj = e2.eval("({ foo: func })"); + final Object newObj = ((ScriptObjectMirror) e2obj).newObject("foo"); + assertTrue(newObj instanceof ScriptObjectMirror); + } +}