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.assertNotNull;
  30 import static org.testng.Assert.assertTrue;
  31 import static org.testng.Assert.fail;
  32 
  33 import java.io.StringReader;
  34 import java.io.StringWriter;
  35 import java.lang.reflect.InvocationHandler;
  36 import java.lang.reflect.Method;
  37 import java.lang.reflect.Proxy;
  38 import java.util.concurrent.Callable;
  39 import javax.script.Compilable;
  40 import javax.script.CompiledScript;
  41 import javax.script.ScriptContext;
  42 import javax.script.ScriptEngine;
  43 import javax.script.ScriptEngineFactory;
  44 import javax.script.ScriptEngineManager;
  45 import javax.script.ScriptException;
  46 import javax.script.SimpleScriptContext;
  47 import org.testng.annotations.Test;
  48 
  49 /**
  50  * Tests for JSR-223 script engine for Nashorn.
  51  *
  52  * @test
  53  * @build jdk.nashorn.api.scripting.Window jdk.nashorn.api.scripting.WindowEventHandler jdk.nashorn.api.scripting.VariableArityTestInterface jdk.nashorn.api.scripting.ScriptEngineTest
  54  * @run testng/othervm jdk.nashorn.api.scripting.ScriptEngineTest
  55  */
  56 public class ScriptEngineTest {
  57 
  58     private void log(final String msg) {
  59         org.testng.Reporter.log(msg, true);
  60     }
  61 
  62     @Test
  63     public void argumentsTest() {
  64         final ScriptEngineManager m = new ScriptEngineManager();
  65         final ScriptEngine e = m.getEngineByName("nashorn");
  66 
  67         final String[] args = new String[] { "hello", "world" };
  68         try {
  69             e.put("arguments", args);
  70             final Object arg0 = e.eval("arguments[0]");
  71             final Object arg1 = e.eval("arguments[1]");
  72             assertEquals(args[0], arg0);
  73             assertEquals(args[1], arg1);
  74         } catch (final Exception exp) {
  75             exp.printStackTrace();
  76             fail(exp.getMessage());
  77         }
  78     }
  79 
  80     @Test
  81     public void argumentsWithTest() {
  82         final ScriptEngineManager m = new ScriptEngineManager();
  83         final ScriptEngine e = m.getEngineByName("nashorn");
  84 
  85         final String[] args = new String[] { "hello", "world" };
  86         try {
  87             e.put("arguments", args);
  88             final Object arg0 = e.eval("var imports = new JavaImporter(java.io); " +
  89                     " with(imports) { arguments[0] }");
  90             final Object arg1 = e.eval("var imports = new JavaImporter(java.util, java.io); " +
  91                     " with(imports) { arguments[1] }");
  92             assertEquals(args[0], arg0);
  93             assertEquals(args[1], arg1);
  94         } catch (final Exception exp) {
  95             exp.printStackTrace();
  96             fail(exp.getMessage());
  97         }
  98     }
  99 
 100     @Test
 101     public void argumentsEmptyTest() {
 102         final ScriptEngineManager m = new ScriptEngineManager();
 103         final ScriptEngine e = m.getEngineByName("nashorn");
 104 
 105         try {
 106             assertEquals(e.eval("arguments instanceof Array"), true);
 107             assertEquals(e.eval("arguments.length == 0"), true);
 108         } catch (final Exception exp) {
 109             exp.printStackTrace();
 110             fail(exp.getMessage());
 111         }
 112     }
 113 
 114     @Test
 115     public void factoryTests() {
 116         final ScriptEngineManager m = new ScriptEngineManager();
 117         final ScriptEngine e = m.getEngineByName("nashorn");
 118         assertNotNull(e);
 119 
 120         final ScriptEngineFactory fac = e.getFactory();
 121 
 122         assertEquals(fac.getLanguageName(), "ECMAScript");
 123         assertEquals(fac.getParameter(ScriptEngine.NAME), "javascript");
 124         assertEquals(fac.getLanguageVersion(), "ECMA - 262 Edition 5.1");
 125         assertEquals(fac.getEngineName(), "Oracle Nashorn");
 126         assertEquals(fac.getOutputStatement("context"), "print(context)");
 127         assertEquals(fac.getProgram("print('hello')", "print('world')"), "print('hello');print('world');");
 128         assertEquals(fac.getParameter(ScriptEngine.NAME), "javascript");
 129 
 130         boolean seenJS = false;
 131         for (final String ext : fac.getExtensions()) {
 132             if (ext.equals("js")) {
 133                 seenJS = true;
 134             }
 135         }
 136 
 137         assertEquals(seenJS, true);
 138         final String str = fac.getMethodCallSyntax("obj", "foo", "x");
 139         assertEquals(str, "obj.foo(x)");
 140 
 141         boolean seenNashorn = false, seenJavaScript = false, seenECMAScript = false;
 142         for (final String name : fac.getNames()) {
 143             switch (name) {
 144                 case "nashorn": seenNashorn = true; break;
 145                 case "javascript": seenJavaScript = true; break;
 146                 case "ECMAScript": seenECMAScript = true; break;
 147             }
 148         }
 149 
 150         assertTrue(seenNashorn);
 151         assertTrue(seenJavaScript);
 152         assertTrue(seenECMAScript);
 153 
 154         boolean seenAppJS = false, seenAppECMA = false, seenTextJS = false, seenTextECMA = false;
 155         for (final String mime : fac.getMimeTypes()) {
 156             switch (mime) {
 157                 case "application/javascript": seenAppJS = true; break;
 158                 case "application/ecmascript": seenAppECMA = true; break;
 159                 case "text/javascript": seenTextJS = true; break;
 160                 case "text/ecmascript": seenTextECMA = true; break;
 161             }
 162         }
 163 
 164         assertTrue(seenAppJS);
 165         assertTrue(seenAppECMA);
 166         assertTrue(seenTextJS);
 167         assertTrue(seenTextECMA);
 168     }
 169 
 170     @Test
 171     public void evalTests() {
 172         final ScriptEngineManager m = new ScriptEngineManager();
 173         final ScriptEngine e = m.getEngineByName("nashorn");
 174         e.put(ScriptEngine.FILENAME, "myfile.js");
 175 
 176         try {
 177             e.eval("print('hello')");
 178         } catch (final ScriptException se) {
 179             fail(se.getMessage());
 180         }
 181         try {
 182             e.eval("print('hello)");
 183             fail("script exception expected");
 184         } catch (final ScriptException se) {
 185             assertEquals(se.getLineNumber(), 1);
 186             assertEquals(se.getColumnNumber(), 13);
 187             assertEquals(se.getFileName(), "myfile.js");
 188             // se.printStackTrace();
 189         }
 190 
 191         try {
 192             Object obj = e.eval("34 + 41");
 193             assertTrue(34.0 + 41.0 == ((Number)obj).doubleValue());
 194             obj = e.eval("x = 5");
 195             assertTrue(5.0 == ((Number)obj).doubleValue());
 196         } catch (final ScriptException se) {
 197             se.printStackTrace();
 198             fail(se.getMessage());
 199         }
 200     }
 201 
 202     @Test
 203     public void compileTests() {
 204         final ScriptEngineManager m = new ScriptEngineManager();
 205         final ScriptEngine e = m.getEngineByName("nashorn");
 206         CompiledScript script = null;
 207 
 208         try {
 209             script = ((Compilable)e).compile("print('hello')");
 210         } catch (final ScriptException se) {
 211             fail(se.getMessage());
 212         }
 213 
 214         try {
 215             script.eval();
 216         } catch (final ScriptException | NullPointerException se) {
 217             se.printStackTrace();
 218             fail(se.getMessage());
 219         }
 220 
 221         // try to compile from a Reader
 222         try {
 223             script = ((Compilable)e).compile(new StringReader("print('world')"));
 224         } catch (final ScriptException se) {
 225             fail(se.getMessage());
 226         }
 227 
 228         try {
 229             script.eval();
 230         } catch (final ScriptException | NullPointerException se) {
 231             se.printStackTrace();
 232             fail(se.getMessage());
 233         }
 234     }
 235 
 236     @Test
 237     public void compileAndEvalInDiffContextTest() throws ScriptException {
 238         final ScriptEngineManager m = new ScriptEngineManager();
 239         final ScriptEngine engine = m.getEngineByName("js");
 240         final Compilable compilable = (Compilable) engine;
 241         final CompiledScript compiledScript = compilable.compile("foo");
 242         final ScriptContext ctxt = new SimpleScriptContext();
 243         ctxt.setAttribute("foo", "hello", ScriptContext.ENGINE_SCOPE);
 244         assertEquals(compiledScript.eval(ctxt), "hello");
 245     }
 246 
 247     @Test
 248     public void accessGlobalTest() {
 249         final ScriptEngineManager m = new ScriptEngineManager();
 250         final ScriptEngine e = m.getEngineByName("nashorn");
 251 
 252         try {
 253             e.eval("var x = 'hello'");
 254             assertEquals(e.get("x"), "hello");
 255         } catch (final ScriptException exp) {
 256             exp.printStackTrace();
 257             fail(exp.getMessage());
 258         }
 259     }
 260 
 261     @Test
 262     public void exposeGlobalTest() {
 263         final ScriptEngineManager m = new ScriptEngineManager();
 264         final ScriptEngine e = m.getEngineByName("nashorn");
 265 
 266         try {
 267             e.put("y", "foo");
 268             e.eval("print(y)");
 269         } catch (final ScriptException exp) {
 270             exp.printStackTrace();
 271             fail(exp.getMessage());
 272         }
 273     }
 274 
 275     @Test
 276     public void putGlobalFunctionTest() {
 277         final ScriptEngineManager m = new ScriptEngineManager();
 278         final ScriptEngine e = m.getEngineByName("nashorn");
 279 
 280         e.put("callable", new Callable<String>() {
 281             @Override
 282             public String call() throws Exception {
 283                 return "callable was called";
 284             }
 285         });
 286 
 287         try {
 288             e.eval("print(callable.call())");
 289         } catch (final ScriptException exp) {
 290             exp.printStackTrace();
 291             fail(exp.getMessage());
 292         }
 293     }
 294 
 295     @Test
 296     public void windowAlertTest() {
 297         final ScriptEngineManager m = new ScriptEngineManager();
 298         final ScriptEngine e = m.getEngineByName("nashorn");
 299         final Window window = new Window();
 300 
 301         try {
 302             e.put("window", window);
 303             e.eval("print(window.alert)");
 304             e.eval("window.alert('calling window.alert...')");
 305         } catch (final Exception exp) {
 306             exp.printStackTrace();
 307             fail(exp.getMessage());
 308         }
 309     }
 310 
 311     @Test
 312     public void windowLocationTest() {
 313         final ScriptEngineManager m = new ScriptEngineManager();
 314         final ScriptEngine e = m.getEngineByName("nashorn");
 315         final Window window = new Window();
 316 
 317         try {
 318             e.put("window", window);
 319             e.eval("print(window.location)");
 320             final Object locationValue = e.eval("window.getLocation()");
 321             assertEquals(locationValue, "http://localhost:8080/window");
 322         } catch (final Exception exp) {
 323             exp.printStackTrace();
 324             fail(exp.getMessage());
 325         }
 326     }
 327 
 328     @Test
 329     public void windowItemTest() {
 330         final ScriptEngineManager m = new ScriptEngineManager();
 331         final ScriptEngine e = m.getEngineByName("nashorn");
 332         final Window window = new Window();
 333 
 334         try {
 335             e.put("window", window);
 336             final String item1 = (String)e.eval("window.item(65535)");
 337             assertEquals(item1, "ffff");
 338             final String item2 = (String)e.eval("window.item(255)");
 339             assertEquals(item2, "ff");
 340         } catch (final Exception exp) {
 341             exp.printStackTrace();
 342             fail(exp.getMessage());
 343         }
 344     }
 345 
 346     @Test
 347     public void windowEventTest() {
 348         final ScriptEngineManager m = new ScriptEngineManager();
 349         final ScriptEngine e = m.getEngineByName("nashorn");
 350         final Window window = new Window();
 351 
 352         try {
 353             e.put("window", window);
 354             e.eval("window.onload = function() { print('window load event fired'); return true }");
 355             assertTrue((Boolean)e.eval("window.onload.loaded()"));
 356             final WindowEventHandler handler = window.getOnload();
 357             assertNotNull(handler);
 358             assertTrue(handler.loaded());
 359         } catch (final Exception exp) {
 360             exp.printStackTrace();
 361             fail(exp.getMessage());
 362         }
 363     }
 364 
 365     @Test
 366     public void throwTest() {
 367         final ScriptEngineManager m = new ScriptEngineManager();
 368         final ScriptEngine e = m.getEngineByName("nashorn");
 369         e.put(ScriptEngine.FILENAME, "throwtest.js");
 370 
 371         try {
 372             e.eval("throw 'foo'");
 373         } catch (final ScriptException exp) {
 374             log(exp.getMessage());
 375             assertEquals(exp.getMessage(), "foo in throwtest.js at line number 1 at column number 0");
 376             assertEquals(exp.getFileName(), "throwtest.js");
 377             assertEquals(exp.getLineNumber(), 1);
 378         }
 379     }
 380 
 381     @Test
 382     public void setTimeoutTest() {
 383         final ScriptEngineManager m = new ScriptEngineManager();
 384         final ScriptEngine e = m.getEngineByName("nashorn");
 385         final Window window = new Window();
 386 
 387         try {
 388             final Class<?> setTimeoutParamTypes[] = { Window.class, String.class, int.class };
 389             final Method setTimeout = Window.class.getDeclaredMethod("setTimeout", setTimeoutParamTypes);
 390             assertNotNull(setTimeout);
 391             e.put("window", window);
 392             e.eval("window.setTimeout('foo()', 100)");
 393 
 394             // try to make setTimeout global
 395             e.put("setTimeout", setTimeout);
 396             // TODO: java.lang.ClassCastException: required class
 397             // java.lang.Integer but encountered class java.lang.Double
 398             // e.eval("setTimeout('foo2()', 200)");
 399         } catch (final Exception exp) {
 400             exp.printStackTrace();
 401             fail(exp.getMessage());
 402         }
 403     }
 404 
 405     @Test
 406     public void setWriterTest() {
 407         final ScriptEngineManager m = new ScriptEngineManager();
 408         final ScriptEngine e = m.getEngineByName("nashorn");
 409         final StringWriter sw = new StringWriter();
 410         e.getContext().setWriter(sw);
 411 
 412         try {
 413             e.eval("print('hello world')");
 414         } catch (final Exception exp) {
 415             exp.printStackTrace();
 416             fail(exp.getMessage());
 417         }
 418         assertEquals(sw.toString(), println("hello world"));
 419     }
 420 
 421     @Test
 422     public void redefineEchoTest() {
 423         final ScriptEngineManager m = new ScriptEngineManager();
 424         final ScriptEngine e = m.getEngineByName("nashorn");
 425 
 426         try {
 427             e.eval("var echo = {}; if (typeof echo !== 'object') { throw 'echo is a '+typeof echo; }");
 428         } catch (final Exception exp) {
 429             exp.printStackTrace();
 430             fail(exp.getMessage());
 431         }
 432     }
 433     @Test
 434     public void noEnumerablePropertiesTest() {
 435         final ScriptEngineManager m = new ScriptEngineManager();
 436         final ScriptEngine e = m.getEngineByName("nashorn");
 437         try {
 438             e.eval("for (i in this) { throw 'found property: ' + i }");
 439         } catch (final Exception exp) {
 440             exp.printStackTrace();
 441             fail(exp.getMessage());
 442         }
 443     }
 444 
 445     @Test
 446     public void noRefErrorForGlobalThisAccessTest() {
 447         final ScriptEngineManager m = new ScriptEngineManager();
 448         final ScriptEngine e = m.getEngineByName("nashorn");
 449         try {
 450             e.eval("this.foo");
 451         } catch (final Exception exp) {
 452             exp.printStackTrace();
 453             fail(exp.getMessage());
 454         }
 455     }
 456 
 457     @Test
 458     public void refErrorForUndeclaredAccessTest() {
 459         final ScriptEngineManager m = new ScriptEngineManager();
 460         final ScriptEngine e = m.getEngineByName("nashorn");
 461         try {
 462             e.eval("try { print(foo); throw 'no ref error' } catch (e) { if (!(e instanceof ReferenceError)) throw e; }");
 463         } catch (final Exception exp) {
 464             exp.printStackTrace();
 465             fail(exp.getMessage());
 466         }
 467     }
 468 
 469     @Test
 470     public void typeErrorForGlobalThisCallTest() {
 471         final ScriptEngineManager m = new ScriptEngineManager();
 472         final ScriptEngine e = m.getEngineByName("nashorn");
 473         try {
 474             e.eval("try { this.foo() } catch(e) { if (! (e instanceof TypeError)) throw 'no type error' }");
 475         } catch (final Exception exp) {
 476             exp.printStackTrace();
 477             fail(exp.getMessage());
 478         }
 479     }
 480 
 481     @Test
 482     public void refErrorForUndeclaredCallTest() {
 483         final ScriptEngineManager m = new ScriptEngineManager();
 484         final ScriptEngine e = m.getEngineByName("nashorn");
 485         try {
 486             e.eval("try { foo() } catch(e) { if (! (e instanceof ReferenceError)) throw 'no ref error' }");
 487         } catch (final Exception exp) {
 488             exp.printStackTrace();
 489             fail(exp.getMessage());
 490         }
 491     }
 492 
 493     @Test
 494     // check that print function prints arg followed by newline char
 495     public void printTest() {
 496         final ScriptEngineManager m = new ScriptEngineManager();
 497         final ScriptEngine e = m.getEngineByName("nashorn");
 498         final StringWriter sw = new StringWriter();
 499         e.getContext().setWriter(sw);
 500         try {
 501             e.eval("print('hello')");
 502         } catch (final Throwable t) {
 503             t.printStackTrace();
 504             fail(t.getMessage());
 505         }
 506 
 507         assertEquals(sw.toString(), println("hello"));
 508     }
 509 
 510     @Test
 511     // check that print prints all arguments (more than one)
 512     public void printManyTest() {
 513         final ScriptEngineManager m = new ScriptEngineManager();
 514         final ScriptEngine e = m.getEngineByName("nashorn");
 515         final StringWriter sw = new StringWriter();
 516         e.getContext().setWriter(sw);
 517         try {
 518             e.eval("print(34, true, 'hello')");
 519         } catch (final Throwable t) {
 520             t.printStackTrace();
 521             fail(t.getMessage());
 522         }
 523 
 524         assertEquals(sw.toString(), println("34 true hello"));
 525     }
 526 
 527     @Test
 528     public void scriptObjectAutoConversionTest() throws ScriptException {
 529         final ScriptEngineManager m = new ScriptEngineManager();
 530         final ScriptEngine e = m.getEngineByName("nashorn");
 531         e.eval("obj = { foo: 'hello' }");
 532         e.put("Window", e.eval("Packages.jdk.nashorn.api.scripting.Window"));
 533         assertEquals(e.eval("Window.funcJSObject(obj)"), "hello");
 534         assertEquals(e.eval("Window.funcScriptObjectMirror(obj)"), "hello");
 535         assertEquals(e.eval("Window.funcMap(obj)"), "hello");
 536         assertEquals(e.eval("Window.funcJSObject(obj)"), "hello");
 537     }
 538 
 539     // @bug 8032948: Nashorn linkages awry
 540     @Test
 541     public void checkProxyAccess() throws ScriptException {
 542         final ScriptEngineManager m = new ScriptEngineManager();
 543         final ScriptEngine e = m.getEngineByName("nashorn");
 544         final boolean[] reached = new boolean[1];
 545         final Runnable r = (Runnable)Proxy.newProxyInstance(
 546             ScriptEngineTest.class.getClassLoader(),
 547             new Class[] { Runnable.class },
 548             new InvocationHandler() {
 549                 @Override
 550                 public Object invoke(final Object p, final Method m, final Object[] a) {
 551                     reached[0] = true;
 552                     return null;
 553                 }
 554             });
 555 
 556         e.put("r", r);
 557         e.eval("r.run()");
 558 
 559         assertTrue(reached[0]);
 560     }
 561 
 562     // properties that can be read by any code
 563     private static String[] propNames = {
 564         "java.version",
 565         "java.vendor",
 566         "java.vendor.url",
 567         "java.class.version",
 568         "os.name",
 569         "os.version",
 570         "os.arch",
 571         "file.separator",
 572         "path.separator",
 573         "line.separator",
 574         "java.specification.version",
 575         "java.specification.vendor",
 576         "java.specification.name",
 577         "java.vm.specification.version",
 578         "java.vm.specification.vendor",
 579         "java.vm.specification.name",
 580         "java.vm.version",
 581         "java.vm.vendor",
 582         "java.vm.name"
 583     };
 584 
 585     // @bug 8033924: Default permissions are not given for eval code
 586     @Test
 587     public void checkPropertyReadPermissions() throws ScriptException {
 588         final ScriptEngineManager m = new ScriptEngineManager();
 589         final ScriptEngine e = m.getEngineByName("nashorn");
 590 
 591         for (final String name : propNames) {
 592             checkProperty(e, name);
 593         }
 594     }
 595 
 596     // @bug 8046013: TypeError: Cannot apply "with" to non script object
 597     @Test
 598     public void withOnMirrorTest() throws ScriptException {
 599         final ScriptEngineManager m = new ScriptEngineManager();
 600         final ScriptEngine e = m.getEngineByName("nashorn");
 601 
 602         final Object obj = e.eval("({ foo: 'hello'})");
 603         final Object[] arr = new Object[1];
 604         arr[0] = obj;
 605         e.put("arr", arr);
 606         final Object res = e.eval("var res; with(arr[0]) { res = foo; }; res");
 607         assertEquals(res, "hello");
 608     }
 609 
 610     // @bug 8054223: Nashorn: AssertionError when use __DIR__ and ScriptEngine.eval()
 611     @Test
 612     public void check__DIR__Test() throws ScriptException {
 613         final ScriptEngineManager m = new ScriptEngineManager();
 614         final ScriptEngine e = m.getEngineByName("nashorn");
 615         e.eval("__DIR__");
 616     }
 617 
 618     // @bug 8050432:javax.script.filename variable should not be enumerable
 619     // with nashorn engine's ENGINE_SCOPE bindings
 620     @Test
 621     public void enumerableGlobalsTest() throws ScriptException {
 622         final ScriptEngineManager m = new ScriptEngineManager();
 623         final ScriptEngine e = m.getEngineByName("nashorn");
 624 
 625         e.put(ScriptEngine.FILENAME, "test");
 626         Object enumerable = e.eval(
 627             "Object.getOwnPropertyDescriptor(this, " +
 628             " 'javax.script.filename').enumerable");
 629         assertEquals(enumerable, Boolean.FALSE);
 630     }
 631 
 632     private static void checkProperty(final ScriptEngine e, final String name)
 633         throws ScriptException {
 634         final String value = System.getProperty(name);
 635         e.put("name", name);
 636         assertEquals(value, e.eval("java.lang.System.getProperty(name)"));
 637     }
 638 
 639     private static final String LINE_SEPARATOR = System.getProperty("line.separator");
 640 
 641     // Returns String that would be the result of calling PrintWriter.println
 642     // of the given String. (This is to handle platform specific newline).
 643     private static String println(final String str) {
 644         return str + LINE_SEPARATOR;
 645     }
 646 }
--- EOF ---