1 /*
   2  * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.nashorn.api.scripting;
  27 
  28 import static org.testng.Assert.assertEquals;
  29 import static org.testng.Assert.assertFalse;
  30 import static org.testng.Assert.assertNotNull;
  31 import static org.testng.Assert.assertTrue;
  32 import static org.testng.Assert.fail;
  33 
  34 import java.io.StringReader;
  35 import java.io.StringWriter;
  36 import java.lang.reflect.Method;
  37 import java.util.HashMap;
  38 import java.util.Map;
  39 import java.util.Objects;
  40 import java.util.concurrent.Callable;
  41 import javax.script.Bindings;
  42 import javax.script.Compilable;
  43 import javax.script.CompiledScript;
  44 import javax.script.Invocable;
  45 import javax.script.ScriptContext;
  46 import javax.script.ScriptEngine;
  47 import javax.script.ScriptEngineFactory;
  48 import javax.script.ScriptEngineManager;
  49 import javax.script.ScriptException;
  50 import javax.script.SimpleScriptContext;
  51 import org.testng.Assert;
  52 import org.testng.annotations.Test;
  53 
  54 /**
  55  * Tests for JSR-223 script engine for Nashorn.
  56  *
  57  * @test
  58  * @build jdk.nashorn.api.scripting.Window jdk.nashorn.api.scripting.WindowEventHandler jdk.nashorn.api.scripting.VariableArityTestInterface jdk.nashorn.api.scripting.ScriptEngineTest
  59  * @run testng jdk.nashorn.api.scripting.ScriptEngineTest
  60  */
  61 public class ScriptEngineTest {
  62 
  63     private void log(String msg) {
  64         org.testng.Reporter.log(msg, true);
  65     }
  66 
  67     @Test
  68     public void argumentsTest() {
  69         final ScriptEngineManager m = new ScriptEngineManager();
  70         final ScriptEngine e = m.getEngineByName("nashorn");
  71 
  72         String[] args = new String[] { "hello", "world" };
  73         try {
  74             e.put("arguments", args);
  75             Object arg0 = e.eval("arguments[0]");
  76             Object arg1 = e.eval("arguments[1]");
  77             assertEquals(args[0], arg0);
  78             assertEquals(args[1], arg1);
  79         } catch (final Exception exp) {
  80             exp.printStackTrace();
  81             fail(exp.getMessage());
  82         }
  83     }
  84 
  85     @Test
  86     public void argumentsWithTest() {
  87         final ScriptEngineManager m = new ScriptEngineManager();
  88         final ScriptEngine e = m.getEngineByName("nashorn");
  89 
  90         String[] args = new String[] { "hello", "world" };
  91         try {
  92             e.put("arguments", args);
  93             Object arg0 = e.eval("var imports = new JavaImporter(java.io); " +
  94                     " with(imports) { arguments[0] }");
  95             Object arg1 = e.eval("var imports = new JavaImporter(java.util, java.io); " +
  96                     " with(imports) { arguments[1] }");
  97             assertEquals(args[0], arg0);
  98             assertEquals(args[1], arg1);
  99         } catch (final Exception exp) {
 100             exp.printStackTrace();
 101             fail(exp.getMessage());
 102         }
 103     }
 104 
 105     @Test
 106     public void argumentsEmptyTest() {
 107         final ScriptEngineManager m = new ScriptEngineManager();
 108         final ScriptEngine e = m.getEngineByName("nashorn");
 109 
 110         try {
 111             assertEquals(e.eval("arguments instanceof Array"), true);
 112             assertEquals(e.eval("arguments.length == 0"), true);
 113         } catch (final Exception exp) {
 114             exp.printStackTrace();
 115             fail(exp.getMessage());
 116         }
 117     }
 118 
 119     @Test
 120     public void factoryTests() {
 121         final ScriptEngineManager m = new ScriptEngineManager();
 122         final ScriptEngine e = m.getEngineByName("nashorn");
 123         assertNotNull(e);
 124 
 125         final ScriptEngineFactory fac = e.getFactory();
 126 
 127         assertEquals(fac.getLanguageName(), "ECMAScript");
 128         assertEquals(fac.getParameter(ScriptEngine.NAME), "javascript");
 129         assertEquals(fac.getLanguageVersion(), "ECMA - 262 Edition 5.1");
 130         assertEquals(fac.getEngineName(), "Oracle Nashorn");
 131         assertEquals(fac.getOutputStatement("context"), "print(context)");
 132         assertEquals(fac.getProgram("print('hello')", "print('world')"), "print('hello');print('world');");
 133         assertEquals(fac.getParameter(ScriptEngine.NAME), "javascript");
 134 
 135         boolean seenJS = false;
 136         for (String ext : fac.getExtensions()) {
 137             if (ext.equals("js")) {
 138                 seenJS = true;
 139             }
 140         }
 141 
 142         assertEquals(seenJS, true);
 143         String str = fac.getMethodCallSyntax("obj", "foo", "x");
 144         assertEquals(str, "obj.foo(x)");
 145 
 146         boolean seenNashorn = false, seenJavaScript = false, seenECMAScript = false;
 147         for (String name : fac.getNames()) {
 148             switch (name) {
 149                 case "nashorn": seenNashorn = true; break;
 150                 case "javascript": seenJavaScript = true; break;
 151                 case "ECMAScript": seenECMAScript = true; break;
 152             }
 153         }
 154 
 155         assertTrue(seenNashorn);
 156         assertTrue(seenJavaScript);
 157         assertTrue(seenECMAScript);
 158 
 159         boolean seenAppJS = false, seenAppECMA = false, seenTextJS = false, seenTextECMA = false;
 160         for (String mime : fac.getMimeTypes()) {
 161             switch (mime) {
 162                 case "application/javascript": seenAppJS = true; break;
 163                 case "application/ecmascript": seenAppECMA = true; break;
 164                 case "text/javascript": seenTextJS = true; break;
 165                 case "text/ecmascript": seenTextECMA = true; break;
 166             }
 167         }
 168 
 169         assertTrue(seenAppJS);
 170         assertTrue(seenAppECMA);
 171         assertTrue(seenTextJS);
 172         assertTrue(seenTextECMA);
 173     }
 174 
 175     @Test
 176     public void evalTests() {
 177         final ScriptEngineManager m = new ScriptEngineManager();
 178         final ScriptEngine e = m.getEngineByName("nashorn");
 179         e.put(ScriptEngine.FILENAME, "myfile.js");
 180 
 181         try {
 182             e.eval("print('hello')");
 183         } catch (final ScriptException se) {
 184             fail(se.getMessage());
 185         }
 186         try {
 187             e.eval("print('hello)");
 188             fail("script exception expected");
 189         } catch (final ScriptException se) {
 190             assertEquals(se.getLineNumber(), 1);
 191             assertEquals(se.getColumnNumber(), 13);
 192             assertEquals(se.getFileName(), "myfile.js");
 193             // se.printStackTrace();
 194         }
 195 
 196         try {
 197             Object obj = e.eval("34 + 41");
 198             assertTrue(34.0 + 41.0 == ((Number)obj).doubleValue());
 199             obj = e.eval("x = 5");
 200             assertTrue(5.0 == ((Number)obj).doubleValue());
 201         } catch (final ScriptException se) {
 202             se.printStackTrace();
 203             fail(se.getMessage());
 204         }
 205     }
 206 
 207     @Test
 208     public void compileTests() {
 209         final ScriptEngineManager m = new ScriptEngineManager();
 210         final ScriptEngine e = m.getEngineByName("nashorn");
 211         CompiledScript script = null;
 212 
 213         try {
 214             script = ((Compilable)e).compile("print('hello')");
 215         } catch (final ScriptException se) {
 216             fail(se.getMessage());
 217         }
 218 
 219         try {
 220             script.eval();
 221         } catch (final ScriptException | NullPointerException se) {
 222             se.printStackTrace();
 223             fail(se.getMessage());
 224         }
 225 
 226         // try to compile from a Reader
 227         try {
 228             script = ((Compilable)e).compile(new StringReader("print('world')"));
 229         } catch (final ScriptException se) {
 230             fail(se.getMessage());
 231         }
 232 
 233         try {
 234             script.eval();
 235         } catch (final ScriptException | NullPointerException se) {
 236             se.printStackTrace();
 237             fail(se.getMessage());
 238         }
 239     }
 240 
 241     @Test
 242     public void createBindingsTest() {
 243         final ScriptEngineManager m = new ScriptEngineManager();
 244         final ScriptEngine e = m.getEngineByName("nashorn");
 245         Bindings b = e.createBindings();
 246         b.put("foo", 42.0);
 247         Object res = null;
 248         try {
 249             res = e.eval("foo == 42.0", b);
 250         } catch (final ScriptException | NullPointerException se) {
 251             se.printStackTrace();
 252             fail(se.getMessage());
 253         }
 254 
 255         assertEquals(res, Boolean.TRUE);
 256     }
 257 
 258     @Test
 259     public void getInterfaceTest() {
 260         final ScriptEngineManager m = new ScriptEngineManager();
 261         final ScriptEngine e = m.getEngineByName("nashorn");
 262         final Invocable inv = (Invocable)e;
 263 
 264         // try to get interface from global functions
 265         try {
 266             e.eval("function run() { print('run'); };");
 267             final Runnable runnable = inv.getInterface(Runnable.class);
 268             runnable.run();
 269         } catch (final Exception exp) {
 270             exp.printStackTrace();
 271             fail(exp.getMessage());
 272         }
 273 
 274         // try interface on specific script object
 275         try {
 276             e.eval("var obj = { run: function() { print('run from obj'); } };");
 277             Object obj = e.get("obj");
 278             final Runnable runnable = inv.getInterface(obj, Runnable.class);
 279             runnable.run();
 280         } catch (final Exception exp) {
 281             exp.printStackTrace();
 282             fail(exp.getMessage());
 283         }
 284     }
 285 
 286     public interface Foo {
 287         public void bar();
 288     }
 289 
 290     public interface Foo2 extends Foo {
 291         public void bar2();
 292     }
 293 
 294     @Test
 295     public void getInterfaceMissingTest() {
 296         final ScriptEngineManager manager = new ScriptEngineManager();
 297         final ScriptEngine engine = manager.getEngineByName("nashorn");
 298 
 299         // don't define any function.
 300         try {
 301             engine.eval("");
 302         } catch (final Exception exp) {
 303             exp.printStackTrace();
 304             fail(exp.getMessage());
 305         }
 306 
 307         Runnable runnable = ((Invocable)engine).getInterface(Runnable.class);
 308         if (runnable != null) {
 309             fail("runnable is not null!");
 310         }
 311 
 312         // now define "run"
 313         try {
 314             engine.eval("function run() { print('this is run function'); }");
 315         } catch (final Exception exp) {
 316             exp.printStackTrace();
 317             fail(exp.getMessage());
 318         }
 319         runnable = ((Invocable)engine).getInterface(Runnable.class);
 320         // should not return null now!
 321         runnable.run();
 322 
 323         // define only one method of "Foo2"
 324         try {
 325             engine.eval("function bar() { print('bar function'); }");
 326         } catch (final Exception exp) {
 327             exp.printStackTrace();
 328             fail(exp.getMessage());
 329         }
 330 
 331         Foo2 foo2 = ((Invocable)engine).getInterface(Foo2.class);
 332         if (foo2 != null) {
 333             throw new RuntimeException("foo2 is not null!");
 334         }
 335 
 336         // now define other method of "Foo2"
 337         try {
 338             engine.eval("function bar2() { print('bar2 function'); }");
 339         } catch (final Exception exp) {
 340             exp.printStackTrace();
 341             fail(exp.getMessage());
 342         }
 343         foo2 = ((Invocable)engine).getInterface(Foo2.class);
 344         foo2.bar();
 345         foo2.bar2();
 346     }
 347 
 348     @Test
 349     /**
 350      * Try passing non-interface Class object for interface implementation.
 351      */
 352     public void getNonInterfaceGetInterfaceTest() {
 353         final ScriptEngineManager manager = new ScriptEngineManager();
 354         final ScriptEngine engine = manager.getEngineByName("nashorn");
 355         try {
 356             log(Objects.toString(((Invocable)engine).getInterface(Object.class)));
 357             fail("Should have thrown IllegalArgumentException");
 358         } catch (final Exception exp) {
 359             if (! (exp instanceof IllegalArgumentException)) {
 360                 fail("IllegalArgumentException expected, got " + exp);
 361             }
 362         }
 363     }
 364 
 365     @Test
 366     /**
 367      * Check that we can get interface out of a script object even after
 368      * switching to use different ScriptContext.
 369      */
 370     public void getInterfaceDifferentContext() {
 371        ScriptEngineManager m = new ScriptEngineManager();
 372        ScriptEngine e = m.getEngineByName("nashorn");
 373        try {
 374            Object obj = e.eval("({ run: function() { } })");
 375 
 376            // change script context
 377            ScriptContext ctxt = new SimpleScriptContext();
 378            ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
 379            e.setContext(ctxt);
 380 
 381            Runnable r = ((Invocable)e).getInterface(obj, Runnable.class);
 382            r.run();
 383        }catch (final Exception exp) {
 384             exp.printStackTrace();
 385             fail(exp.getMessage());
 386        }
 387     }
 388 
 389     @Test
 390     /**
 391      * Check that getInterface on non-script object 'thiz' results in IllegalArgumentException.
 392      */
 393     public void getInterfaceNonScriptObjectThizTest() {
 394         final ScriptEngineManager m = new ScriptEngineManager();
 395         final ScriptEngine e = m.getEngineByName("nashorn");
 396 
 397         try {
 398             ((Invocable)e).getInterface(new Object(), Runnable.class);
 399             fail("should have thrown IllegalArgumentException");
 400         } catch (final Exception exp) {
 401             if (! (exp instanceof IllegalArgumentException)) {
 402                 exp.printStackTrace();
 403                 fail(exp.getMessage());
 404             }
 405         }
 406     }
 407 
 408     @Test
 409     /**
 410      * Check that getInterface on null 'thiz' results in IllegalArgumentException.
 411      */
 412     public void getInterfaceNullThizTest() {
 413         final ScriptEngineManager m = new ScriptEngineManager();
 414         final ScriptEngine e = m.getEngineByName("nashorn");
 415 
 416         try {
 417             ((Invocable)e).getInterface(null, Runnable.class);
 418             fail("should have thrown IllegalArgumentException");
 419         } catch (final Exception exp) {
 420             if (! (exp instanceof IllegalArgumentException)) {
 421                 exp.printStackTrace();
 422                 fail(exp.getMessage());
 423             }
 424         }
 425     }
 426 
 427     @Test
 428     /**
 429      * Check that calling getInterface on mirror created by another engine results in IllegalArgumentException.
 430      */
 431     public void getInterfaceMixEnginesTest() {
 432         final ScriptEngineManager m = new ScriptEngineManager();
 433         final ScriptEngine engine1 = m.getEngineByName("nashorn");
 434         final ScriptEngine engine2 = m.getEngineByName("nashorn");
 435 
 436         try {
 437             Object obj = engine1.eval("({ run: function() {} })");
 438             // pass object from engine1 to engine2 as 'thiz' for getInterface
 439             ((Invocable)engine2).getInterface(obj, Runnable.class);
 440             fail("should have thrown IllegalArgumentException");
 441         } catch (final Exception exp) {
 442             if (! (exp instanceof IllegalArgumentException)) {
 443                 exp.printStackTrace();
 444                 fail(exp.getMessage());
 445             }
 446         }
 447     }
 448 
 449     @Test
 450     public void accessGlobalTest() {
 451         final ScriptEngineManager m = new ScriptEngineManager();
 452         final ScriptEngine e = m.getEngineByName("nashorn");
 453 
 454         try {
 455             e.eval("var x = 'hello'");
 456             assertEquals(e.get("x"), "hello");
 457         } catch (final ScriptException exp) {
 458             exp.printStackTrace();
 459             fail(exp.getMessage());
 460         }
 461     }
 462 
 463     @Test
 464     public void exposeGlobalTest() {
 465         final ScriptEngineManager m = new ScriptEngineManager();
 466         final ScriptEngine e = m.getEngineByName("nashorn");
 467 
 468         try {
 469             e.put("y", "foo");
 470             e.eval("print(y)");
 471         } catch (final ScriptException exp) {
 472             exp.printStackTrace();
 473             fail(exp.getMessage());
 474         }
 475     }
 476 
 477     @Test
 478     public void putGlobalFunctionTest() {
 479         final ScriptEngineManager m = new ScriptEngineManager();
 480         final ScriptEngine e = m.getEngineByName("nashorn");
 481 
 482         e.put("callable", new Callable<String>() {
 483             @Override
 484             public String call() throws Exception {
 485                 return "callable was called";
 486             }
 487         });
 488 
 489         try {
 490             e.eval("print(callable.call())");
 491         } catch (final ScriptException exp) {
 492             exp.printStackTrace();
 493             fail(exp.getMessage());
 494         }
 495     }
 496 
 497     @Test
 498     public void windowAlertTest() {
 499         final ScriptEngineManager m = new ScriptEngineManager();
 500         final ScriptEngine e = m.getEngineByName("nashorn");
 501         final Window window = new Window();
 502 
 503         try {
 504             e.put("window", window);
 505             e.eval("print(window.alert)");
 506             e.eval("window.alert('calling window.alert...')");
 507         } catch (final Exception exp) {
 508             exp.printStackTrace();
 509             fail(exp.getMessage());
 510         }
 511     }
 512 
 513     @Test
 514     public void windowLocationTest() {
 515         final ScriptEngineManager m = new ScriptEngineManager();
 516         final ScriptEngine e = m.getEngineByName("nashorn");
 517         final Window window = new Window();
 518 
 519         try {
 520             e.put("window", window);
 521             e.eval("print(window.location)");
 522             final Object locationValue = e.eval("window.getLocation()");
 523             assertEquals(locationValue, "http://localhost:8080/window");
 524         } catch (final Exception exp) {
 525             exp.printStackTrace();
 526             fail(exp.getMessage());
 527         }
 528     }
 529 
 530     @Test
 531     public void windowItemTest() {
 532         final ScriptEngineManager m = new ScriptEngineManager();
 533         final ScriptEngine e = m.getEngineByName("nashorn");
 534         final Window window = new Window();
 535 
 536         try {
 537             e.put("window", window);
 538             final String item1 = (String)e.eval("window.item(65535)");
 539             assertEquals(item1, "ffff");
 540             final String item2 = (String)e.eval("window.item(255)");
 541             assertEquals(item2, "ff");
 542         } catch (final Exception exp) {
 543             exp.printStackTrace();
 544             fail(exp.getMessage());
 545         }
 546     }
 547 
 548     @Test
 549     public void windowEventTest() {
 550         final ScriptEngineManager m = new ScriptEngineManager();
 551         final ScriptEngine e = m.getEngineByName("nashorn");
 552         final Window window = new Window();
 553 
 554         try {
 555             e.put("window", window);
 556             e.eval("window.onload = function() { print('window load event fired'); return true }");
 557             assertTrue((Boolean)e.eval("window.onload.loaded()"));
 558             final WindowEventHandler handler = window.getOnload();
 559             assertNotNull(handler);
 560             assertTrue(handler.loaded());
 561         } catch (final Exception exp) {
 562             exp.printStackTrace();
 563             fail(exp.getMessage());
 564         }
 565     }
 566 
 567     @Test
 568     public void throwTest() {
 569         final ScriptEngineManager m = new ScriptEngineManager();
 570         final ScriptEngine e = m.getEngineByName("nashorn");
 571         e.put(ScriptEngine.FILENAME, "throwtest.js");
 572 
 573         try {
 574             e.eval("throw 'foo'");
 575         } catch (final ScriptException exp) {
 576             log(exp.getMessage());
 577             assertEquals(exp.getMessage(), "foo in throwtest.js at line number 1 at column number 0");
 578             assertEquals(exp.getFileName(), "throwtest.js");
 579             assertEquals(exp.getLineNumber(), 1);
 580         }
 581     }
 582 
 583     @Test
 584     public void setTimeoutTest() {
 585         final ScriptEngineManager m = new ScriptEngineManager();
 586         final ScriptEngine e = m.getEngineByName("nashorn");
 587         final Window window = new Window();
 588 
 589         try {
 590             final Class<?> setTimeoutParamTypes[] = { Window.class, String.class, int.class };
 591             final Method setTimeout = Window.class.getDeclaredMethod("setTimeout", setTimeoutParamTypes);
 592             assertNotNull(setTimeout);
 593             e.put("window", window);
 594             e.eval("window.setTimeout('foo()', 100)");
 595 
 596             // try to make setTimeout global
 597             e.put("setTimeout", setTimeout);
 598             // TODO: java.lang.ClassCastException: required class
 599             // java.lang.Integer but encountered class java.lang.Double
 600             // e.eval("setTimeout('foo2()', 200)");
 601         } catch (final Exception exp) {
 602             exp.printStackTrace();
 603             fail(exp.getMessage());
 604         }
 605     }
 606 
 607     @Test
 608     public void setWriterTest() {
 609         final ScriptEngineManager m = new ScriptEngineManager();
 610         final ScriptEngine e = m.getEngineByName("nashorn");
 611         final StringWriter sw = new StringWriter();
 612         e.getContext().setWriter(sw);
 613 
 614         try {
 615             e.eval("print('hello world')");
 616         } catch (final Exception exp) {
 617             exp.printStackTrace();
 618             fail(exp.getMessage());
 619         }
 620         // dos2unix - fix line endings if running on windows
 621         assertEquals(sw.toString().replaceAll("\r", ""), "hello world\n");
 622     }
 623 
 624     @SuppressWarnings("unchecked")
 625     @Test
 626     public void reflectionTest() throws ScriptException {
 627         final ScriptEngineManager m = new ScriptEngineManager();
 628         final ScriptEngine e = m.getEngineByName("nashorn");
 629 
 630         e.eval("var obj = { x: 344, y: 'nashorn' }");
 631 
 632         int count = 0;
 633         Map<Object, Object> map = (Map<Object, Object>)e.get("obj");
 634         assertFalse(map.isEmpty());
 635         assertTrue(map.keySet().contains("x"));
 636         assertTrue(map.containsKey("x"));
 637         assertTrue(map.values().contains("nashorn"));
 638         assertTrue(map.containsValue("nashorn"));
 639         for (final Map.Entry<?, ?> ex : map.entrySet()) {
 640             final Object key = ex.getKey();
 641             if (key.equals("x")) {
 642                 assertTrue(344 == ((Number)ex.getValue()).doubleValue());
 643                 count++;
 644             } else if (key.equals("y")) {
 645                 assertEquals(ex.getValue(), "nashorn");
 646                 count++;
 647             }
 648         }
 649         assertEquals(2, count);
 650         assertEquals(2, map.size());
 651 
 652         // add property
 653         map.put("z", "hello");
 654         assertEquals(e.eval("obj.z"), "hello");
 655         assertEquals(map.get("z"), "hello");
 656         assertTrue(map.keySet().contains("z"));
 657         assertTrue(map.containsKey("z"));
 658         assertTrue(map.values().contains("hello"));
 659         assertTrue(map.containsValue("hello"));
 660         assertEquals(map.size(), 3);
 661 
 662         final Map<Object, Object> newMap = new HashMap<>();
 663         newMap.put("foo", 23.0);
 664         newMap.put("bar", true);
 665         map.putAll(newMap);
 666 
 667         assertEquals(e.eval("obj.foo"), 23.0);
 668         assertEquals(e.eval("obj.bar"), true);
 669 
 670         // remove using map method
 671         map.remove("foo");
 672         assertEquals(e.eval("typeof obj.foo"), "undefined");
 673 
 674         count = 0;
 675         e.eval("var arr = [ true, 'hello' ]");
 676         map = (Map<Object, Object>)e.get("arr");
 677         assertFalse(map.isEmpty());
 678         assertTrue(map.containsKey("length"));
 679         assertTrue(map.containsValue("hello"));
 680         for (final Map.Entry<?, ?> ex : map.entrySet()) {
 681             final Object key = ex.getKey();
 682             if (key.equals("0")) {
 683                 assertEquals(ex.getValue(), Boolean.TRUE);
 684                 count++;
 685             } else if (key.equals("1")) {
 686                 assertEquals(ex.getValue(), "hello");
 687                 count++;
 688             }
 689         }
 690         assertEquals(count, 2);
 691         assertEquals(map.size(), 2);
 692 
 693         // add element
 694         map.put("2", "world");
 695         assertEquals(map.get("2"), "world");
 696         assertEquals(map.size(), 3);
 697 
 698         // remove all
 699         map.clear();
 700         assertTrue(map.isEmpty());
 701         assertEquals(e.eval("typeof arr[0]"), "undefined");
 702         assertEquals(e.eval("typeof arr[1]"), "undefined");
 703         assertEquals(e.eval("typeof arr[2]"), "undefined");
 704     }
 705 
 706     @Test
 707     public void redefineEchoTest() {
 708         final ScriptEngineManager m = new ScriptEngineManager();
 709         final ScriptEngine e = m.getEngineByName("nashorn");
 710 
 711         try {
 712             e.eval("var echo = {}; if (typeof echo !== 'object') { throw 'echo is a '+typeof echo; }");
 713         } catch (final Exception exp) {
 714             exp.printStackTrace();
 715             fail(exp.getMessage());
 716         }
 717     }
 718 
 719     @Test
 720     public void invokeMethodTest() {
 721         final ScriptEngineManager m = new ScriptEngineManager();
 722         final ScriptEngine e = m.getEngineByName("nashorn");
 723 
 724         try {
 725             e.eval("var Example = function() { this.hello = function() { return 'Hello World!'; };}; myExample = new Example();");
 726             final Object obj = e.get("myExample");
 727             final Object res = ((Invocable)e).invokeMethod(obj, "hello");
 728             assertEquals(res, "Hello World!");
 729         } catch (final Exception exp) {
 730             exp.printStackTrace();
 731             fail(exp.getMessage());
 732         }
 733     }
 734 
 735     @Test
 736     /**
 737      * Check that we can call invokeMethod on an object that we got by evaluating
 738      * script with different Context set.
 739      */
 740     public void invokeMethodDifferentContextTest() {
 741        ScriptEngineManager m = new ScriptEngineManager();
 742        ScriptEngine e = m.getEngineByName("nashorn");
 743 
 744        try {
 745            // define an object with method on it
 746            Object obj = e.eval("({ hello: function() { return 'Hello World!'; } })");
 747 
 748            final ScriptContext ctxt = new SimpleScriptContext();
 749            ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
 750            e.setContext(ctxt);
 751 
 752            // invoke 'func' on obj - but with current script context changed
 753            final Object res = ((Invocable)e).invokeMethod(obj, "hello");
 754            assertEquals(res, "Hello World!");
 755        } catch (final Exception exp) {
 756            exp.printStackTrace();
 757            fail(exp.getMessage());
 758        }
 759     }
 760 
 761     @Test
 762     /**
 763      * Check that invokeMethod throws NPE on null method name.
 764      */
 765     public void invokeMethodNullNameTest() {
 766         final ScriptEngineManager m = new ScriptEngineManager();
 767         final ScriptEngine e = m.getEngineByName("nashorn");
 768 
 769         try {
 770             final Object obj = e.eval("({})");
 771             final Object res = ((Invocable)e).invokeMethod(obj, null);
 772             fail("should have thrown NPE");
 773         } catch (final Exception exp) {
 774             if (! (exp instanceof NullPointerException)) {
 775                 exp.printStackTrace();
 776                 fail(exp.getMessage());
 777             }
 778         }
 779     }
 780 
 781     @Test
 782     /**
 783      * Check that invokeMethod throws NoSuchMethodException on missing method.
 784      */
 785     public void invokeMethodMissingTest() {
 786         final ScriptEngineManager m = new ScriptEngineManager();
 787         final ScriptEngine e = m.getEngineByName("nashorn");
 788 
 789         try {
 790             final Object obj = e.eval("({})");
 791             final Object res = ((Invocable)e).invokeMethod(obj, "nonExistentMethod");
 792             fail("should have thrown NoSuchMethodException");
 793         } catch (final Exception exp) {
 794             if (! (exp instanceof NoSuchMethodException)) {
 795                 exp.printStackTrace();
 796                 fail(exp.getMessage());
 797             }
 798         }
 799     }
 800 
 801     @Test
 802     /**
 803      * Check that calling method on non-script object 'thiz' results in IllegalArgumentException.
 804      */
 805     public void invokeMethodNonScriptObjectThizTest() {
 806         final ScriptEngineManager m = new ScriptEngineManager();
 807         final ScriptEngine e = m.getEngineByName("nashorn");
 808 
 809         try {
 810             ((Invocable)e).invokeMethod(new Object(), "toString");
 811             fail("should have thrown IllegalArgumentException");
 812         } catch (final Exception exp) {
 813             if (! (exp instanceof IllegalArgumentException)) {
 814                 exp.printStackTrace();
 815                 fail(exp.getMessage());
 816             }
 817         }
 818     }
 819 
 820     @Test
 821     /**
 822      * Check that calling method on null 'thiz' results in IllegalArgumentException.
 823      */
 824     public void invokeMethodNullThizTest() {
 825         final ScriptEngineManager m = new ScriptEngineManager();
 826         final ScriptEngine e = m.getEngineByName("nashorn");
 827 
 828         try {
 829             ((Invocable)e).invokeMethod(null, "toString");
 830             fail("should have thrown IllegalArgumentException");
 831         } catch (final Exception exp) {
 832             if (! (exp instanceof IllegalArgumentException)) {
 833                 exp.printStackTrace();
 834                 fail(exp.getMessage());
 835             }
 836         }
 837     }
 838 
 839 
 840     @Test
 841     /**
 842      * Check that calling method on mirror created by another engine results in IllegalArgumentException.
 843      */
 844     public void invokeMethodMixEnginesTest() {
 845         final ScriptEngineManager m = new ScriptEngineManager();
 846         final ScriptEngine engine1 = m.getEngineByName("nashorn");
 847         final ScriptEngine engine2 = m.getEngineByName("nashorn");
 848 
 849         try {
 850             Object obj = engine1.eval("({ run: function() {} })");
 851             // pass object from engine1 to engine2 as 'thiz' for invokeMethod
 852             ((Invocable)engine2).invokeMethod(obj, "run");
 853             fail("should have thrown IllegalArgumentException");
 854         } catch (final Exception exp) {
 855             if (! (exp instanceof IllegalArgumentException)) {
 856                 exp.printStackTrace();
 857                 fail(exp.getMessage());
 858             }
 859         }
 860     }
 861 
 862     @Test
 863     public void noEnumerablePropertiesTest() {
 864         final ScriptEngineManager m = new ScriptEngineManager();
 865         final ScriptEngine e = m.getEngineByName("nashorn");
 866         try {
 867             e.eval("for (i in this) { throw 'found property: ' + i }");
 868         } catch (final Exception exp) {
 869             exp.printStackTrace();
 870             fail(exp.getMessage());
 871         }
 872     }
 873 
 874     @Test
 875     public void noRefErrorForGlobalThisAccessTest() {
 876         final ScriptEngineManager m = new ScriptEngineManager();
 877         final ScriptEngine e = m.getEngineByName("nashorn");
 878         try {
 879             e.eval("this.foo");
 880         } catch (final Exception exp) {
 881             exp.printStackTrace();
 882             fail(exp.getMessage());
 883         }
 884     }
 885 
 886     @Test
 887     public void refErrorForUndeclaredAccessTest() {
 888         final ScriptEngineManager m = new ScriptEngineManager();
 889         final ScriptEngine e = m.getEngineByName("nashorn");
 890         try {
 891             e.eval("try { print(foo); throw 'no ref error' } catch (e) { if (!(e instanceof ReferenceError)) throw e; }");
 892         } catch (final Exception exp) {
 893             exp.printStackTrace();
 894             fail(exp.getMessage());
 895         }
 896     }
 897 
 898     @Test
 899     public void typeErrorForGlobalThisCallTest() {
 900         final ScriptEngineManager m = new ScriptEngineManager();
 901         final ScriptEngine e = m.getEngineByName("nashorn");
 902         try {
 903             e.eval("try { this.foo() } catch(e) { if (! (e instanceof TypeError)) throw 'no type error' }");
 904         } catch (final Exception exp) {
 905             exp.printStackTrace();
 906             fail(exp.getMessage());
 907         }
 908     }
 909 
 910     @Test
 911     public void refErrorForUndeclaredCallTest() {
 912         final ScriptEngineManager m = new ScriptEngineManager();
 913         final ScriptEngine e = m.getEngineByName("nashorn");
 914         try {
 915             e.eval("try { foo() } catch(e) { if (! (e instanceof ReferenceError)) throw 'no ref error' }");
 916         } catch (final Exception exp) {
 917             exp.printStackTrace();
 918             fail(exp.getMessage());
 919         }
 920     }
 921 
 922     @Test
 923     public void jsobjectTest() {
 924         final ScriptEngineManager m = new ScriptEngineManager();
 925         final ScriptEngine e = m.getEngineByName("nashorn");
 926         try {
 927             e.eval("var obj = { '1': 'world', func: function() { return this.bar; }, bar: 'hello' }");
 928             JSObject obj = (JSObject) e.get("obj");
 929 
 930             // try basic get on existing properties
 931             if (! obj.getMember("bar").equals("hello")) {
 932                 fail("obj.bar != 'hello'");
 933             }
 934 
 935             if (! obj.getSlot(1).equals("world")) {
 936                 fail("obj[1] != 'world'");
 937             }
 938 
 939             if (! obj.call("func", new Object[0]).equals("hello")) {
 940                 fail("obj.call('func') != 'hello'");
 941             }
 942 
 943             // try setting properties
 944             obj.setMember("bar", "new-bar");
 945             obj.setSlot(1, "new-element-1");
 946             if (! obj.getMember("bar").equals("new-bar")) {
 947                 fail("obj.bar != 'new-bar'");
 948             }
 949 
 950             if (! obj.getSlot(1).equals("new-element-1")) {
 951                 fail("obj[1] != 'new-element-1'");
 952             }
 953 
 954             // try adding properties
 955             obj.setMember("prop", "prop-value");
 956             obj.setSlot(12, "element-12");
 957             if (! obj.getMember("prop").equals("prop-value")) {
 958                 fail("obj.prop != 'prop-value'");
 959             }
 960 
 961             if (! obj.getSlot(12).equals("element-12")) {
 962                 fail("obj[12] != 'element-12'");
 963             }
 964 
 965             // delete properties
 966             obj.removeMember("prop");
 967             if ("prop-value".equals(obj.getMember("prop"))) {
 968                 fail("obj.prop is not deleted!");
 969             }
 970 
 971             // Simple eval tests
 972             assertEquals(obj.eval("typeof Object"), "function");
 973             assertEquals(obj.eval("'nashorn'.substring(3)"), "horn");
 974         } catch (final Exception exp) {
 975             exp.printStackTrace();
 976             fail(exp.getMessage());
 977         }
 978     }
 979 
 980     @Test
 981     /**
 982      * check that null function name results in NPE.
 983      */
 984     public void invokeFunctionNullNameTest() {
 985         final ScriptEngineManager m = new ScriptEngineManager();
 986         final ScriptEngine e = m.getEngineByName("nashorn");
 987 
 988         try {
 989             final Object res = ((Invocable)e).invokeFunction(null);
 990             fail("should have thrown NPE");
 991         } catch (final Exception exp) {
 992             if (! (exp instanceof NullPointerException)) {
 993                 exp.printStackTrace();
 994                 fail(exp.getMessage());
 995             }
 996         }
 997     }
 998 
 999     @Test
1000     /**
1001      * Check that attempt to call missing function results in NoSuchMethodException.
1002      */
1003     public void invokeFunctionMissingTest() {
1004         final ScriptEngineManager m = new ScriptEngineManager();
1005         final ScriptEngine e = m.getEngineByName("nashorn");
1006 
1007         try {
1008             final Object res = ((Invocable)e).invokeFunction("NonExistentFunc");
1009             fail("should have thrown NoSuchMethodException");
1010         } catch (final Exception exp) {
1011             if (! (exp instanceof NoSuchMethodException)) {
1012                 exp.printStackTrace();
1013                 fail(exp.getMessage());
1014             }
1015         }
1016     }
1017 
1018     @Test
1019     /**
1020      * Check that invokeFunction calls functions only from current context's Bindings.
1021      */
1022     public void invokeFunctionDifferentContextTest() {
1023         ScriptEngineManager m = new ScriptEngineManager();
1024         ScriptEngine e = m.getEngineByName("nashorn");
1025 
1026         try {
1027             // define an object with method on it
1028             Object obj = e.eval("function hello() { return 'Hello World!'; }");
1029             final ScriptContext ctxt = new SimpleScriptContext();
1030             ctxt.setBindings(e.createBindings(), ScriptContext.ENGINE_SCOPE);
1031             // change engine's current context
1032             e.setContext(ctxt);
1033 
1034             ((Invocable)e).invokeFunction("hello"); // no 'hello' in new context!
1035             fail("should have thrown NoSuchMethodException");
1036         } catch (final Exception exp) {
1037             if (! (exp instanceof NoSuchMethodException)) {
1038                 exp.printStackTrace();
1039                 fail(exp.getMessage());
1040             }
1041         }
1042     }
1043 
1044     @Test
1045     public void invokeFunctionExceptionTest() {
1046         final ScriptEngineManager m = new ScriptEngineManager();
1047         final ScriptEngine e = m.getEngineByName("nashorn");
1048         try {
1049             e.eval("function func() { throw new TypeError(); }");
1050         } catch (final Throwable t) {
1051             t.printStackTrace();
1052             fail(t.getMessage());
1053         }
1054 
1055         try {
1056             ((Invocable)e).invokeFunction("func");
1057             fail("should have thrown exception");
1058         } catch (final ScriptException se) {
1059             // ECMA TypeError property wrapped as a ScriptException
1060             log("got " + se + " as expected");
1061         } catch (final Throwable t) {
1062             t.printStackTrace();
1063             fail(t.getMessage());
1064         }
1065     }
1066 
1067     @Test
1068     public void invokeMethodExceptionTest() {
1069         final ScriptEngineManager m = new ScriptEngineManager();
1070         final ScriptEngine e = m.getEngineByName("nashorn");
1071         try {
1072             e.eval("var sobj = {}; sobj.foo = function func() { throw new TypeError(); }");
1073         } catch (final Throwable t) {
1074             t.printStackTrace();
1075             fail(t.getMessage());
1076         }
1077 
1078         try {
1079             final Object sobj = e.get("sobj");
1080             ((Invocable)e).invokeMethod(sobj, "foo");
1081             fail("should have thrown exception");
1082         } catch (final ScriptException se) {
1083             // ECMA TypeError property wrapped as a ScriptException
1084             log("got " + se + " as expected");
1085         } catch (final Throwable t) {
1086             t.printStackTrace();
1087             fail(t.getMessage());
1088         }
1089     }
1090 
1091     @Test
1092     public void scriptObjectMirrorToStringTest() {
1093         final ScriptEngineManager m = new ScriptEngineManager();
1094         final ScriptEngine e = m.getEngineByName("nashorn");
1095         try {
1096             Object obj = e.eval("new TypeError('wrong type')");
1097             assertEquals(obj.toString(), "TypeError: wrong type", "toString returns wrong value");
1098         } catch (final Throwable t) {
1099             t.printStackTrace();
1100             fail(t.getMessage());
1101         }
1102 
1103         try {
1104             Object obj = e.eval("function func() { print('hello'); }");
1105             assertEquals(obj.toString(), "function func() { print('hello'); }", "toString returns wrong value");
1106         } catch (final Throwable t) {
1107             t.printStackTrace();
1108             fail(t.getMessage());
1109         }
1110     }
1111 
1112     @Test
1113     public void engineScopeTest() {
1114         final ScriptEngineManager m = new ScriptEngineManager();
1115         final ScriptEngine e = m.getEngineByName("nashorn");
1116         Bindings engineScope = e.getBindings(ScriptContext.ENGINE_SCOPE);
1117 
1118         // check few ECMA standard built-in global properties
1119         assertNotNull(engineScope.get("Object"));
1120         assertNotNull(engineScope.get("TypeError"));
1121         assertNotNull(engineScope.get("eval"));
1122 
1123         // can access via ScriptEngine.get as well
1124         assertNotNull(e.get("Object"));
1125         assertNotNull(e.get("TypeError"));
1126         assertNotNull(e.get("eval"));
1127 
1128         // Access by either way should return same object
1129         assertEquals(engineScope.get("Array"), e.get("Array"));
1130         assertEquals(engineScope.get("EvalError"), e.get("EvalError"));
1131         assertEquals(engineScope.get("undefined"), e.get("undefined"));
1132 
1133         // try exposing a new variable from scope
1134         engineScope.put("myVar", "foo");
1135         try {
1136             assertEquals(e.eval("myVar"), "foo");
1137         } catch (final ScriptException se) {
1138             se.printStackTrace();
1139             fail(se.getMessage());
1140         }
1141 
1142         // update "myVar" in script an check the value from scope
1143         try {
1144             e.eval("myVar = 'nashorn';");
1145         } catch (final ScriptException se) {
1146             se.printStackTrace();
1147             fail(se.getMessage());
1148         }
1149 
1150         // now check modified value from scope and engine
1151         assertEquals(engineScope.get("myVar"), "nashorn");
1152         assertEquals(e.get("myVar"), "nashorn");
1153     }
1154 
1155     @Test
1156     public void multiGlobalTest() {
1157         final ScriptEngineManager m = new ScriptEngineManager();
1158         final ScriptEngine e = m.getEngineByName("nashorn");
1159         final Bindings b = e.createBindings();
1160         final ScriptContext newCtxt = new SimpleScriptContext();
1161         newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
1162 
1163         try {
1164             Object obj1 = e.eval("Object");
1165             Object obj2 = e.eval("Object", newCtxt);
1166             Assert.assertNotEquals(obj1, obj2);
1167             Assert.assertNotNull(obj1);
1168             Assert.assertNotNull(obj2);
1169             Assert.assertEquals(obj1.toString(), obj2.toString());
1170 
1171             e.eval("x = 'hello'");
1172             e.eval("x = 'world'", newCtxt);
1173             Object x1 = e.getContext().getAttribute("x");
1174             Object x2 = newCtxt.getAttribute("x");
1175             Assert.assertNotEquals(x1, x2);
1176             Assert.assertEquals(x1, "hello");
1177             Assert.assertEquals(x2, "world");
1178 
1179             x1 = e.eval("x");
1180             x2 = e.eval("x", newCtxt);
1181             Assert.assertNotEquals(x1, x2);
1182             Assert.assertEquals(x1, "hello");
1183             Assert.assertEquals(x2, "world");
1184 
1185             final ScriptContext origCtxt = e.getContext();
1186             e.setContext(newCtxt);
1187             e.eval("y = new Object()");
1188             e.eval("y = new Object()", origCtxt);
1189 
1190             Object y1 = origCtxt.getAttribute("y");
1191             Object y2 = newCtxt.getAttribute("y");
1192             Assert.assertNotEquals(y1, y2);
1193             Assert.assertNotEquals(e.eval("y"), e.eval("y", origCtxt));
1194             Assert.assertEquals("[object Object]", y1.toString());
1195             Assert.assertEquals("[object Object]", y2.toString());
1196         } catch (final ScriptException se) {
1197             se.printStackTrace();
1198             fail(se.getMessage());
1199         }
1200     }
1201 
1202     @Test
1203     /**
1204      * Tests whether invocation of a JavaScript method through a variable arity Java method will pass the vararg array.
1205      * Both non-vararg and vararg JavaScript methods are tested.
1206      * @throws ScriptException
1207      */
1208     public void variableArityInterfaceTest() throws ScriptException {
1209         final ScriptEngineManager m = new ScriptEngineManager();
1210         final ScriptEngine e = m.getEngineByName("nashorn");
1211         e.eval(
1212             "function test1(i, strings) {" +
1213             "    return 'i == ' + i + ', strings instanceof java.lang.String[] == ' + (strings instanceof Java.type('java.lang.String[]')) + ', strings == ' + java.util.Arrays.toString(strings)" +
1214             "}" +
1215             "function test2() {" +
1216             "    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])" +
1217             "}"
1218         );
1219         final VariableArityTestInterface itf = ((Invocable)e).getInterface(VariableArityTestInterface.class);
1220         Assert.assertEquals(itf.test1(42, "a", "b"), "i == 42, strings instanceof java.lang.String[] == true, strings == [a, b]");
1221         Assert.assertEquals(itf.test2(44, "c", "d", "e"), "arguments[0] == 44, arguments[1] instanceof java.lang.String[] == true, arguments[1] == [c, d, e]");
1222     }
1223 
1224     @Test
1225     // check that print function prints arg followed by newline char
1226     public void printTest() {
1227         final ScriptEngineManager m = new ScriptEngineManager();
1228         final ScriptEngine e = m.getEngineByName("nashorn");
1229         final StringWriter sw = new StringWriter();
1230         e.getContext().setWriter(sw);
1231         try {
1232             e.eval("print('hello')");
1233         } catch (final Throwable t) {
1234             t.printStackTrace();
1235             fail(t.getMessage());
1236         }
1237 
1238         // dos2unix - fix line endings if running on windows
1239         assertEquals(sw.toString().replaceAll("\r", ""), "hello\n");
1240     }
1241 
1242     @Test
1243     // check that print prints all arguments (more than one)
1244     public void printManyTest() {
1245         final ScriptEngineManager m = new ScriptEngineManager();
1246         final ScriptEngine e = m.getEngineByName("nashorn");
1247         final StringWriter sw = new StringWriter();
1248         e.getContext().setWriter(sw);
1249         try {
1250             e.eval("print(34, true, 'hello')");
1251         } catch (final Throwable t) {
1252             t.printStackTrace();
1253             fail(t.getMessage());
1254         }
1255 
1256         // dos2unix - fix line endings if running on windows
1257         assertEquals(sw.toString().replaceAll("\r", ""), "34 true hello\n");
1258     }
1259 
1260     @Test
1261     public void mirrorNewObjectGlobalFunctionTest() throws ScriptException {
1262         final ScriptEngineManager m = new ScriptEngineManager();
1263         final ScriptEngine e = m.getEngineByName("nashorn");
1264         final ScriptEngine e2 = m.getEngineByName("nashorn");
1265 
1266         e.eval("function func() {}");
1267         e2.put("foo", e.get("func"));
1268         final Object e2global = e2.eval("this");
1269         final Object newObj = ((ScriptObjectMirror)e2global).newObject("foo");
1270         assertTrue(newObj instanceof ScriptObjectMirror);
1271     }
1272 
1273     @Test
1274     public void mirrorNewObjectInstanceFunctionTest() throws ScriptException {
1275         final ScriptEngineManager m = new ScriptEngineManager();
1276         final ScriptEngine e = m.getEngineByName("nashorn");
1277         final ScriptEngine e2 = m.getEngineByName("nashorn");
1278 
1279         e.eval("function func() {}");
1280         e2.put("func", e.get("func"));
1281         final Object e2obj = e2.eval("({ foo: func })");
1282         final Object newObj = ((ScriptObjectMirror)e2obj).newObject("foo");
1283         assertTrue(newObj instanceof ScriptObjectMirror);
1284     }
1285 }