changeset 1102:c3a510b73875

8057691: Nashorn: let & const declarations are not shared between scripts Reviewed-by: lagergren, attila
author hannesw
date Mon, 24 Nov 2014 12:03:15 +0100
parents c22dd9ae7ff0
children ac111e4cb1dc
files src/jdk/nashorn/internal/codegen/AssignSymbols.java src/jdk/nashorn/internal/codegen/MapCreator.java src/jdk/nashorn/internal/objects/Global.java src/jdk/nashorn/internal/parser/Parser.java src/jdk/nashorn/internal/runtime/Property.java src/jdk/nashorn/internal/runtime/ScriptObject.java test/script/basic/es6/const-redeclare-extra.js.EXPECTED test/script/basic/es6/let-load.js test/script/basic/es6/let-load.js.EXPECTED test/script/basic/es6/let-redeclare-extra.js.EXPECTED test/script/basic/es6/lexical-toplevel-def.js test/script/basic/es6/lexical-toplevel-print.js test/script/basic/es6/lexical-toplevel-redeclare-func-on-let.js test/script/basic/es6/lexical-toplevel-redeclare-let-on-builtin.js test/script/basic/es6/lexical-toplevel-redeclare-let-on-func.js test/script/basic/es6/lexical-toplevel-redeclare-let-on-global.js test/script/basic/es6/lexical-toplevel-redeclare-let-on-var.js test/script/basic/es6/lexical-toplevel-redeclare-var-on-let.js test/script/basic/es6/lexical-toplevel-redeclare.js test/script/basic/es6/lexical-toplevel-redeclare.js.EXPECTED test/script/basic/es6/lexical-toplevel.js test/script/basic/es6/lexical-toplevel.js.EXPECTED test/src/jdk/nashorn/internal/runtime/LexicalBindingTest.java
diffstat 23 files changed, 983 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/src/jdk/nashorn/internal/codegen/AssignSymbols.java	Fri Nov 21 20:17:02 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/AssignSymbols.java	Mon Nov 24 12:03:15 2014 +0100
@@ -356,6 +356,10 @@
                         throwParserException(ECMAErrors.getMessage("syntax.error.redeclare.variable", name), origin);
                     } else {
                         symbol.setHasBeenDeclared();
+                        // Set scope flag on top-level block scoped symbols
+                        if (function.isProgram() && function.getBody() == block) {
+                            symbol.setIsScope();
+                        }
                     }
                 } else if ((flags & IS_INTERNAL) != 0) {
                     // Always create a new definition.
@@ -540,7 +544,7 @@
         final int flags;
         if (varNode.isAnonymousFunctionDeclaration()) {
             flags = IS_INTERNAL;
-        } else if (lc.getCurrentFunction().isProgram()) {
+        } else if (!varNode.isBlockScoped() && lc.getCurrentFunction().isProgram()) {
             flags = IS_SCOPE;
         } else {
             flags = 0;
--- a/src/jdk/nashorn/internal/codegen/MapCreator.java	Fri Nov 21 20:17:02 2014 +0100
+++ b/src/jdk/nashorn/internal/codegen/MapCreator.java	Mon Nov 24 12:03:15 2014 +0100
@@ -152,6 +152,10 @@
             flags |= Property.NOT_WRITABLE;
         }
 
+        if (symbol.isBlockScoped()) {
+            flags |= Property.IS_LEXICAL_BINDING;
+        }
+
         // Mark symbol as needing declaration. Access before declaration will throw a ReferenceError.
         if (symbol.isBlockScoped() && symbol.isScope()) {
             flags |= Property.NEEDS_DECLARATION;
--- a/src/jdk/nashorn/internal/objects/Global.java	Fri Nov 21 20:17:02 2014 +0100
+++ b/src/jdk/nashorn/internal/objects/Global.java	Mon Nov 24 12:03:15 2014 +0100
@@ -34,6 +34,7 @@
 import java.io.PrintWriter;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
 import java.lang.invoke.SwitchPoint;
 import java.lang.reflect.Field;
 import java.util.ArrayList;
@@ -44,6 +45,7 @@
 import java.util.concurrent.ConcurrentHashMap;
 import javax.script.ScriptContext;
 import javax.script.ScriptEngine;
+import jdk.internal.dynalink.CallSiteDescriptor;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.linker.LinkRequest;
 import jdk.nashorn.api.scripting.ClassFilter;
@@ -54,6 +56,8 @@
 import jdk.nashorn.internal.objects.annotations.ScriptClass;
 import jdk.nashorn.internal.runtime.ConsString;
 import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ECMAErrors;
+import jdk.nashorn.internal.runtime.GlobalConstants;
 import jdk.nashorn.internal.runtime.GlobalFunctions;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.NativeJavaPackage;
@@ -69,6 +73,7 @@
 import jdk.nashorn.internal.runtime.arrays.ArrayData;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
 import jdk.nashorn.internal.runtime.linker.InvokeByName;
+import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
 import jdk.nashorn.internal.runtime.regexp.RegExpResult;
 import jdk.nashorn.internal.scripts.JO;
 
@@ -410,13 +415,14 @@
     // Used to store the last RegExp result to support deprecated RegExp constructor properties
     private RegExpResult lastRegExpResult;
 
-    private static final MethodHandle EVAL              = findOwnMH_S("eval",                Object.class, Object.class, Object.class);
-    private static final MethodHandle NO_SUCH_PROPERTY  = findOwnMH_S(NO_SUCH_PROPERTY_NAME, Object.class, Object.class, Object.class);
-    private static final MethodHandle PRINT             = findOwnMH_S("print",               Object.class, Object.class, Object[].class);
-    private static final MethodHandle PRINTLN           = findOwnMH_S("println",             Object.class, Object.class, Object[].class);
-    private static final MethodHandle LOAD              = findOwnMH_S("load",                Object.class, Object.class, Object.class);
-    private static final MethodHandle LOADWITHNEWGLOBAL = findOwnMH_S("loadWithNewGlobal",   Object.class, Object.class, Object[].class);
-    private static final MethodHandle EXIT              = findOwnMH_S("exit",                Object.class, Object.class, Object.class);
+    private static final MethodHandle EVAL                 = findOwnMH_S("eval",                Object.class, Object.class, Object.class);
+    private static final MethodHandle NO_SUCH_PROPERTY     = findOwnMH_S(NO_SUCH_PROPERTY_NAME, Object.class, Object.class, Object.class);
+    private static final MethodHandle PRINT                = findOwnMH_S("print",               Object.class, Object.class, Object[].class);
+    private static final MethodHandle PRINTLN              = findOwnMH_S("println",             Object.class, Object.class, Object[].class);
+    private static final MethodHandle LOAD                 = findOwnMH_S("load",                Object.class, Object.class, Object.class);
+    private static final MethodHandle LOAD_WITH_NEW_GLOBAL = findOwnMH_S("loadWithNewGlobal",   Object.class, Object.class, Object[].class);
+    private static final MethodHandle EXIT                 = findOwnMH_S("exit",                Object.class, Object.class, Object.class);
+    private static final MethodHandle LEXICAL_SCOPE_FILTER = findOwnMH_S("lexicalScopeFilter", Object.class, Object.class);
 
     // initialized by nasgen
     private static PropertyMap $nasgenmap$;
@@ -429,6 +435,12 @@
     // current ScriptEngine associated - can be null.
     private ScriptEngine engine;
 
+    // ES6 global lexical scope.
+    private final LexicalScope lexicalScope;
+
+    // Switchpoint for non-constant global callsites in the presence of ES6 lexical scope.
+    private SwitchPoint lexicalScopeSwitchPoint;
+
     /**
      * Set the current script context
      * @param scontext script context
@@ -466,6 +478,7 @@
         super(checkAndGetMap(context));
         this.context = context;
         this.setIsScope();
+        this.lexicalScope = context.getEnv()._es6 ? new LexicalScope(this) : null;
     }
 
     /**
@@ -1694,6 +1707,133 @@
         splitState = state;
     }
 
+    /**
+     * Return the ES6 global scope for lexically declared bindings.
+     * @return the ES6 lexical global scope.
+     */
+    public final ScriptObject getLexicalScope() {
+        assert context.getEnv()._es6;
+        return lexicalScope;
+    }
+
+    @Override
+    public void addBoundProperties(final ScriptObject source, final jdk.nashorn.internal.runtime.Property[] properties) {
+        PropertyMap ownMap = getMap();
+        LexicalScope lexicalScope = null;
+        PropertyMap lexicalMap = null;
+        boolean hasLexicalDefinitions = false;
+
+        if (context.getEnv()._es6) {
+            lexicalScope = (LexicalScope) getLexicalScope();
+            lexicalMap = lexicalScope.getMap();
+
+            for (final jdk.nashorn.internal.runtime.Property property : properties) {
+                if (property.isLexicalBinding()) {
+                    hasLexicalDefinitions = true;
+                }
+                // ES6 15.1.8 steps 6. and 7.
+                final jdk.nashorn.internal.runtime.Property globalProperty = ownMap.findProperty(property.getKey());
+                if (globalProperty != null && !globalProperty.isConfigurable() && property.isLexicalBinding()) {
+                    throw ECMAErrors.syntaxError("redeclare.variable", property.getKey());
+                }
+                final jdk.nashorn.internal.runtime.Property lexicalProperty = lexicalMap.findProperty(property.getKey());
+                if (lexicalProperty != null && !property.isConfigurable()) {
+                    throw ECMAErrors.syntaxError("redeclare.variable", property.getKey());
+                }
+            }
+        }
+
+        for (final jdk.nashorn.internal.runtime.Property property : properties) {
+            if (property.isLexicalBinding()) {
+                assert lexicalScope != null;
+                lexicalMap = lexicalScope.addBoundProperty(lexicalMap, source, property);
+
+                if (ownMap.findProperty(property.getKey()) != null) {
+                    // If property exists in the global object invalidate any global constant call sites.
+                    invalidateGlobalConstant(property.getKey());
+                }
+            } else {
+                ownMap = addBoundProperty(ownMap, source, property);
+            }
+        }
+
+        setMap(ownMap);
+
+        if (hasLexicalDefinitions) {
+            lexicalScope.setMap(lexicalMap);
+            invalidateLexicalSwitchPoint();
+        }
+    }
+
+    @Override
+    public GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
+        final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+        final boolean isScope = NashornCallSiteDescriptor.isScope(desc);
+
+        if (lexicalScope != null && isScope && !NashornCallSiteDescriptor.isApplyToCall(desc)) {
+            if (lexicalScope.hasOwnProperty(name)) {
+                return lexicalScope.findGetMethod(desc, request, operator);
+            }
+        }
+
+        final GuardedInvocation invocation =  super.findGetMethod(desc, request, operator);
+
+        // We want to avoid adding our generic lexical scope switchpoint to global constant invocations,
+        // because those are invalidated per-key in the addBoundProperties method above.
+        // We therefor check if the invocation does already have a switchpoint and the property is non-inherited,
+        // assuming this only applies to global constants. If other non-inherited properties will
+        // start using switchpoints some time in the future we'll have to revisit this.
+        if (isScope && context.getEnv()._es6 && (invocation.getSwitchPoints() == null || !hasOwnProperty(name))) {
+            return invocation.addSwitchPoint(getLexicalScopeSwitchPoint());
+        }
+
+        return invocation;
+    }
+
+    @Override
+    public GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
+        final boolean isScope = NashornCallSiteDescriptor.isScope(desc);
+
+        if (lexicalScope != null && isScope) {
+            final String name = desc.getNameToken(CallSiteDescriptor.NAME_OPERAND);
+            if (lexicalScope.hasOwnProperty(name)) {
+                return lexicalScope.findSetMethod(desc, request);
+            }
+        }
+
+        final GuardedInvocation invocation = super.findSetMethod(desc, request);
+
+        if (isScope && context.getEnv()._es6) {
+            return invocation.addSwitchPoint(getLexicalScopeSwitchPoint());
+        }
+
+        return invocation;
+    }
+
+    private synchronized SwitchPoint getLexicalScopeSwitchPoint() {
+        SwitchPoint switchPoint = lexicalScopeSwitchPoint;
+        if (switchPoint == null || switchPoint.hasBeenInvalidated()) {
+            switchPoint = lexicalScopeSwitchPoint = new SwitchPoint();
+        }
+        return switchPoint;
+    }
+
+    private synchronized void invalidateLexicalSwitchPoint() {
+        if (lexicalScopeSwitchPoint != null) {
+            context.getLogger(GlobalConstants.class).info("Invalidating non-constant globals on lexical scope update");
+            SwitchPoint.invalidateAll(new SwitchPoint[]{ lexicalScopeSwitchPoint });
+        }
+    }
+
+
+    @SuppressWarnings("unused")
+    private static Object lexicalScopeFilter(final Object self) {
+        if (self instanceof Global) {
+            return ((Global) self).getLexicalScope();
+        }
+        return self;
+    }
+
     private <T extends ScriptObject> T initConstructorAndSwitchPoint(final String name, final Class<T> clazz) {
         final T func = initConstructor(name, clazz);
         tagBuiltinProperties(name, func);
@@ -1739,7 +1879,7 @@
         this.unescape           = ScriptFunctionImpl.makeFunction("unescape",   GlobalFunctions.UNESCAPE);
         this.print              = ScriptFunctionImpl.makeFunction("print",      env._print_no_newline ? PRINT : PRINTLN);
         this.load               = ScriptFunctionImpl.makeFunction("load",       LOAD);
-        this.loadWithNewGlobal  = ScriptFunctionImpl.makeFunction("loadWithNewGlobal", LOADWITHNEWGLOBAL);
+        this.loadWithNewGlobal  = ScriptFunctionImpl.makeFunction("loadWithNewGlobal", LOAD_WITH_NEW_GLOBAL);
         this.exit               = ScriptFunctionImpl.makeFunction("exit",       EXIT);
         this.quit               = ScriptFunctionImpl.makeFunction("quit",       EXIT);
 
@@ -2205,4 +2345,36 @@
     protected boolean isGlobal() {
         return true;
     }
+
+    /**
+     * A class representing the ES6 global lexical scope.
+     */
+    private static class LexicalScope extends ScriptObject {
+
+        LexicalScope(final ScriptObject proto) {
+            super(proto, PropertyMap.newMap());
+        }
+
+        @Override
+        protected GuardedInvocation findGetMethod(final CallSiteDescriptor desc, final LinkRequest request, final String operator) {
+            return filterInvocation(super.findGetMethod(desc, request, operator));
+        }
+
+        @Override
+        protected GuardedInvocation findSetMethod(final CallSiteDescriptor desc, final LinkRequest request) {
+            return filterInvocation(super.findSetMethod(desc, request));
+        }
+
+        @Override
+        protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final jdk.nashorn.internal.runtime.Property property) {
+            // We override this method just to make it callable by Global
+            return super.addBoundProperty(propMap, source, property);
+        }
+
+        private static GuardedInvocation filterInvocation(final GuardedInvocation invocation) {
+            final MethodType type = invocation.getInvocation().type();
+            return invocation.asType(type.changeParameterType(0, Object.class)).filterArguments(0, LEXICAL_SCOPE_FILTER);
+        }
+    }
+
 }
--- a/src/jdk/nashorn/internal/parser/Parser.java	Fri Nov 21 20:17:02 2014 +0100
+++ b/src/jdk/nashorn/internal/parser/Parser.java	Mon Nov 24 12:03:15 2014 +0100
@@ -707,20 +707,9 @@
             FunctionNode.Kind.SCRIPT,
             functionLine);
 
-        // If ES6 block scope is enabled add a per-script block for top-level LET and CONST declarations.
-        final int startLine = start;
-        Block outer = useBlockScope() ? newBlock() : null;
         functionDeclarations = new ArrayList<>();
-
-        try {
-            sourceElements(allowPropertyFunction);
-            addFunctionDeclarations(script);
-        } finally {
-            if (outer != null) {
-                outer = restoreBlock(outer);
-                appendStatement(new BlockStatement(startLine, outer));
-            }
-        }
+        sourceElements(allowPropertyFunction);
+        addFunctionDeclarations(script);
         functionDeclarations = null;
 
         expect(EOF);
--- a/src/jdk/nashorn/internal/runtime/Property.java	Fri Nov 21 20:17:02 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/Property.java	Mon Nov 24 12:03:15 2014 +0100
@@ -84,14 +84,17 @@
     public static final int IS_NASGEN_PRIMITIVE     = 1 << 6;
 
     /** Is this a builtin property, e.g. Function.prototype.apply */
-    public static final int IS_BUILTIN = 1 << 7;
+    public static final int IS_BUILTIN              = 1 << 7;
 
     /** Is this property bound to a receiver? This means get/set operations will be delegated to
      *  a statically defined object instead of the object passed as callsite parameter. */
-    public static final int IS_BOUND                = 1 << 7;
+    public static final int IS_BOUND                = 1 << 8;
 
     /** Is this a lexically scoped LET or CONST variable that is dead until it is declared. */
-    public static final int NEEDS_DECLARATION       = 1 << 8;
+    public static final int NEEDS_DECLARATION       = 1 << 9;
+
+    /** Is this property an ES6 lexical binding? */
+    public static final int IS_LEXICAL_BINDING      = 1 << 10;
 
     /** Property key. */
     private final String key;
@@ -714,4 +717,12 @@
     public boolean isFunctionDeclaration() {
         return (flags & IS_FUNCTION_DECLARATION) == IS_FUNCTION_DECLARATION;
     }
+
+    /**
+     * Is this a property defined by ES6 let or const?
+     * @return true if this property represents a lexical binding.
+     */
+    public boolean isLexicalBinding() {
+        return (flags & IS_LEXICAL_BINDING) == IS_LEXICAL_BINDING;
+    }
 }
--- a/src/jdk/nashorn/internal/runtime/ScriptObject.java	Fri Nov 21 20:17:02 2014 +0100
+++ b/src/jdk/nashorn/internal/runtime/ScriptObject.java	Mon Nov 24 12:03:15 2014 +0100
@@ -304,29 +304,44 @@
         PropertyMap newMap = this.getMap();
 
         for (final Property property : properties) {
-            final String key = property.getKey();
-            final Property oldProp = newMap.findProperty(key);
-            if (oldProp == null) {
-                if (property instanceof UserAccessorProperty) {
-                    // Note: we copy accessor functions to this object which is semantically different from binding.
-                    final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
-                    newMap = newMap.addPropertyNoHistory(prop);
-                } else {
-                    newMap = newMap.addPropertyBind((AccessorProperty)property, source);
-                }
+            newMap = addBoundProperty(newMap, source, property);
+        }
+
+        this.setMap(newMap);
+    }
+
+    /**
+     * Add a bound property from {@code source}, using the interim property map {@code propMap}, and return the
+     * new interim property map.
+     *
+     * @param propMap the property map
+     * @param source the source object
+     * @param property the property to be added
+     * @return the new property map
+     */
+    protected PropertyMap addBoundProperty(final PropertyMap propMap, final ScriptObject source, final Property property) {
+        PropertyMap newMap = propMap;
+        final String key = property.getKey();
+        final Property oldProp = newMap.findProperty(key);
+        if (oldProp == null) {
+            if (property instanceof UserAccessorProperty) {
+                // Note: we copy accessor functions to this object which is semantically different from binding.
+                final UserAccessorProperty prop = this.newUserAccessors(key, property.getFlags(), property.getGetterFunction(source), property.getSetterFunction(source));
+                newMap = newMap.addPropertyNoHistory(prop);
             } else {
-                // See ECMA section 10.5 Declaration Binding Instantiation
-                // step 5 processing each function declaration.
-                if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) {
-                     if (oldProp instanceof UserAccessorProperty ||
-                         !(oldProp.isWritable() && oldProp.isEnumerable())) {
-                         throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
-                     }
+                newMap = newMap.addPropertyBind((AccessorProperty)property, source);
+            }
+        } else {
+            // See ECMA section 10.5 Declaration Binding Instantiation
+            // step 5 processing each function declaration.
+            if (property.isFunctionDeclaration() && !oldProp.isConfigurable()) {
+                if (oldProp instanceof UserAccessorProperty ||
+                        !(oldProp.isWritable() && oldProp.isEnumerable())) {
+                    throw typeError("cant.redefine.property", key, ScriptRuntime.safeToString(this));
                 }
             }
         }
-
-        this.setMap(newMap);
+        return newMap;
     }
 
     /**
@@ -510,7 +525,11 @@
         }
     }
 
-    private void invalidateGlobalConstant(final String key) {
+    /**
+     * Invalidate any existing global constant method handles that may exist for {@code key}.
+     * @param key the property name
+     */
+    protected void invalidateGlobalConstant(final String key) {
         final GlobalConstants globalConstants = getGlobalConstants();
         if (globalConstants != null) {
             globalConstants.delete(key);
--- a/test/script/basic/es6/const-redeclare-extra.js.EXPECTED	Fri Nov 21 20:17:02 2014 +0100
+++ b/test/script/basic/es6/const-redeclare-extra.js.EXPECTED	Mon Nov 24 12:03:15 2014 +0100
@@ -1,9 +1,9 @@
 SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:3:8 Variable "x" has already been declared
     var x = {};
         ^
-SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:2:8 Variable "x" has already been declared
-    var x = 2;
-        ^
-SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:2:13 Variable "x" has already been declared
-    function x () {}
-             ^
+SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:3:10 Variable "x" has already been declared
+    const x = {};
+          ^
+SyntaxError: test/script/basic/es6/const-redeclare-extra.js#36:8<eval>:3:10 Variable "x" has already been declared
+    const x = 5;
+          ^
--- a/test/script/basic/es6/let-load.js	Fri Nov 21 20:17:02 2014 +0100
+++ b/test/script/basic/es6/let-load.js	Mon Nov 24 12:03:15 2014 +0100
@@ -26,7 +26,8 @@
  *
  * @test
  * @run
- * @option --language=es6 */
+ * @option --language=es6
+ */
 
 "use strict";
 
--- a/test/script/basic/es6/let-load.js.EXPECTED	Fri Nov 21 20:17:02 2014 +0100
+++ b/test/script/basic/es6/let-load.js.EXPECTED	Mon Nov 24 12:03:15 2014 +0100
@@ -2,7 +2,7 @@
 block function
 print local defs: 20 30
 imported var: 1
-ReferenceError: "b" is not defined
-ReferenceError: "c" is not defined
+imported let: 2
+imported const: 3
 top level function
 ReferenceError: "block" is not defined
--- a/test/script/basic/es6/let-redeclare-extra.js.EXPECTED	Fri Nov 21 20:17:02 2014 +0100
+++ b/test/script/basic/es6/let-redeclare-extra.js.EXPECTED	Mon Nov 24 12:03:15 2014 +0100
@@ -4,12 +4,12 @@
 SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:3:8 Variable "x" has already been declared
     var x = 2;
         ^
-SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:2:8 Variable "x" has already been declared
-    var x = 2;
+SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:3:8 Variable "x" has already been declared
+    let x = undefined;
         ^
 SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:2:10 Variable "x" has already been declared
     const x = function (){};
           ^
-SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:3:13 Variable "a" has already been declared
-    function a () {};
-             ^
+SyntaxError: test/script/basic/es6/let-redeclare-extra.js#35:8<eval>:2:8 Variable "a" has already been declared
+    let a = 2;
+        ^
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel-def.js	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8057691: Nashorn: let & const declarations are not shared between scripts
+ *
+ * @subtest
+ */
+
+var   VAR   = "VAR";
+let   LET   = "LET";
+const CONST = "CONST";
+function FUNC() {}
+this.GLOBAL = "GLOBAL";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel-print.js	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8057691: Nashorn: let & const declarations are not shared between scripts
+ *
+ * @subtest
+ */
+
+print(VAR);
+print(LET);
+print(CONST);
+print(FUNC);
+print(GLOBAL);
+print(this.VAR);
+print(this.LET);   // undefined
+print(this.CONST); // undefined
+print(this.FUNC);
+print(this.GLOBAL);
+print("VAR" in this);
+print("LET" in this);   // false
+print("CONST" in this); // false
+print("FUNC" in this);
+print("GLOBAL" in this);
+
+try {
+    LET   = LET + "LET";
+    CONST = CONST + "CONST";
+} catch (e) {
+    print(String(e).replace(/\\/g, "/"));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel-redeclare-func-on-let.js	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8057691: Nashorn: let & const declarations are not shared between scripts
+ *
+ * @subtest
+ */
+
+function LET() {}
+var SHOULD_NOT_EXIST = 10;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel-redeclare-let-on-builtin.js	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8057691: Nashorn: let & const declarations are not shared between scripts
+ *
+ * @subtest
+ */
+
+let Object = "LEXICAL BUILTIN";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel-redeclare-let-on-func.js	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8057691: Nashorn: let & const declarations are not shared between scripts
+ *
+ * @subtest
+ */
+
+let FUNC = 10;
+var SHOULD_NOT_EXIST = 10;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel-redeclare-let-on-global.js	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8057691: Nashorn: let & const declarations are not shared between scripts
+ *
+ * @subtest
+ */
+
+let GLOBAL = "LEXICAL GLOBAL";
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel-redeclare-let-on-var.js	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8057691: Nashorn: let & const declarations are not shared between scripts
+ *
+ * @subtest
+ */
+
+let VAR = 10;
+var SHOULD_NOT_EXIST = 10;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel-redeclare-var-on-let.js	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8057691: Nashorn: let & const declarations are not shared between scripts
+ *
+ * @subtest
+ */
+
+var LET = 10;
+var SHOULD_NOT_EXIST = 10;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel-redeclare.js	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8057691: Nashorn: let & const declarations are not shared between scripts
+ *
+ * @test
+ * @run
+ * @option -scripting
+ * @option --language=es6
+ */
+
+load(__DIR__ + "lexical-toplevel-def.js");
+
+var global = this;
+
+function tryIt (code) {
+    try {
+        eval(code)
+    } catch (e) {
+        print(String(e).replace(/\\/g, "/"))
+    }
+}
+
+function loadScript(script) {
+    print(script);
+    try {
+        load(__DIR__ + script);
+    } catch (e) {
+        print(String(e).replace(/\\/g, "/"));
+    }
+    print(VAR);
+    print(LET);
+    print(CONST);
+    print(FUNC);
+    print(GLOBAL);
+    print(global.VAR);
+    print(global.LET);
+    print(global.CONST);
+    print(global.FUNC);
+    print(global.GLOBAL);
+    try {
+        print(SHOULD_NOT_EXIST);
+    } catch (e) {
+        print(String(e).replace(/\\/g, "/"));
+    }
+    print(global.SHOULD_NOT_EXIST);
+    print(Object);
+    print(global.Object);
+    print();
+}
+
+loadScript("lexical-toplevel-redeclare-var-on-let.js");
+loadScript("lexical-toplevel-redeclare-func-on-let.js");
+loadScript("lexical-toplevel-redeclare-let-on-var.js");
+loadScript("lexical-toplevel-redeclare-let-on-func.js");
+loadScript("lexical-toplevel-redeclare-let-on-builtin.js");
+loadScript("lexical-toplevel-redeclare-let-on-global.js");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel-redeclare.js.EXPECTED	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,100 @@
+lexical-toplevel-redeclare-var-on-let.js
+SyntaxError: Variable "LET" has already been declared
+VAR
+LET
+CONST
+function FUNC() {}
+GLOBAL
+VAR
+undefined
+undefined
+function FUNC() {}
+GLOBAL
+ReferenceError: "SHOULD_NOT_EXIST" is not defined
+undefined
+function Object() { [native code] }
+function Object() { [native code] }
+
+lexical-toplevel-redeclare-func-on-let.js
+SyntaxError: Variable "LET" has already been declared
+VAR
+LET
+CONST
+function FUNC() {}
+GLOBAL
+VAR
+undefined
+undefined
+function FUNC() {}
+GLOBAL
+ReferenceError: "SHOULD_NOT_EXIST" is not defined
+undefined
+function Object() { [native code] }
+function Object() { [native code] }
+
+lexical-toplevel-redeclare-let-on-var.js
+SyntaxError: Variable "VAR" has already been declared
+VAR
+LET
+CONST
+function FUNC() {}
+GLOBAL
+VAR
+undefined
+undefined
+function FUNC() {}
+GLOBAL
+ReferenceError: "SHOULD_NOT_EXIST" is not defined
+undefined
+function Object() { [native code] }
+function Object() { [native code] }
+
+lexical-toplevel-redeclare-let-on-func.js
+SyntaxError: Variable "FUNC" has already been declared
+VAR
+LET
+CONST
+function FUNC() {}
+GLOBAL
+VAR
+undefined
+undefined
+function FUNC() {}
+GLOBAL
+ReferenceError: "SHOULD_NOT_EXIST" is not defined
+undefined
+function Object() { [native code] }
+function Object() { [native code] }
+
+lexical-toplevel-redeclare-let-on-builtin.js
+VAR
+LET
+CONST
+function FUNC() {}
+GLOBAL
+VAR
+undefined
+undefined
+function FUNC() {}
+GLOBAL
+ReferenceError: "SHOULD_NOT_EXIST" is not defined
+undefined
+LEXICAL BUILTIN
+function Object() { [native code] }
+
+lexical-toplevel-redeclare-let-on-global.js
+VAR
+LET
+CONST
+function FUNC() {}
+LEXICAL GLOBAL
+VAR
+undefined
+undefined
+function FUNC() {}
+GLOBAL
+ReferenceError: "SHOULD_NOT_EXIST" is not defined
+undefined
+LEXICAL BUILTIN
+function Object() { [native code] }
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel.js	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8057691: Nashorn: let & const declarations are not shared between scripts
+ *
+ * @test
+ * @run
+ * @option --language=es6
+ */
+
+load(__DIR__ + "lexical-toplevel-def.js");
+
+load(__DIR__ + "lexical-toplevel-print.js");
+load(__DIR__ + "lexical-toplevel-print.js");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/script/basic/es6/lexical-toplevel.js.EXPECTED	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,30 @@
+VAR
+LET
+CONST
+function FUNC() {}
+GLOBAL
+VAR
+undefined
+undefined
+function FUNC() {}
+GLOBAL
+true
+false
+false
+true
+true
+VAR
+LETLET
+CONST
+function FUNC() {}
+GLOBAL
+VAR
+undefined
+undefined
+function FUNC() {}
+GLOBAL
+true
+false
+false
+true
+true
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/src/jdk/nashorn/internal/runtime/LexicalBindingTest.java	Mon Nov 24 12:03:15 2014 +0100
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.internal.runtime;
+
+import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
+import org.testng.annotations.Test;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptException;
+import javax.script.SimpleScriptContext;
+
+import static org.testng.Assert.assertEquals;
+
+/**
+ * Top-level lexical binding tests.
+ *
+ * @test
+ * @run testng jdk.nashorn.internal.runtime.LexicalBindingTest
+ */
+@SuppressWarnings("javadoc")
+public class LexicalBindingTest {
+
+    final static String LANGUAGE_ES6 = "--language=es6";
+    final static int NUMBER_OF_CONTEXTS = 20;
+    final static int MEGAMORPHIC_LOOP_COUNT = 20;
+
+    /**
+     * Test access to global var-declared variables for shared script classes with multiple globals.
+     */
+    @Test
+    public static void megamorphicVarTest() throws ScriptException, InterruptedException {
+        final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+        final ScriptEngine e = factory.getScriptEngine();
+        final ScriptContext[] contexts = new ScriptContext[NUMBER_OF_CONTEXTS];
+        final String sharedScript = "foo";
+
+
+        for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
+            final ScriptContext context = contexts[i] = new SimpleScriptContext();
+            final Bindings b = e.createBindings();
+            context.setBindings(b, ScriptContext.ENGINE_SCOPE);
+            assertEquals(e.eval("var foo = '" + i + "';", context), null);
+        }
+
+        for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
+            final ScriptContext context = contexts[i];
+            assertEquals(e.eval(sharedScript, context), String.valueOf(i));
+        }
+    }
+
+    /**
+     * Test access to global lexically declared variables for shared script classes with multiple globals.
+     */
+    @Test
+    public static void megamorphicMultiGlobalLetTest() throws ScriptException, InterruptedException {
+        final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+        final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
+        final ScriptContext[] contexts = new ScriptContext[NUMBER_OF_CONTEXTS];
+        final String sharedScript = "foo";
+
+
+        for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
+            final ScriptContext context = contexts[i] = new SimpleScriptContext();
+            final Bindings b = e.createBindings();
+            context.setBindings(b, ScriptContext.ENGINE_SCOPE);
+            assertEquals(e.eval("let foo = '" + i + "';", context), null);
+        }
+
+        for (int i = 0; i < NUMBER_OF_CONTEXTS; i++) {
+            final ScriptContext context = contexts[i];
+            assertEquals(e.eval(sharedScript, context), String.valueOf(i));
+        }
+    }
+
+
+    /**
+     * Test access to global lexically declared variables for shared script classes with single global.
+     */
+    @Test
+    public static void megamorphicSingleGlobalLetTest() throws ScriptException, InterruptedException {
+        final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+        final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
+        final String sharedGetterScript = "foo";
+        final String sharedSetterScript = "foo = 1";
+
+        for (int i = 0; i < MEGAMORPHIC_LOOP_COUNT; i++) {
+            assertEquals(e.eval(sharedSetterScript), 1);
+            assertEquals(e.eval(sharedGetterScript), 1);
+            assertEquals(e.eval("delete foo; a" + i + " = 1; foo = " + i + ";"), i);
+            assertEquals(e.eval(sharedGetterScript), i);
+        }
+
+        assertEquals(e.eval("let foo = 'foo';"), null);
+        assertEquals(e.eval(sharedGetterScript), "foo");
+        assertEquals(e.eval(sharedSetterScript), 1);
+        assertEquals(e.eval(sharedGetterScript), 1);
+        assertEquals(e.eval("this.foo"), MEGAMORPHIC_LOOP_COUNT - 1);
+    }
+
+    /**
+     * Test access to global lexically declared variables for shared script classes with single global.
+     */
+    @Test
+    public static void megamorphicInheritedGlobalLetTest() throws ScriptException, InterruptedException {
+        final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+        final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
+        final String sharedGetterScript = "foo";
+        final String sharedSetterScript = "foo = 1";
+
+        for (int i = 0; i < MEGAMORPHIC_LOOP_COUNT; i++) {
+            assertEquals(e.eval(sharedSetterScript), 1);
+            assertEquals(e.eval(sharedGetterScript), 1);
+            assertEquals(e.eval("delete foo; a" + i + " = 1; Object.prototype.foo = " + i + ";"), i);
+            assertEquals(e.eval(sharedGetterScript), i);
+        }
+
+        assertEquals(e.eval("let foo = 'foo';"), null);
+        assertEquals(e.eval(sharedGetterScript), "foo");
+        assertEquals(e.eval(sharedSetterScript), 1);
+        assertEquals(e.eval(sharedGetterScript), 1);
+        assertEquals(e.eval("this.foo"), MEGAMORPHIC_LOOP_COUNT - 1);
+    }
+
+    /**
+     * Test multi-threaded access to global lexically declared variables for shared script classes with multiple globals.
+     */
+    @Test
+    public static void multiThreadedLetTest() throws ScriptException, InterruptedException {
+        final NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
+        final ScriptEngine e = factory.getScriptEngine(LANGUAGE_ES6);
+        final Bindings b = e.createBindings();
+        final ScriptContext origContext = e.getContext();
+        final ScriptContext newCtxt = new SimpleScriptContext();
+        newCtxt.setBindings(b, ScriptContext.ENGINE_SCOPE);
+        final String sharedScript = "foo";
+
+        assertEquals(e.eval("let foo = 'original context';", origContext), null);
+        assertEquals(e.eval("let foo = 'new context';", newCtxt), null);
+
+        final Thread t1 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
+        final Thread t2 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "new context", 1000));
+        t1.start();
+        t2.start();
+        t1.join();
+        t2.join();
+
+        assertEquals(e.eval("foo = 'newer context';", newCtxt), "newer context");
+        final Thread t3 = new Thread(new ScriptRunner(e, origContext, sharedScript, "original context", 1000));
+        final Thread t4 = new Thread(new ScriptRunner(e, newCtxt, sharedScript, "newer context", 1000));
+
+        t3.start();
+        t4.start();
+        t3.join();
+        t4.join();
+
+        assertEquals(e.eval(sharedScript), "original context");
+        assertEquals(e.eval(sharedScript, newCtxt), "newer context");
+    }
+
+    private static class ScriptRunner implements Runnable {
+
+        final ScriptEngine engine;
+        final ScriptContext context;
+        final String source;
+        final Object expected;
+        final int iterations;
+
+        ScriptRunner(final ScriptEngine engine, final ScriptContext context, final String source, final Object expected, final int iterations) {
+            this.engine = engine;
+            this.context = context;
+            this.source = source;
+            this.expected = expected;
+            this.iterations = iterations;
+        }
+
+        @Override
+        public void run() {
+            try {
+                for (int i = 0; i < iterations; i++) {
+                    assertEquals(engine.eval(source, context), expected);
+                }
+            } catch (final ScriptException se) {
+                throw new RuntimeException(se);
+            }
+        }
+    }
+}