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