/** * Copyright (c) 2012-2016 André Bargull * Alle Rechte vorbehalten / All Rights Reserved. Use is subject to license terms. * * <https://github.com/anba/es6draft> */ package com.github.anba.es6draft.interpreter; import static com.github.anba.es6draft.runtime.types.Undefined.UNDEFINED; import static com.github.anba.es6draft.semantics.StaticSemantics.*; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import com.github.anba.es6draft.ast.Script; import com.github.anba.es6draft.ast.StatementListItem; import com.github.anba.es6draft.ast.VariableStatement; import com.github.anba.es6draft.ast.scope.Name; import com.github.anba.es6draft.parser.Parser; import com.github.anba.es6draft.runtime.DeclarativeEnvironmentRecord; import com.github.anba.es6draft.runtime.EnvironmentRecord; import com.github.anba.es6draft.runtime.ExecutionContext; import com.github.anba.es6draft.runtime.GlobalEnvironmentRecord; import com.github.anba.es6draft.runtime.LexicalEnvironment; import com.github.anba.es6draft.runtime.ObjectEnvironmentRecord; import com.github.anba.es6draft.runtime.internal.CompatibilityOption; import com.github.anba.es6draft.runtime.internal.ScriptRuntime; /** * <h1>Declaration Binding Instantiation</h1> * <ul> * <li>15.1.8 Runtime Semantics: GlobalDeclarationInstantiation (script, env) * <li>18.2.1.2 Runtime Semantics: EvalDeclarationInstantiation (body, varEnv, lexEnv, strict) * </ul> */ final class DeclarationBindingInstantiation { private DeclarationBindingInstantiation() { } /** * 15.1.8 Runtime Semantics: GlobalDeclarationInstantiation (script, env) * * @param cx * the execution context * @param script * the global script to instantiate * @param env * the global environment */ public static void GlobalDeclarationInstantiation(ExecutionContext cx, Script script, LexicalEnvironment<GlobalEnvironmentRecord> env) { /* steps 1-2 */ GlobalEnvironmentRecord envRec = env.getEnvRec(); /* step 3 */ assert LexicallyDeclaredNames(script).isEmpty(); /* step 4 */ Set<Name> varNames = VarDeclaredNames(script); /* step 5 (not applicable) */ /* step 6 */ for (Name name : varNames) { ScriptRuntime.canDeclareVarScopedOrThrow(cx, envRec, name.getIdentifier()); } /* step 7 */ List<StatementListItem> varDeclarations = VarScopedDeclarations(script); /* steps 8-10 (not applicable) */ /* step 11 */ LinkedHashSet<Name> declaredVarNames = new LinkedHashSet<>(); /* step 12 */ for (StatementListItem d : varDeclarations) { assert d instanceof VariableStatement; for (Name vn : BoundNames((VariableStatement) d)) { ScriptRuntime.canDeclareGlobalVarOrThrow(cx, envRec, vn.getIdentifier()); declaredVarNames.add(vn); } } /* step 13 (NOTE) */ /* steps 14-16 (not applicable) */ /* step 17 */ for (Name vn : declaredVarNames) { envRec.createGlobalVarBinding(vn.getIdentifier(), false); } /* step 18 (return) */ } /** * 18.2.1.2 Runtime Semantics: EvalDeclarationInstantiation (body, varEnv, lexEnv, strict) * * @param cx * the execution context * @param evalScript * the global script to instantiate * @param varEnv * the variable environment * @param lexEnv * the lexical environment */ public static void EvalDeclarationInstantiation(ExecutionContext cx, Script evalScript, LexicalEnvironment<?> varEnv, LexicalEnvironment<DeclarativeEnvironmentRecord> lexEnv) { boolean strict = evalScript.isStrict(); boolean nonStrictGlobal = !strict && evalScript.isGlobalCode() && !evalScript.isScripting(); /* step 1 */ Set<Name> varNames = VarDeclaredNames(evalScript); /* step 2 */ List<StatementListItem> varDeclarations = VarScopedDeclarations(evalScript); /* step 3 (not applicable) */ /* step 4 */ EnvironmentRecord varEnvRec = varEnv.getEnvRec(); assert !nonStrictGlobal || varEnvRec instanceof GlobalEnvironmentRecord : String.format( "Unexpected environment record type: %s", varEnvRec); /* step 5 */ if (!strict) { if (nonStrictGlobal) { /* step 5.a */ GlobalEnvironmentRecord gEnvRec = (GlobalEnvironmentRecord) varEnvRec; for (Name name : varNames) { ScriptRuntime.canDeclareVarScopedOrThrow(cx, gEnvRec, name.getIdentifier()); } } /* steps 5.b-d */ if (!evalScript.isScripting() && !varNames.isEmpty() && isEnclosedByLexicalOrHasVarForOf(evalScript)) { checkLexicalRedeclaration(cx, varEnv, lexEnv, varNames); } } /* steps 6-8 (not applicable) */ /* step 9 */ LinkedHashSet<Name> declaredNames = new LinkedHashSet<>(); /* step 10 */ for (StatementListItem d : varDeclarations) { assert d instanceof VariableStatement; for (Name vn : BoundNames((VariableStatement) d)) { if (nonStrictGlobal) { GlobalEnvironmentRecord gEnvRec = (GlobalEnvironmentRecord) varEnvRec; ScriptRuntime.canDeclareGlobalVarOrThrow(cx, gEnvRec, vn.getIdentifier()); } declaredNames.add(vn); } } /* step 11 (note) */ /* steps 12-13 */ assert LexicallyScopedDeclarations(evalScript).isEmpty(); /* step 13 (not applicable) */ /* step 15 */ for (Name vn : declaredNames) { if (nonStrictGlobal) { GlobalEnvironmentRecord gEnvRec = (GlobalEnvironmentRecord) varEnvRec; gEnvRec.createGlobalVarBinding(vn.getIdentifier(), true); } else { boolean bindingExists = varEnvRec.hasBinding(vn.getIdentifier()); if (!bindingExists) { varEnvRec.createMutableBinding(vn.getIdentifier(), true); varEnvRec.initializeBinding(vn.getIdentifier(), UNDEFINED); } } } /* step 16 (return) */ } private static boolean isEnclosedByLexicalOrHasVarForOf(Script evalScript) { assert evalScript.getScope().varForOfDeclaredNames().isEmpty(); return evalScript.getParserOptions().contains(Parser.Option.EnclosedByLexicalDeclaration); } private static void checkLexicalRedeclaration(ExecutionContext cx, LexicalEnvironment<?> varEnv, LexicalEnvironment<DeclarativeEnvironmentRecord> lexEnv, Set<Name> varNames) { // Skip the initial lexEnv which is empty by construction. assert lexEnv.getEnvRec().bindingNames().isEmpty(); final boolean catchVar = cx.getRealm().isEnabled(CompatibilityOption.CatchVarStatement); for (LexicalEnvironment<?> thisLex = lexEnv; (thisLex = thisLex.getOuter()) != varEnv;) { EnvironmentRecord thisEnvRec = thisLex.getEnvRec(); if (!(thisEnvRec instanceof ObjectEnvironmentRecord)) { assert thisEnvRec instanceof DeclarativeEnvironmentRecord; DeclarativeEnvironmentRecord envRec = (DeclarativeEnvironmentRecord) thisEnvRec; for (Name name : varNames) { ScriptRuntime.canDeclareVarOrThrow(cx, envRec, name.getIdentifier(), catchVar); } } } } }