1 /*
   2  * Copyright (c) 2014, 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 
  26 package jdk.nashorn.internal.runtime.test;
  27 
  28 import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
  29 import org.testng.annotations.Test;
  30 
  31 import javax.script.Bindings;
  32 import javax.script.ScriptContext;
  33 import javax.script.ScriptEngine;
  34 import javax.script.ScriptException;
  35 import javax.script.SimpleScriptContext;
  36 
  37 import static org.testng.Assert.assertEquals;
  38 
  39 /**
  40  * Top-level lexical binding tests.
  41  *
  42  * @test
  43  * @run testng jdk.nashorn.internal.runtime.test.LexicalBindingTest
  44  */
  45 @SuppressWarnings("javadoc")
  46 public class LexicalBindingTest {
  47 
  48     final static String LANGUAGE_ES6 = "--language=es6";
  49     final static int NUMBER_OF_CONTEXTS = 40;
  50     final static int MEGAMORPHIC_LOOP_COUNT = 40;
  51 
  52     /**
  53      * Test access to global var-declared variables for shared script classes with multiple globals.
  54      */
  55     @Test
  56     public static void megamorphicVarTest() throws ScriptException, InterruptedException {
  57         final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
  58         final ScriptEngine e = factory.getScriptEngine();
  59         final ScriptContext[] contexts = new ScriptContext[NUMBER_OF_CONTEXTS];
  60         final String sharedScript1 = "foo";
  61         final String sharedScript2 = "bar = foo; bar";
  62 
  63 
  64         for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
  65             final ScriptContext context = contexts[i] = new SimpleScriptContext();
  66             final Bindings b = e.createBindings();
  67             context.setBindings(b, ScriptContext.ENGINE_SCOPE);
  68             assertEquals(e.eval("var foo = '" + i + "'; var bar;", context), null);
  69         }
  70 
  71         for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
  72             final ScriptContext context = contexts[i];
  73             assertEquals(e.eval(sharedScript1, context), String.valueOf(i));
  74             assertEquals(e.eval(sharedScript2, context), String.valueOf(i));
  75         }
  76     }
  77 
  78     /**
  79      * Test access to global lexically declared variables for shared script classes with multiple globals.
  80      */
  81     @Test
  82     public static void megamorphicMultiGlobalLetTest() throws ScriptException, InterruptedException {
  83         final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
  84         final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
  85         final ScriptContext[] contexts = new ScriptContext[NUMBER_OF_CONTEXTS];
  86         final String sharedScript1 = "foo";
  87         final String sharedScript2 = "bar = foo; bar";
  88 
  89 
  90         for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
  91             final ScriptContext context = contexts[i] = new SimpleScriptContext();
  92             final Bindings b = e.createBindings();
  93             context.setBindings(b, ScriptContext.ENGINE_SCOPE);
  94             assertEquals(e.eval("let foo = '" + i + "'; let bar; ", context), null);
  95         }
  96 
  97         for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
  98             final ScriptContext context = contexts[i];
  99             assertEquals(e.eval(sharedScript1, context), String.valueOf(i));
 100             assertEquals(e.eval(sharedScript2, context), String.valueOf(i));
 101         }
 102     }
 103 
 104 
 105     /**
 106      * Test access to global lexically declared variables for shared script classes with single global.
 107      */
 108     @Test
 109     public static void megamorphicSingleGlobalLetTest() throws ScriptException, InterruptedException {
 110         final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
 111         final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
 112         final String sharedGetterScript = "foo";
 113         final String sharedSetterScript = "foo = 1";
 114 
 115         for (int i = 0; i < MEGAMORPHIC_LOOP_COUNT; i++) {
 116             assertEquals(e.eval(sharedSetterScript), 1);
 117             assertEquals(e.eval(sharedGetterScript), 1);
 118             assertEquals(e.eval("delete foo; a" + i + " = 1; foo = " + i + ";"), i);
 119             assertEquals(e.eval(sharedGetterScript), i);
 120         }
 121 
 122         assertEquals(e.eval("let foo = 'foo';"), null);
 123         assertEquals(e.eval(sharedGetterScript), "foo");
 124         assertEquals(e.eval(sharedSetterScript), 1);
 125         assertEquals(e.eval(sharedGetterScript), 1);
 126         assertEquals(e.eval("this.foo"), MEGAMORPHIC_LOOP_COUNT - 1);
 127     }
 128 
 129     /**
 130      * Test access to global lexically declared variables for shared script classes with single global.
 131      */
 132     @Test
 133     public static void megamorphicInheritedGlobalLetTest() throws ScriptException, InterruptedException {
 134         final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
 135         final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
 136         final String sharedGetterScript = "foo";
 137         final String sharedSetterScript = "foo = 1";
 138 
 139         for (int i = 0; i < MEGAMORPHIC_LOOP_COUNT; i++) {
 140             assertEquals(e.eval(sharedSetterScript), 1);
 141             assertEquals(e.eval(sharedGetterScript), 1);
 142             assertEquals(e.eval("delete foo; a" + i + " = 1; Object.prototype.foo = " + i + ";"), i);
 143             assertEquals(e.eval(sharedGetterScript), i);
 144         }
 145 
 146         assertEquals(e.eval("let foo = 'foo';"), null);
 147         assertEquals(e.eval(sharedGetterScript), "foo");
 148         assertEquals(e.eval(sharedSetterScript), 1);
 149         assertEquals(e.eval(sharedGetterScript), 1);
 150         assertEquals(e.eval("this.foo"), MEGAMORPHIC_LOOP_COUNT - 1);
 151     }
 152 
 153     /**
 154      * Test multi-threaded access to global lexically declared variables for shared script classes with multiple globals.
 155      */
 156     @Test
 157     public static void multiThreadedLetTest() throws ScriptException, InterruptedException {
 158         final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
 159         final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
 160         final Bindings b = e.createBindings();
 161         final ScriptContext origContext = e.getContext();
 162         final ScriptContext newCtxt = new SimpleScriptContext();
 163         newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
 164         final String sharedScript = "foo";
 165 
 166         assertEquals(e.eval("let foo = 'original context';", origContext), null);
 167         assertEquals(e.eval("let foo = 'new context';", newCtxt), null);
 168 
 169         final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
 170         final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000));
 171         t1.start();
 172         t2.start();
 173         t1.join();
 174         t2.join();
 175 
 176         assertEquals(e.eval("foo = 'newer context';", newCtxt), "newer context");
 177         final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
 178         final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000));
 179 
 180         t3.start();
 181         t4.start();
 182         t3.join();
 183         t4.join();
 184 
 185         assertEquals(e.eval(sharedScript), "original context");
 186         assertEquals(e.eval(sharedScript, newCtxt), "newer context");
 187     }
 188 
 189     /**
 190      * Make sure lexically defined variables are accessible in other scripts.
 191      */
 192     @Test
 193     public void lexicalScopeTest() throws ScriptException {
 194         final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
 195         final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
 196 
 197         e.eval("let x; const y = 'world';");
 198 
 199         assertEquals(e.eval("x = 'hello'"), "hello");
 200         assertEquals(e.eval("typeof x"), "string");
 201         assertEquals(e.eval("typeof y"), "string");
 202         assertEquals(e.eval("x"), "hello");
 203         assertEquals(e.eval("y"), "world");
 204         assertEquals(e.eval("typeof this.x"), "undefined");
 205         assertEquals(e.eval("typeof this.y"), "undefined");
 206         assertEquals(e.eval("this.x"), null);
 207         assertEquals(e.eval("this.y"), null);
 208     }
 209 
 210     private static class ScriptRunner implements Runnable {
 211 
 212         final ScriptEngine engine;
 213         final ScriptContext context;
 214         final String source;
 215         final Object expected;
 216         final int iterations;
 217 
 218         ScriptRunner(final ScriptEngine engine, final ScriptContext context, final String source, final Object expected, final int iterations) {
 219             this.engine = engine;
 220             this.context = context;
 221             this.source = source;
 222             this.expected = expected;
 223             this.iterations = iterations;
 224         }
 225 
 226         @Override
 227         public void run() {
 228             try {
 229                 for (int i = 0; i < iterations; i++) {
 230                     assertEquals(engine.eval(source, context), expected);
 231                 }
 232             } catch (final ScriptException se) {
 233                 throw new RuntimeException(se);
 234             }
 235         }
 236     }
 237 }