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