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