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 }