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