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