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;
  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.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 = 20;
  50     final static int MEGAMORPHIC_LOOP_COUNT = 20;
  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 sharedScript = "foo";
  61 
  62 
  63         for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
  64             final ScriptContext context = contexts[i] = new SimpleScriptContext();
  65             final Bindings b = e.createBindings();
  66             context.setBindings(b, ScriptContext.ENGINE_SCOPE);
  67             assertEquals(e.eval("var foo = '" + i + "';", context), null);
  68         }
  69 
  70         for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
  71             final ScriptContext context = contexts[i];
  72             assertEquals(e.eval(sharedScript, context), String.valueOf(i));
  73         }
  74     }
  75 
  76     /**
  77      * Test access to global lexically declared variables for shared script classes with multiple globals.
  78      */
  79     @Test
  80     public static void megamorphicMultiGlobalLetTest() throws ScriptException, InterruptedException {
  81         final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
  82         final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
  83         final ScriptContext[] contexts = new ScriptContext[NUMBER_OF_CONTEXTS];
  84         final String sharedScript = "foo";
  85 
  86 
  87         for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
  88             final ScriptContext context = contexts[i] = new SimpleScriptContext();
  89             final Bindings b = e.createBindings();
  90             context.setBindings(b, ScriptContext.ENGINE_SCOPE);
  91             assertEquals(e.eval("let foo = '" + i + "';", context), null);
  92         }
  93 
  94         for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
  95             final ScriptContext context = contexts[i];
  96             assertEquals(e.eval(sharedScript, context), String.valueOf(i));
  97         }
  98     }
  99 
 100 
 101     /**
 102      * Test access to global lexically declared variables for shared script classes with single global.
 103      */
 104     @Test
 105     public static void megamorphicSingleGlobalLetTest() throws ScriptException, InterruptedException {
 106         final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
 107         final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
 108         final String sharedGetterScript = "foo";
 109         final String sharedSetterScript = "foo = 1";
 110 
 111         for (int i = 0; i < MEGAMORPHIC_LOOP_COUNT; i++) {
 112             assertEquals(e.eval(sharedSetterScript), 1);
 113             assertEquals(e.eval(sharedGetterScript), 1);
 114             assertEquals(e.eval("delete foo; a" + i + " = 1; foo = " + i + ";"), i);
 115             assertEquals(e.eval(sharedGetterScript), i);
 116         }
 117 
 118         assertEquals(e.eval("let foo = 'foo';"), null);
 119         assertEquals(e.eval(sharedGetterScript), "foo");
 120         assertEquals(e.eval(sharedSetterScript), 1);
 121         assertEquals(e.eval(sharedGetterScript), 1);
 122         assertEquals(e.eval("this.foo"), MEGAMORPHIC_LOOP_COUNT - 1);
 123     }
 124 
 125     /**
 126      * Test access to global lexically declared variables for shared script classes with single global.
 127      */
 128     @Test
 129     public static void megamorphicInheritedGlobalLetTest() throws ScriptException, InterruptedException {
 130         final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
 131         final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
 132         final String sharedGetterScript = "foo";
 133         final String sharedSetterScript = "foo = 1";
 134 
 135         for (int i = 0; i < MEGAMORPHIC_LOOP_COUNT; i++) {
 136             assertEquals(e.eval(sharedSetterScript), 1);
 137             assertEquals(e.eval(sharedGetterScript), 1);
 138             assertEquals(e.eval("delete foo; a" + i + " = 1; Object.prototype.foo = " + i + ";"), i);
 139             assertEquals(e.eval(sharedGetterScript), i);
 140         }
 141 
 142         assertEquals(e.eval("let foo = 'foo';"), null);
 143         assertEquals(e.eval(sharedGetterScript), "foo");
 144         assertEquals(e.eval(sharedSetterScript), 1);
 145         assertEquals(e.eval(sharedGetterScript), 1);
 146         assertEquals(e.eval("this.foo"), MEGAMORPHIC_LOOP_COUNT - 1);
 147     }
 148 
 149     /**
 150      * Test multi-threaded access to global lexically declared variables for shared script classes with multiple globals.
 151      */
 152     @Test
 153     public static void multiThreadedLetTest() throws ScriptException, InterruptedException {
 154         final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
 155         final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
 156         final Bindings b = e.createBindings();
 157         final ScriptContext origContext = e.getContext();
 158         final ScriptContext newCtxt = new SimpleScriptContext();
 159         newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
 160         final String sharedScript = "foo";
 161 
 162         assertEquals(e.eval("let foo = 'original context';", origContext), null);
 163         assertEquals(e.eval("let foo = 'new context';", newCtxt), null);
 164 
 165         final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
 166         final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000));
 167         t1.start();
 168         t2.start();
 169         t1.join();
 170         t2.join();
 171 
 172         assertEquals(e.eval("foo = 'newer context';", newCtxt), "newer context");
 173         final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
 174         final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000));
 175 
 176         t3.start();
 177         t4.start();
 178         t3.join();
 179         t4.join();
 180 
 181         assertEquals(e.eval(sharedScript), "original context");
 182         assertEquals(e.eval(sharedScript, newCtxt), "newer context");
 183     }
 184 
 185     private static class ScriptRunner implements Runnable {
 186 
 187         final ScriptEngine engine;
 188         final ScriptContext context;
 189         final String source;
 190         final Object expected;
 191         final int iterations;
 192 
 193         ScriptRunner(final ScriptEngine engine, final ScriptContext context, final String source, final Object expected, final int iterations) {
 194             this.engine = engine;
 195             this.context = context;
 196             this.source = source;
 197             this.expected = expected;
 198             this.iterations = iterations;
 199         }
 200 
 201         @Override
 202         public void run() {
 203             try {
 204                 for (int i = 0; i < iterations; i++) {
 205                     assertEquals(engine.eval(source, context), expected);
 206                 }
 207             } catch (final ScriptException se) {
 208                 throw new RuntimeException(se);
 209             }
 210         }
 211     }
 212 }