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