ScopeTest.java revision 1786:80120e9b3273
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 */ 25package jdk.nashorn.api.scripting.test; 26 27import static org.testng.Assert.assertEquals; 28import static org.testng.Assert.assertNotNull; 29import static org.testng.Assert.assertFalse; 30import static org.testng.Assert.assertTrue; 31import static org.testng.Assert.fail; 32import javax.script.Bindings; 33import javax.script.Compilable; 34import javax.script.CompiledScript; 35import javax.script.Invocable; 36import javax.script.ScriptContext; 37import javax.script.ScriptEngine; 38import javax.script.ScriptEngineFactory; 39import javax.script.ScriptEngineManager; 40import javax.script.ScriptException; 41import javax.script.SimpleBindings; 42import javax.script.SimpleScriptContext; 43import jdk.nashorn.api.scripting.NashornScriptEngineFactory; 44import jdk.nashorn.api.scripting.ScriptObjectMirror; 45import jdk.nashorn.api.scripting.URLReader; 46import org.testng.Assert; 47import org.testng.annotations.Test; 48 49/** 50 * Tests for jsr223 Bindings "scope" (engine, global scopes) 51 */ 52@SuppressWarnings("javadoc") 53public class ScopeTest { 54 55 @Test 56 public void createBindingsTest() { 57 final ScriptEngineManager m = new ScriptEngineManager(); 58 final ScriptEngine e = m.getEngineByName("nashorn"); 59 final Bindings b = e.createBindings(); 60 b.put("foo", 42.0); 61 Object res = null; 62 try { 63 res = e.eval("foo == 42.0", b); 64 } catch (final ScriptException | NullPointerException se) { 65 se.printStackTrace(); 66 fail(se.getMessage()); 67 } 68 69 assertEquals(res, Boolean.TRUE); 70 } 71 72 @Test 73 public void engineScopeTest() { 74 final ScriptEngineManager m = new ScriptEngineManager(); 75 final ScriptEngine e = m.getEngineByName("nashorn"); 76 final Bindings engineScope = e.getBindings(ScriptContext.ENGINE_SCOPE); 77 78 // check few ECMA standard built-in global properties 79 assertNotNull(engineScope.get("Object")); 80 assertNotNull(engineScope.get("TypeError")); 81 assertNotNull(engineScope.get("eval")); 82 83 // can access via ScriptEngine.get as well 84 assertNotNull(e.get("Object")); 85 assertNotNull(e.get("TypeError")); 86 assertNotNull(e.get("eval")); 87 88 // Access by either way should return same object 89 assertEquals(engineScope.get("Array"), e.get("Array")); 90 assertEquals(engineScope.get("EvalError"), e.get("EvalError")); 91 assertEquals(engineScope.get("undefined"), e.get("undefined")); 92 93 // try exposing a new variable from scope 94 engineScope.put("myVar", "foo"); 95 try { 96 assertEquals(e.eval("myVar"), "foo"); 97 } catch (final ScriptException se) { 98 se.printStackTrace(); 99 fail(se.getMessage()); 100 } 101 102 // update "myVar" in script an check the value from scope 103 try { 104 e.eval("myVar = 'nashorn';"); 105 } catch (final ScriptException se) { 106 se.printStackTrace(); 107 fail(se.getMessage()); 108 } 109 110 // now check modified value from scope and engine 111 assertEquals(engineScope.get("myVar"), "nashorn"); 112 assertEquals(e.get("myVar"), "nashorn"); 113 } 114 115 @Test 116 public void multiGlobalTest() { 117 final ScriptEngineManager m = new ScriptEngineManager(); 118 final ScriptEngine e = m.getEngineByName("nashorn"); 119 final Bindings b = e.createBindings(); 120 final ScriptContext newCtxt = new SimpleScriptContext(); 121 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 122 123 try { 124 final Object obj1 = e.eval("Object"); 125 final Object obj2 = e.eval("Object", newCtxt); 126 Assert.assertNotEquals(obj1, obj2); 127 Assert.assertNotNull(obj1); 128 Assert.assertNotNull(obj2); 129 Assert.assertEquals(obj1.toString(), obj2.toString()); 130 131 e.eval("x = 'hello'"); 132 e.eval("x = 'world'", newCtxt); 133 Object x1 = e.getContext().getAttribute("x"); 134 Object x2 = newCtxt.getAttribute("x"); 135 Assert.assertNotEquals(x1, x2); 136 Assert.assertEquals(x1, "hello"); 137 Assert.assertEquals(x2, "world"); 138 139 x1 = e.eval("x"); 140 x2 = e.eval("x", newCtxt); 141 Assert.assertNotEquals(x1, x2); 142 Assert.assertEquals(x1, "hello"); 143 Assert.assertEquals(x2, "world"); 144 145 final ScriptContext origCtxt = e.getContext(); 146 e.setContext(newCtxt); 147 e.eval("y = new Object()"); 148 e.eval("y = new Object()", origCtxt); 149 150 final Object y1 = origCtxt.getAttribute("y"); 151 final Object y2 = newCtxt.getAttribute("y"); 152 Assert.assertNotEquals(y1, y2); 153 final Object yeval1 = e.eval("y"); 154 final Object yeval2 = e.eval("y", origCtxt); 155 Assert.assertNotEquals(yeval1, yeval2); 156 Assert.assertEquals("[object Object]", y1.toString()); 157 Assert.assertEquals("[object Object]", y2.toString()); 158 } catch (final ScriptException se) { 159 se.printStackTrace(); 160 fail(se.getMessage()); 161 } 162 } 163 164 @Test 165 public void userEngineScopeBindingsTest() throws ScriptException { 166 final ScriptEngineManager m = new ScriptEngineManager(); 167 final ScriptEngine e = m.getEngineByName("nashorn"); 168 e.eval("function func() {}"); 169 170 final ScriptContext newContext = new SimpleScriptContext(); 171 newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); 172 // we are using a new bindings - so it should have 'func' defined 173 final Object value = e.eval("typeof func", newContext); 174 assertTrue(value.equals("undefined")); 175 } 176 177 @Test 178 public void userEngineScopeBindingsNoLeakTest() throws ScriptException { 179 final ScriptEngineManager m = new ScriptEngineManager(); 180 final ScriptEngine e = m.getEngineByName("nashorn"); 181 final ScriptContext newContext = new SimpleScriptContext(); 182 newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); 183 e.eval("function foo() {}", newContext); 184 185 // in the default context's ENGINE_SCOPE, 'foo' shouldn't exist 186 assertTrue(e.eval("typeof foo").equals("undefined")); 187 } 188 189 @Test 190 public void userEngineScopeBindingsRetentionTest() throws ScriptException { 191 final ScriptEngineManager m = new ScriptEngineManager(); 192 final ScriptEngine e = m.getEngineByName("nashorn"); 193 final ScriptContext newContext = new SimpleScriptContext(); 194 newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); 195 e.eval("function foo() {}", newContext); 196 197 // definition retained with user's ENGINE_SCOPE Binding 198 assertTrue(e.eval("typeof foo", newContext).equals("function")); 199 200 final Bindings oldBindings = newContext.getBindings(ScriptContext.ENGINE_SCOPE); 201 // but not in another ENGINE_SCOPE binding 202 newContext.setBindings(new SimpleBindings(), ScriptContext.ENGINE_SCOPE); 203 assertTrue(e.eval("typeof foo", newContext).equals("undefined")); 204 205 // restore ENGINE_SCOPE and check again 206 newContext.setBindings(oldBindings, ScriptContext.ENGINE_SCOPE); 207 assertTrue(e.eval("typeof foo", newContext).equals("function")); 208 } 209 210 @Test 211 // check that engine.js definitions are visible in all new global instances 212 public void checkBuiltinsInNewBindingsTest() throws ScriptException { 213 final ScriptEngineManager m = new ScriptEngineManager(); 214 final ScriptEngine e = m.getEngineByName("nashorn"); 215 216 // check default global instance has engine.js definitions 217 final Bindings g = (Bindings) e.eval("this"); 218 Object value = g.get("__noSuchProperty__"); 219 assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction()); 220 value = g.get("print"); 221 assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction()); 222 223 // check new global instance created has engine.js definitions 224 final Bindings b = e.createBindings(); 225 value = b.get("__noSuchProperty__"); 226 assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction()); 227 value = b.get("print"); 228 assertTrue(value instanceof ScriptObjectMirror && ((ScriptObjectMirror)value).isFunction()); 229 230 // put a mapping into GLOBAL_SCOPE 231 final Bindings globalScope = e.getContext().getBindings(ScriptContext.GLOBAL_SCOPE); 232 globalScope.put("x", "hello"); 233 234 // GLOBAL_SCOPE mapping should be visible from default ScriptContext eval 235 assertTrue(e.eval("x").equals("hello")); 236 237 final ScriptContext ctx = new SimpleScriptContext(); 238 ctx.setBindings(globalScope, ScriptContext.GLOBAL_SCOPE); 239 ctx.setBindings(b, ScriptContext.ENGINE_SCOPE); 240 241 // GLOBAL_SCOPE mapping should be visible from non-default ScriptContext eval 242 assertTrue(e.eval("x", ctx).equals("hello")); 243 244 // try some arbitray Bindings for ENGINE_SCOPE 245 final Bindings sb = new SimpleBindings(); 246 ctx.setBindings(sb, ScriptContext.ENGINE_SCOPE); 247 248 // GLOBAL_SCOPE mapping should be visible from non-default ScriptContext eval 249 assertTrue(e.eval("x", ctx).equals("hello")); 250 251 // engine.js builtins are still defined even with arbitrary Bindings 252 assertTrue(e.eval("typeof print", ctx).equals("function")); 253 assertTrue(e.eval("typeof __noSuchProperty__", ctx).equals("function")); 254 255 // ENGINE_SCOPE definition should 'hide' GLOBAL_SCOPE definition 256 sb.put("x", "newX"); 257 assertTrue(e.eval("x", ctx).equals("newX")); 258 } 259 260 /** 261 * Test multi-threaded access to defined global variables for shared script classes with multiple globals. 262 */ 263 @Test 264 public static void multiThreadedVarTest() throws ScriptException, InterruptedException { 265 final ScriptEngineManager m = new ScriptEngineManager(); 266 final ScriptEngine e = m.getEngineByName("nashorn"); 267 final Bindings b = e.createBindings(); 268 final ScriptContext origContext = e.getContext(); 269 final ScriptContext newCtxt = new SimpleScriptContext(); 270 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 271 final String sharedScript = "foo"; 272 273 assertEquals(e.eval("var foo = 'original context';", origContext), null); 274 assertEquals(e.eval("var foo = 'new context';", newCtxt), null); 275 276 final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); 277 final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); 278 t1.start(); 279 t2.start(); 280 t1.join(); 281 t2.join(); 282 283 assertEquals(e.eval("var foo = 'newer context';", newCtxt), null); 284 final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); 285 final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); 286 287 t3.start(); 288 t4.start(); 289 t3.join(); 290 t4.join(); 291 292 assertEquals(e.eval(sharedScript), "original context"); 293 assertEquals(e.eval(sharedScript, newCtxt), "newer context"); 294 } 295 296 /** 297 * Test multi-threaded access to undefined global variables for shared script classes with multiple globals. 298 */ 299 @Test 300 public static void multiThreadedGlobalTest() throws ScriptException, InterruptedException { 301 final ScriptEngineManager m = new ScriptEngineManager(); 302 final ScriptEngine e = m.getEngineByName("nashorn"); 303 final Bindings b = e.createBindings(); 304 final ScriptContext origContext = e.getContext(); 305 final ScriptContext newCtxt = new SimpleScriptContext(); 306 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 307 308 assertEquals(e.eval("foo = 'original context';", origContext), "original context"); 309 assertEquals(e.eval("foo = 'new context';", newCtxt), "new context"); 310 final String sharedScript = "foo"; 311 312 final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); 313 final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); 314 t1.start(); 315 t2.start(); 316 t1.join(); 317 t2.join(); 318 319 final Object obj3 = e.eval("delete foo; foo = 'newer context';", newCtxt); 320 assertEquals(obj3, "newer context"); 321 final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); 322 final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); 323 324 t3.start(); 325 t4.start(); 326 t3.join(); 327 t4.join(); 328 329 Assert.assertEquals(e.eval(sharedScript), "original context"); 330 Assert.assertEquals(e.eval(sharedScript, newCtxt), "newer context"); 331 } 332 333 /** 334 * Test multi-threaded access using the postfix ++ operator for shared script classes with multiple globals. 335 */ 336 @Test 337 public static void multiThreadedIncTest() throws ScriptException, InterruptedException { 338 final ScriptEngineManager m = new ScriptEngineManager(); 339 final ScriptEngine e = m.getEngineByName("nashorn"); 340 final Bindings b = e.createBindings(); 341 final ScriptContext origContext = e.getContext(); 342 final ScriptContext newCtxt = new SimpleScriptContext(); 343 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 344 345 assertEquals(e.eval("var x = 0;", origContext), null); 346 assertEquals(e.eval("var x = 2;", newCtxt), null); 347 final String sharedScript = "x++;"; 348 349 final Thread t1 = new Thread(new Runnable() { 350 @Override 351 public void run() { 352 try { 353 for (int i = 0; i < 1000; i++) { 354 assertEquals(e.eval(sharedScript, origContext), (double)i); 355 } 356 } catch (final ScriptException se) { 357 fail(se.toString()); 358 } 359 } 360 }); 361 final Thread t2 = new Thread(new Runnable() { 362 @Override 363 public void run() { 364 try { 365 for (int i = 2; i < 1000; i++) { 366 assertEquals(e.eval(sharedScript, newCtxt), (double)i); 367 } 368 } catch (final ScriptException se) { 369 fail(se.toString()); 370 } 371 } 372 }); 373 t1.start(); 374 t2.start(); 375 t1.join(); 376 t2.join(); 377 } 378 379 /** 380 * Test multi-threaded access to primitive prototype properties for shared script classes with multiple globals. 381 */ 382 @Test 383 public static void multiThreadedPrimitiveTest() throws ScriptException, InterruptedException { 384 final ScriptEngineManager m = new ScriptEngineManager(); 385 final ScriptEngine e = m.getEngineByName("nashorn"); 386 final Bindings b = e.createBindings(); 387 final ScriptContext origContext = e.getContext(); 388 final ScriptContext newCtxt = new SimpleScriptContext(); 389 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 390 391 final Object obj1 = e.eval("String.prototype.foo = 'original context';", origContext); 392 final Object obj2 = e.eval("String.prototype.foo = 'new context';", newCtxt); 393 assertEquals(obj1, "original context"); 394 assertEquals(obj2, "new context"); 395 final String sharedScript = "''.foo"; 396 397 final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); 398 final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); 399 t1.start(); 400 t2.start(); 401 t1.join(); 402 t2.join(); 403 404 final Object obj3 = e.eval("delete String.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt); 405 assertEquals(obj3, "newer context"); 406 final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); 407 final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); 408 409 t3.start(); 410 t4.start(); 411 t3.join(); 412 t4.join(); 413 414 Assert.assertEquals(e.eval(sharedScript), "original context"); 415 Assert.assertEquals(e.eval(sharedScript, newCtxt), "newer context"); 416 } 417 418 419 /** 420 * Test multi-threaded access to prototype user accessor properties for shared script classes with multiple globals. 421 */ 422 @Test 423 public static void multiThreadedAccessorTest() throws ScriptException, InterruptedException { 424 final ScriptEngineManager m = new ScriptEngineManager(); 425 final ScriptEngine e = m.getEngineByName("nashorn"); 426 final Bindings b = e.createBindings(); 427 final ScriptContext origContext = e.getContext(); 428 final ScriptContext newCtxt = new SimpleScriptContext(); 429 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 430 431 e.eval("Object.defineProperty(Object.prototype, 'foo', { get: function() 'original context' })", origContext); 432 e.eval("Object.defineProperty(Object.prototype, 'foo', { get: function() 'new context', configurable: true })", newCtxt); 433 final String sharedScript = "({}).foo"; 434 435 final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); 436 final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); 437 t1.start(); 438 t2.start(); 439 t1.join(); 440 t2.join(); 441 442 final Object obj3 = e.eval("delete Object.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt); 443 assertEquals(obj3, "newer context"); 444 final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); 445 final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); 446 447 t3.start(); 448 t4.start(); 449 t3.join(); 450 t4.join(); 451 } 452 453 /** 454 * Test multi-threaded access to primitive prototype user accessor properties for shared script classes with multiple globals. 455 */ 456 @Test 457 public static void multiThreadedPrimitiveAccessorTest() throws ScriptException, InterruptedException { 458 final ScriptEngineManager m = new ScriptEngineManager(); 459 final ScriptEngine e = m.getEngineByName("nashorn"); 460 final Bindings b = e.createBindings(); 461 final ScriptContext origContext = e.getContext(); 462 final ScriptContext newCtxt = new SimpleScriptContext(); 463 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 464 465 e.eval("Object.defineProperty(String.prototype, 'foo', { get: function() 'original context' })", origContext); 466 e.eval("Object.defineProperty(String.prototype, 'foo', { get: function() 'new context' })", newCtxt); 467 final String sharedScript = "''.foo"; 468 469 final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); 470 final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000)); 471 t1.start(); 472 t2.start(); 473 t1.join(); 474 t2.join(); 475 476 final Object obj3 = e.eval("delete String.prototype.foo; Object.prototype.foo = 'newer context';", newCtxt); 477 assertEquals(obj3, "newer context"); 478 final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000)); 479 final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000)); 480 481 t3.start(); 482 t4.start(); 483 t3.join(); 484 t4.join(); 485 } 486 487 /** 488 * Test multi-threaded scope function invocation for shared script classes with multiple globals. 489 */ 490 @Test 491 public static void multiThreadedFunctionTest() throws ScriptException, InterruptedException { 492 final ScriptEngineManager m = new ScriptEngineManager(); 493 final ScriptEngine e = m.getEngineByName("nashorn"); 494 final Bindings b = e.createBindings(); 495 final ScriptContext origContext = e.getContext(); 496 final ScriptContext newCtxt = new SimpleScriptContext(); 497 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 498 499 e.eval(new URLReader(ScopeTest.class.getResource("resources/func.js")), origContext); 500 assertEquals(origContext.getAttribute("scopeVar"), 1); 501 assertEquals(e.eval("scopeTest()"), 1); 502 503 e.eval(new URLReader(ScopeTest.class.getResource("resources/func.js")), newCtxt); 504 assertEquals(newCtxt.getAttribute("scopeVar"), 1); 505 assertEquals(e.eval("scopeTest();", newCtxt), 1); 506 507 assertEquals(e.eval("scopeVar = 3;", newCtxt), 3); 508 assertEquals(newCtxt.getAttribute("scopeVar"), 3); 509 510 511 final Thread t1 = new Thread(new ScriptRunner(e, origContext, "scopeTest()", 1, 1000)); 512 final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, "scopeTest()", 3, 1000)); 513 514 t1.start(); 515 t2.start(); 516 t1.join(); 517 t2.join(); 518 519 } 520 521 /** 522 * Test multi-threaded access to global getters and setters for shared script classes with multiple globals. 523 */ 524 @Test 525 public static void getterSetterTest() throws ScriptException, InterruptedException { 526 final ScriptEngineManager m = new ScriptEngineManager(); 527 final ScriptEngine e = m.getEngineByName("nashorn"); 528 final Bindings b = e.createBindings(); 529 final ScriptContext origContext = e.getContext(); 530 final ScriptContext newCtxt = new SimpleScriptContext(); 531 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 532 final String sharedScript = "accessor1"; 533 534 e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), origContext); 535 assertEquals(e.eval("accessor1 = 1;"), 1); 536 assertEquals(e.eval(sharedScript), 1); 537 538 e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), newCtxt); 539 assertEquals(e.eval("accessor1 = 2;", newCtxt), 2); 540 assertEquals(e.eval(sharedScript, newCtxt), 2); 541 542 543 final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, 1, 1000)); 544 final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, 2, 1000)); 545 546 t1.start(); 547 t2.start(); 548 t1.join(); 549 t2.join(); 550 551 assertEquals(e.eval(sharedScript), 1); 552 assertEquals(e.eval(sharedScript, newCtxt), 2); 553 assertEquals(e.eval("v"), 1); 554 assertEquals(e.eval("v", newCtxt), 2); 555 } 556 557 /** 558 * Test multi-threaded access to global getters and setters for shared script classes with multiple globals. 559 */ 560 @Test 561 public static void getterSetter2Test() throws ScriptException, InterruptedException { 562 final ScriptEngineManager m = new ScriptEngineManager(); 563 final ScriptEngine e = m.getEngineByName("nashorn"); 564 final Bindings b = e.createBindings(); 565 final ScriptContext origContext = e.getContext(); 566 final ScriptContext newCtxt = new SimpleScriptContext(); 567 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 568 final String sharedScript = "accessor2"; 569 570 e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), origContext); 571 assertEquals(e.eval("accessor2 = 1;"), 1); 572 assertEquals(e.eval(sharedScript), 1); 573 574 e.eval(new URLReader(ScopeTest.class.getResource("resources/gettersetter.js")), newCtxt); 575 assertEquals(e.eval("accessor2 = 2;", newCtxt), 2); 576 assertEquals(e.eval(sharedScript, newCtxt), 2); 577 578 579 final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, 1, 1000)); 580 final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, 2, 1000)); 581 582 t1.start(); 583 t2.start(); 584 t1.join(); 585 t2.join(); 586 587 assertEquals(e.eval(sharedScript), 1); 588 assertEquals(e.eval(sharedScript, newCtxt), 2); 589 assertEquals(e.eval("x"), 1); 590 assertEquals(e.eval("x", newCtxt), 2); 591 } 592 593 // @bug 8058422: Users should be able to overwrite "context" and "engine" variables 594 @Test 595 public static void contextOverwriteTest() throws ScriptException { 596 final ScriptEngineManager m = new ScriptEngineManager(); 597 final ScriptEngine e = m.getEngineByName("nashorn"); 598 final Bindings b = new SimpleBindings(); 599 b.put("context", "hello"); 600 b.put("foo", 32); 601 final ScriptContext newCtxt = new SimpleScriptContext(); 602 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 603 e.setContext(newCtxt); 604 assertEquals(e.eval("context"), "hello"); 605 assertEquals(((Number)e.eval("foo")).intValue(), 32); 606 } 607 608 // @bug 8058422: Users should be able to overwrite "context" and "engine" variables 609 @Test 610 public static void contextOverwriteInScriptTest() throws ScriptException { 611 final ScriptEngineManager m = new ScriptEngineManager(); 612 final ScriptEngine e = m.getEngineByName("nashorn"); 613 e.put("foo", 32); 614 615 assertEquals(((Number)e.eval("foo")).intValue(), 32); 616 assertEquals(e.eval("context = 'bar'"), "bar"); 617 assertEquals(((Number)e.eval("foo")).intValue(), 32); 618 } 619 620 // @bug 8058422: Users should be able to overwrite "context" and "engine" variables 621 @Test 622 public static void engineOverwriteTest() throws ScriptException { 623 final ScriptEngineManager m = new ScriptEngineManager(); 624 final ScriptEngine e = m.getEngineByName("nashorn"); 625 final Bindings b = new SimpleBindings(); 626 b.put("engine", "hello"); 627 b.put("foo", 32); 628 final ScriptContext newCtxt = new SimpleScriptContext(); 629 newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 630 e.setContext(newCtxt); 631 assertEquals(e.eval("engine"), "hello"); 632 assertEquals(((Number)e.eval("foo")).intValue(), 32); 633 } 634 635 // @bug 8058422: Users should be able to overwrite "context" and "engine" variables 636 @Test 637 public static void engineOverwriteInScriptTest() throws ScriptException { 638 final ScriptEngineManager m = new ScriptEngineManager(); 639 final ScriptEngine e = m.getEngineByName("nashorn"); 640 e.put("foo", 32); 641 642 assertEquals(((Number)e.eval("foo")).intValue(), 32); 643 assertEquals(e.eval("engine = 'bar'"), "bar"); 644 assertEquals(((Number)e.eval("foo")).intValue(), 32); 645 } 646 647 // @bug 8044750: megamorphic getter for scope objects does not call __noSuchProperty__ hook 648 @Test 649 public static void testMegamorphicGetInGlobal() throws Exception { 650 final ScriptEngineManager m = new ScriptEngineManager(); 651 final ScriptEngine engine = m.getEngineByName("nashorn"); 652 final String script = "foo"; 653 // "foo" is megamorphic because of different global scopes. 654 // Make sure ScriptContext variable search works even after 655 // it becomes megamorphic. 656 for (int index = 0; index < 25; index++) { 657 final Bindings bindings = new SimpleBindings(); 658 bindings.put("foo", index); 659 final Number value = (Number)engine.eval(script, bindings); 660 assertEquals(index, value.intValue()); 661 } 662 } 663 664 /** 665 * Test "slow" scopes involving {@code with} and {@code eval} statements for shared script classes with multiple globals. 666 * @throws ScriptException 667 * @throws InterruptedException 668 */ 669 @Test 670 public static void testSlowScope() throws ScriptException, InterruptedException { 671 final ScriptEngineManager m = new ScriptEngineManager(); 672 final ScriptEngine e = m.getEngineByName("nashorn"); 673 674 for (int i = 0; i < 100; i++) { 675 final Bindings b = e.createBindings(); 676 final ScriptContext ctxt = new SimpleScriptContext(); 677 ctxt.setBindings(b, ScriptContext.ENGINE_SCOPE); 678 679 e.eval(new URLReader(ScopeTest.class.getResource("resources/witheval.js")), ctxt); 680 assertEquals(e.eval("a", ctxt), 1); 681 assertEquals(b.get("a"), 1); 682 assertEquals(e.eval("b", ctxt), 3); 683 assertEquals(b.get("b"), 3); 684 assertEquals(e.eval("c", ctxt), 10); 685 assertEquals(b.get("c"), 10); 686 } 687 } 688 689 private static class ScriptRunner implements Runnable { 690 691 final ScriptEngine engine; 692 final ScriptContext context; 693 final String source; 694 final Object expected; 695 final int iterations; 696 697 ScriptRunner(final ScriptEngine engine, final ScriptContext context, final String source, final Object expected, final int iterations) { 698 this.engine = engine; 699 this.context = context; 700 this.source = source; 701 this.expected = expected; 702 this.iterations = iterations; 703 } 704 705 @Override 706 public void run() { 707 try { 708 for (int i = 0; i < iterations; i++) { 709 assertEquals(engine.eval(source, context), expected); 710 } 711 } catch (final ScriptException se) { 712 throw new RuntimeException(se); 713 } 714 } 715 } 716 717 // @bug 8071678: NashornScriptEngine returns javax.script.ScriptContext instance 718 // with get/setAttribute methods insonsistent for GLOBAL_SCOPE 719 @Test 720 public void testGlobalScopeSearch() throws Exception { 721 final ScriptEngineManager m = new ScriptEngineManager(); 722 final ScriptEngine e = m.getEngineByName("nashorn"); 723 final ScriptContext c = e.getContext(); 724 c.setAttribute("name1234", "value", ScriptContext.GLOBAL_SCOPE); 725 assertEquals(c.getAttribute("name1234"), "value"); 726 assertEquals(c.getAttributesScope("name1234"), 727 ScriptContext.GLOBAL_SCOPE); 728 } 729 730 // @bug 8071594: NashornScriptEngine returns javax.script.ScriptContext instance 731 // which doesn't completely conform to the spec regarding exceptions throwing 732 @Test 733 public void testScriptContext_NPE_IAE() throws Exception { 734 final ScriptEngineManager m = new ScriptEngineManager(); 735 final ScriptEngine e = m.getEngineByName("nashorn"); 736 final ScriptContext c = e.getContext(); 737 try { 738 c.getAttribute(""); 739 throw new AssertionError("should have thrown IAE"); 740 } catch (final IllegalArgumentException iae1) {} 741 742 try { 743 c.getAttribute(null); 744 throw new AssertionError("should have thrown NPE"); 745 } catch (final NullPointerException npe1) {} 746 747 try { 748 c.getAttribute("", ScriptContext.ENGINE_SCOPE); 749 throw new AssertionError("should have thrown IAE"); 750 } catch (final IllegalArgumentException iae2) {} 751 752 try { 753 c.getAttribute(null, ScriptContext.ENGINE_SCOPE); 754 throw new AssertionError("should have thrown NPE"); 755 } catch (final NullPointerException npe2) {} 756 757 try { 758 c.removeAttribute("", ScriptContext.ENGINE_SCOPE); 759 throw new AssertionError("should have thrown IAE"); 760 } catch (final IllegalArgumentException iae3) {} 761 762 try { 763 c.removeAttribute(null, ScriptContext.ENGINE_SCOPE); 764 throw new AssertionError("should have thrown NPE"); 765 } catch (final NullPointerException npe3) {} 766 767 try { 768 c.setAttribute("", "value", ScriptContext.ENGINE_SCOPE); 769 throw new AssertionError("should have thrown IAE"); 770 } catch (final IllegalArgumentException iae4) {} 771 772 try { 773 c.setAttribute(null, "value", ScriptContext.ENGINE_SCOPE); 774 throw new AssertionError("should have thrown NPE"); 775 } catch (final NullPointerException npe4) {} 776 777 try { 778 c.getAttributesScope(""); 779 throw new AssertionError("should have thrown IAE"); 780 } catch (final IllegalArgumentException iae5) {} 781 782 try { 783 c.getAttributesScope(null); 784 throw new AssertionError("should have thrown NPE"); 785 } catch (final NullPointerException npe5) {} 786 } 787 788 public static class RecursiveEval { 789 private final ScriptEngineFactory factory = new NashornScriptEngineFactory(); 790 private final ScriptEngine engine = factory.getScriptEngine(); 791 private final Bindings engineBindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); 792 793 public void program() throws ScriptException { 794 final ScriptContext sc = new SimpleScriptContext(); 795 final Bindings global = new SimpleBindings(); 796 sc.setBindings(global, ScriptContext.GLOBAL_SCOPE); 797 sc.setBindings(engineBindings, ScriptContext.ENGINE_SCOPE); 798 global.put("text", "programText"); 799 String value = engine.eval("text", sc).toString(); 800 Assert.assertEquals(value, "programText"); 801 engine.put("program", this); 802 engine.eval("program.method()"); 803 // eval again from here! 804 value = engine.eval("text", sc).toString(); 805 Assert.assertEquals(value, "programText"); 806 } 807 808 public void method() throws ScriptException { 809 // a context with a new global bindings, same engine bindings 810 final ScriptContext sc = new SimpleScriptContext(); 811 final Bindings global = new SimpleBindings(); 812 sc.setBindings(global, ScriptContext.GLOBAL_SCOPE); 813 sc.setBindings(engineBindings, ScriptContext.ENGINE_SCOPE); 814 global.put("text", "methodText"); 815 final String value = engine.eval("text", sc).toString(); 816 Assert.assertEquals(value, "methodText"); 817 } 818 } 819 820 // @bug 8081609: engine.eval call from a java method which 821 // was called from a previous engine.eval results in wrong 822 // ScriptContext being used. 823 @Test 824 public void recursiveEvalCallScriptContextTest() throws ScriptException { 825 new RecursiveEval().program(); 826 } 827 828 private static final String VAR_NAME = "myvar"; 829 830 private static boolean lookupVar(final ScriptEngine engine, final String varName) { 831 try { 832 engine.eval(varName); 833 return true; 834 } catch (final ScriptException se) { 835 return false; 836 } 837 } 838 839 // @bug 8136544: Call site switching to megamorphic causes incorrect property read 840 @Test 841 public void megamorphicPropertyReadTest() throws ScriptException { 842 final NashornScriptEngineFactory factory = new NashornScriptEngineFactory(); 843 final ScriptEngine engine = factory.getScriptEngine(); 844 final Bindings scope = engine.getBindings(ScriptContext.ENGINE_SCOPE); 845 boolean ret; 846 847 // Why 16 is the upper limit of this loop? The default nashorn dynalink megamorphic threshold is 16. 848 // See jdk.nashorn.internal.runtime.linker.Bootstrap.NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD 849 // We do, 'eval' of the same in this loop twice. So, 16*2 = 32 times that callsite in the script 850 // is exercised - much beyond the default megamorphic threshold. 851 852 for (int i = 0; i < 16; i++) { 853 scope.remove(VAR_NAME); 854 ret = lookupVar(engine, VAR_NAME); 855 assertFalse(ret, "Expected false in iteration " + i); 856 scope.put(VAR_NAME, "foo"); 857 ret = lookupVar(engine, VAR_NAME); 858 assertTrue(ret, "Expected true in iteration " + i); 859 } 860 } 861 862 // @bug 8138616: invokeFunction fails if function calls a function defined in GLOBAL_SCOPE 863 @Test 864 public void invokeFunctionInGlobalScopeTest() throws Exception { 865 final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); 866 final ScriptContext ctxt = engine.getContext(); 867 868 // define a function called "func" 869 engine.eval("func = function() { return 42 }"); 870 871 // move ENGINE_SCOPE Bindings to GLOBAL_SCOPE 872 ctxt.setBindings(ctxt.getBindings(ScriptContext.ENGINE_SCOPE), ScriptContext.GLOBAL_SCOPE); 873 874 // create a new Bindings and set as ENGINE_SCOPE 875 ctxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); 876 877 // define new function that calls "func" now in GLOBAL_SCOPE 878 engine.eval("newfunc = function() { return func() }"); 879 880 // call "newfunc" and check the return value 881 final Object value = ((Invocable)engine).invokeFunction("newfunc"); 882 assertTrue(((Number)value).intValue() == 42); 883 } 884 885 886 // @bug 8138616: invokeFunction fails if function calls a function defined in GLOBAL_SCOPE 887 // variant of above that replaces default ScriptContext of the engine with a fresh instance! 888 @Test 889 public void invokeFunctionInGlobalScopeTest2() throws Exception { 890 final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); 891 892 // create a new ScriptContext instance 893 final ScriptContext ctxt = new SimpleScriptContext(); 894 // set it as 'default' ScriptContext 895 engine.setContext(ctxt); 896 897 // create a new Bindings and set as ENGINE_SCOPE 898 ctxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); 899 900 // define a function called "func" 901 engine.eval("func = function() { return 42 }"); 902 903 // move ENGINE_SCOPE Bindings to GLOBAL_SCOPE 904 ctxt.setBindings(ctxt.getBindings(ScriptContext.ENGINE_SCOPE), ScriptContext.GLOBAL_SCOPE); 905 906 // create a new Bindings and set as ENGINE_SCOPE 907 ctxt.setBindings(engine.createBindings(), ScriptContext.ENGINE_SCOPE); 908 909 // define new function that calls "func" now in GLOBAL_SCOPE 910 engine.eval("newfunc = function() { return func() }"); 911 912 // call "newfunc" and check the return value 913 final Object value = ((Invocable)engine).invokeFunction("newfunc"); 914 assertTrue(((Number)value).intValue() == 42); 915 } 916 917 // @bug 8150219 ReferenceError in 1.8.0_72 918 // When we create a Global for a non-default ScriptContext that needs one keep the 919 // ScriptContext associated with the Global so that invoke methods work as expected. 920 @Test 921 public void invokeFunctionWithCustomScriptContextTest() throws Exception { 922 final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn"); 923 924 // create an engine and a ScriptContext, but don't set it as default 925 final ScriptContext scriptContext = new SimpleScriptContext(); 926 927 // Set some value in the context 928 scriptContext.setAttribute("myString", "foo", ScriptContext.ENGINE_SCOPE); 929 930 // Evaluate script with custom context and get back a function 931 final String script = "function (c) { return myString.indexOf(c); }"; 932 final CompiledScript compiledScript = ((Compilable)engine).compile(script); 933 final Object func = compiledScript.eval(scriptContext); 934 935 // Invoked function should be able to see context it was evaluated with 936 final Object result = ((Invocable) engine).invokeMethod(func, "call", func, "o", null); 937 assertTrue(((Number)result).intValue() == 1); 938 } 939} 940