diff --git a/Jint/Engine.Ast.cs b/Jint/Engine.Ast.cs index 0c6d7a703..b87ebbe71 100644 --- a/Jint/Engine.Ast.cs +++ b/Jint/Engine.Ast.cs @@ -165,8 +165,7 @@ public CachedHoistingScope(Program program) VarNames = new List(); GatherVarNames(Scope, VarNames); - LexNames = new List(); - GatherLexNames(Scope, LexNames); + LexNames = DeclarationCacheBuilder.Build(Scope._lexicalDeclarations); } internal static void GatherVarNames(HoistingScope scope, List boundNames) @@ -182,31 +181,9 @@ internal static void GatherVarNames(HoistingScope scope, List boundNames) } } - internal static void GatherLexNames(HoistingScope scope, List boundNames) - { - var lexDeclarations = scope._lexicalDeclarations; - if (lexDeclarations != null) - { - var temp = new List(); - for (var i = 0; i < lexDeclarations.Count; i++) - { - var d = lexDeclarations[i]; - temp.Clear(); - d.GetBoundNames(temp); - for (var j = 0; j < temp.Count; j++) - { - boundNames.Add(new CachedLexicalName(temp[j], d.IsConstantDeclaration())); - } - } - } - } - - [StructLayout(LayoutKind.Auto)] - internal readonly record struct CachedLexicalName(Key Name, bool Constant); - public HoistingScope Scope { get; } public List VarNames { get; } - public List LexNames { get; } + public DeclarationCache LexNames { get; } } internal static class AstPreparationExtensions @@ -225,26 +202,25 @@ internal static List GetVarNames(this Program program, HoistingScope hoisti } else { - boundNames = new List(); + boundNames = []; CachedHoistingScope.GatherVarNames(hoistingScope, boundNames); } return boundNames; } - internal static List GetLexNames(this Program program, HoistingScope hoistingScope) + internal static List GetLexNames(this Program program, HoistingScope hoistingScope) { - List boundNames; + DeclarationCache cache; if (program.UserData is CachedHoistingScope cached) { - boundNames = cached.LexNames; + cache = cached.LexNames; } else { - boundNames = new List(); - CachedHoistingScope.GatherLexNames(hoistingScope, boundNames); + cache = DeclarationCacheBuilder.Build(hoistingScope._lexicalDeclarations); } - return boundNames; + return cache.Declarations; } } diff --git a/Jint/Engine.cs b/Jint/Engine.cs index c25b174a9..84937e15f 100644 --- a/Jint/Engine.cs +++ b/Jint/Engine.cs @@ -1004,19 +1004,22 @@ private void GlobalDeclarationInstantiation( var lexNames = script.GetLexNames(hoistingScope); for (var i = 0; i < lexNames.Count; i++) { - var (dn, constant) = lexNames[i]; - if (env.HasLexicalDeclaration(dn) || env.HasRestrictedGlobalProperty(dn)) + var declaration = lexNames[i]; + foreach (var dn in declaration.BoundNames) { - ExceptionHelper.ThrowSyntaxError(realm, $"Identifier '{dn}' has already been declared"); - } + if (env.HasLexicalDeclaration(dn) || env.HasRestrictedGlobalProperty(dn)) + { + ExceptionHelper.ThrowSyntaxError(realm, $"Identifier '{dn}' has already been declared"); + } - if (constant) - { - env.CreateImmutableBinding(dn, strict: true); - } - else - { - env.CreateMutableBinding(dn, canBeDeleted: false); + if (declaration.IsConstantDeclaration) + { + env.CreateImmutableBinding(dn, strict: true); + } + else + { + env.CreateMutableBinding(dn, canBeDeleted: false); + } } } @@ -1114,8 +1117,7 @@ private void GlobalDeclarationInstantiation( { // NOTE: A separate Environment Record is needed to ensure that closures created by expressions // in the formal parameter list do not have visibility of declarations in the function body. - var varEnvRec = JintEnvironment.NewDeclarativeEnvironment(this, env); - varEnv = varEnvRec; + varEnv = JintEnvironment.NewDeclarativeEnvironment(this, env); UpdateVariableEnvironment(varEnv); @@ -1124,7 +1126,7 @@ private void GlobalDeclarationInstantiation( { var pair = varsToInitialize[i]; var initialValue = pair.InitialValue ?? env.GetBindingValue(pair.Name, strict: false); - varEnvRec.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, initialValue); + varEnv.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, initialValue); } } @@ -1147,20 +1149,24 @@ private void GlobalDeclarationInstantiation( UpdateLexicalEnvironment(lexEnv); - if (configuration.LexicalDeclarations.Count > 0) + if (configuration.LexicalDeclarations?.Declarations.Count > 0) { - var dictionary = lexEnv._dictionary ??= new HybridDictionary(configuration.LexicalDeclarations.Count, checkExistingKeys: true); - dictionary.EnsureCapacity(dictionary.Count + configuration.LexicalDeclarations.Count); - for (var i = 0; i < configuration.LexicalDeclarations.Count; i++) + var lexicalDeclarations = configuration.LexicalDeclarations.Value.Declarations; + var dictionary = lexEnv._dictionary ??= new HybridDictionary(lexicalDeclarations.Count, checkExistingKeys: true); + dictionary.EnsureCapacity(dictionary.Count + lexicalDeclarations.Count); + for (var i = 0; i < lexicalDeclarations.Count; i++) { - var d = configuration.LexicalDeclarations[i]; - if (d.IsConstantDeclaration) - { - dictionary[d.BoundName] = new Binding(null!, canBeDeleted: false, mutable: false, strict); - } - else + var declaration = lexicalDeclarations[i]; + foreach (var bn in declaration.BoundNames) { - dictionary[d.BoundName] = new Binding(null!, canBeDeleted: false, mutable: true, strict: false); + if (declaration.IsConstantDeclaration) + { + dictionary.CreateImmutableBinding(bn, strict); + } + else + { + dictionary.CreateMutableBinding(bn, canBeDeleted: false); + } } } } diff --git a/Jint/HoistingScope.cs b/Jint/HoistingScope.cs index 0e65bee81..558188146 100644 --- a/Jint/HoistingScope.cs +++ b/Jint/HoistingScope.cs @@ -71,55 +71,6 @@ public static HoistingScope GetModuleLevelDeclarations( treeWalker._lexicalNames); } - public static List? GetLexicalDeclarations(BlockStatement statement) - { - List? lexicalDeclarations = null; - ref readonly var statementListItems = ref statement.Body; - for (var i = 0; i < statementListItems.Count; i++) - { - var node = statementListItems[i]; - if (node.Type != NodeType.VariableDeclaration && node.Type != NodeType.FunctionDeclaration && node.Type != NodeType.ClassDeclaration) - { - continue; - } - - if (node is VariableDeclaration { Kind: VariableDeclarationKind.Var }) - { - continue; - } - - lexicalDeclarations ??= new List(); - lexicalDeclarations.Add((Declaration)node); - } - - return lexicalDeclarations; - } - - public static List? GetLexicalDeclarations(SwitchCase statement) - { - List? lexicalDeclarations = null; - ref readonly var statementListItems = ref statement.Consequent; - for (var i = 0; i < statementListItems.Count; i++) - { - var node = statementListItems[i]; - if (node.Type != NodeType.VariableDeclaration) - { - continue; - } - - var rootVariable = (VariableDeclaration)node; - if (rootVariable.Kind == VariableDeclarationKind.Var) - { - continue; - } - - lexicalDeclarations ??= new List(); - lexicalDeclarations.Add(rootVariable); - } - - return lexicalDeclarations; - } - public static void GetImportsAndExports( AstModule module, out HashSet requestedModules, @@ -326,4 +277,4 @@ internal void Visit(Node node) } } } -} \ No newline at end of file +} diff --git a/Jint/Runtime/Environments/DeclarativeEnvironment.cs b/Jint/Runtime/Environments/DeclarativeEnvironment.cs index 86f865a8d..309e23d9f 100644 --- a/Jint/Runtime/Environments/DeclarativeEnvironment.cs +++ b/Jint/Runtime/Environments/DeclarativeEnvironment.cs @@ -50,13 +50,13 @@ internal void CreateImmutableBindingAndInitialize(Key name, bool strict, JsValue internal sealed override void CreateMutableBinding(Key name, bool canBeDeleted = false) { _dictionary ??= new HybridDictionary(); - _dictionary[name] = new Binding(null!, canBeDeleted, mutable: true, strict: false); + _dictionary.CreateMutableBinding(name, canBeDeleted); } internal sealed override void CreateImmutableBinding(Key name, bool strict = true) { _dictionary ??= new HybridDictionary(); - _dictionary[name] = new Binding(null!, canBeDeleted: false, mutable: false, strict); + _dictionary.CreateImmutableBinding(name, strict); } internal sealed override void InitializeBinding(Key name, JsValue value) @@ -69,14 +69,17 @@ internal sealed override void InitializeBinding(Key name, JsValue value) internal sealed override void SetMutableBinding(Key name, JsValue value, bool strict) { - if (_dictionary is null || !_dictionary.TryGetValue(name, out var binding)) + _dictionary ??= new HybridDictionary(); + + ref var binding = ref _dictionary.GetValueRefOrNullRef(name); + if (Unsafe.IsNullRef(ref binding)) { if (strict) { ExceptionHelper.ThrowReferenceNameError(_engine.Realm, name); } - CreateMutableBindingAndInitialize(name, canBeDeleted: true, value); + _dictionary[name] = new Binding(value, canBeDeleted: true, mutable: true, strict: false); return; } @@ -93,7 +96,7 @@ internal sealed override void SetMutableBinding(Key name, JsValue value, bool st if (binding.Mutable) { - _dictionary[name] = binding.ChangeValue(value); + binding = binding.ChangeValue(value); } else { @@ -116,7 +119,7 @@ internal override JsValue GetBindingValue(Key name, bool strict) } [MethodImpl(MethodImplOptions.NoInlining)] - private void ThrowUninitializedBindingError(string name) + private void ThrowUninitializedBindingError(Key name) { ExceptionHelper.ThrowReferenceError(_engine.Realm, $"Cannot access '{name}' before initialization"); } @@ -151,7 +154,7 @@ internal sealed override string[] GetAllBindingNames() { if (_dictionary is null) { - return Array.Empty(); + return []; } var keys = new string[_dictionary.Count]; @@ -183,3 +186,18 @@ internal void TransferTo(List names, DeclarativeEnvironment env) } } } + +internal static class DictionaryExtensions +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void CreateMutableBinding(this T dictionary, Key name, bool canBeDeleted = false) where T : IEngineDictionary + { + dictionary[name] = new Binding(null!, canBeDeleted, mutable: true, strict: false); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static void CreateImmutableBinding(this T dictionary, Key name, bool strict = true) where T : IEngineDictionary + { + dictionary[name] = new Binding(null!, canBeDeleted: false, mutable: false, strict); + } +} diff --git a/Jint/Runtime/Environments/FunctionEnvironment.cs b/Jint/Runtime/Environments/FunctionEnvironment.cs index 074e9b5eb..130931c85 100644 --- a/Jint/Runtime/Environments/FunctionEnvironment.cs +++ b/Jint/Runtime/Environments/FunctionEnvironment.cs @@ -32,7 +32,7 @@ public FunctionEnvironment( { _functionObject = functionObject; NewTarget = newTarget; - if (functionObject._functionDefinition?.Function is ArrowFunctionExpression) + if (functionObject._functionDefinition?.Function.Type is NodeType.ArrowFunctionExpression) { _thisBindingStatus = ThisBindingStatus.Lexical; } @@ -107,21 +107,19 @@ internal void InitializeParameters( var value = hasDuplicates ? Undefined : null; var directSet = !hasDuplicates && (_dictionary is null || _dictionary.Count == 0); + _dictionary ??= new HybridDictionary(parameterNames.Length, checkExistingKeys: !directSet); for (uint i = 0; i < (uint) parameterNames.Length; i++) { var paramName = parameterNames[i]; - if (directSet || _dictionary is null || !_dictionary.ContainsKey(paramName)) + ref var binding = ref _dictionary.GetValueRefOrAddDefault(paramName, out var exists); + if (directSet || !exists) { - var parameterValue = value; - if (arguments != null) - { - parameterValue = i < (uint) arguments.Length ? arguments[i] : Undefined; - } - - _dictionary ??= new HybridDictionary(); - _dictionary[paramName] = new Binding(parameterValue!, canBeDeleted: false, mutable: true, strict: false); + var parameterValue = arguments?.At((int) i, Undefined) ?? value; + binding = new Binding(parameterValue!, canBeDeleted: false, mutable: true, strict: false); } } + + _dictionary.CheckExistingKeys = true; } internal void AddFunctionParameters(EvaluationContext context, IFunction functionDeclaration, JsValue[] arguments) diff --git a/Jint/Runtime/Interpreter/DeclarationCache.cs b/Jint/Runtime/Interpreter/DeclarationCache.cs new file mode 100644 index 000000000..5958619b9 --- /dev/null +++ b/Jint/Runtime/Interpreter/DeclarationCache.cs @@ -0,0 +1,105 @@ +using System.Runtime.InteropServices; + +namespace Jint.Runtime.Interpreter; + +internal readonly record struct DeclarationCache(List Declarations, bool AllLexicalScoped); + +[StructLayout(LayoutKind.Auto)] +internal readonly record struct ScopedDeclaration(Key[] BoundNames, bool IsConstantDeclaration, Node Declaration); + +internal static class DeclarationCacheBuilder +{ + public static DeclarationCache Build(List? lexicalDeclarations) + { + if (lexicalDeclarations is null) + { + return new DeclarationCache([], AllLexicalScoped: true); + } + + var allLexical = true; + List boundNames = []; + List declarations = []; + for (var i = 0; i < lexicalDeclarations.Count; i++) + { + var d = lexicalDeclarations[i]; + Collect(boundNames, d, ref allLexical, declarations); + } + + return new DeclarationCache(declarations, allLexical); + } + + public static DeclarationCache Build(BlockStatement statement) + { + var allLexical = true; + List boundNames = []; + List declarations = []; + + ref readonly var statementListItems = ref statement.Body; + foreach (var node in statementListItems.AsSpan()) + { + if (node.Type != NodeType.VariableDeclaration && node.Type != NodeType.FunctionDeclaration && node.Type != NodeType.ClassDeclaration) + { + continue; + } + + if (node is VariableDeclaration { Kind: VariableDeclarationKind.Var }) + { + continue; + } + + Collect(boundNames, node, ref allLexical, declarations); + } + + return new DeclarationCache(declarations, allLexical); + } + + public static DeclarationCache Build(SwitchCase statement) + { + var allLexical = true; + List boundNames = []; + List declarations = []; + + ref readonly var statementListItems = ref statement.Consequent; + foreach (var node in statementListItems.AsSpan()) + { + if (node.Type != NodeType.VariableDeclaration) + { + continue; + } + + var rootVariable = (VariableDeclaration)node; + if (rootVariable.Kind == VariableDeclarationKind.Var) + { + continue; + } + + Collect(boundNames, node, ref allLexical, declarations); + } + + return new DeclarationCache(declarations, allLexical); + } + + private static void Collect( + List boundNames, + Node node, + ref bool allLexical, + List declarations) + { + boundNames.Clear(); + node.GetBoundNames(boundNames); + + var isConstantDeclaration = false; + if (node is VariableDeclaration variableDeclaration) + { + isConstantDeclaration = variableDeclaration.Kind == VariableDeclarationKind.Const; + allLexical &= variableDeclaration.Kind != VariableDeclarationKind.Var; + } + else + { + allLexical = false; + } + + declarations.Add(new ScopedDeclaration(boundNames.ToArray(), isConstantDeclaration, node)); + } +} + diff --git a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs index 1b8373565..08b1bf896 100644 --- a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs +++ b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs @@ -196,14 +196,11 @@ internal sealed class State public List? VarNames; public LinkedList? FunctionsToInitialize; public readonly HashSet FunctionNames = new(); - public List LexicalDeclarations = []; + public DeclarationCache? LexicalDeclarations; public HashSet? ParameterBindings; public List? VarsToInitialize; internal readonly record struct VariableValuePair(Key Name, JsValue? InitialValue); - - [StructLayout(LayoutKind.Auto)] - internal readonly record struct LexicalVariableDeclaration(Key BoundName, bool IsConstantDeclaration); } internal static State BuildState(IFunction function) @@ -325,21 +322,7 @@ internal static State BuildState(IFunction function) if (hoistingScope._lexicalDeclarations != null) { - var boundNames = new List(); - var lexicalDeclarations = hoistingScope._lexicalDeclarations; - var lexicalDeclarationsCount = lexicalDeclarations.Count; - var declarations = new List(lexicalDeclarationsCount); - for (var i = 0; i < lexicalDeclarationsCount; i++) - { - var d = lexicalDeclarations[i]; - boundNames.Clear(); - d.GetBoundNames(boundNames); - for (var j = 0; j < boundNames.Count; j++) - { - declarations.Add(new State.LexicalVariableDeclaration(boundNames[j], d.IsConstantDeclaration())); - } - } - state.LexicalDeclarations = declarations; + state.LexicalDeclarations = DeclarationCacheBuilder.Build(hoistingScope._lexicalDeclarations); } return state; diff --git a/Jint/Runtime/Interpreter/JintStatementList.cs b/Jint/Runtime/Interpreter/JintStatementList.cs index b9e12110b..df88426bd 100644 --- a/Jint/Runtime/Interpreter/JintStatementList.cs +++ b/Jint/Runtime/Interpreter/JintStatementList.cs @@ -1,6 +1,8 @@ using System.Runtime.CompilerServices; +using Jint.Collections; using Jint.Native; using Jint.Native.Error; +using Jint.Runtime.Environments; using Jint.Runtime.Interpreter.Statements; using Environment = Jint.Runtime.Environments.Environment; @@ -181,29 +183,30 @@ private static Completion CreateThrowCompletion(JintStatement? s, JavaScriptExce /// /// https://tc39.es/ecma262/#sec-blockdeclarationinstantiation /// - internal static void BlockDeclarationInstantiation(Environment env, List declarations) + internal static void BlockDeclarationInstantiation(DeclarativeEnvironment env, DeclarationCache declarations) { var privateEnv = env._engine.ExecutionContext.PrivateEnvironment; - var boundNames = new List(); - for (var i = 0; i < declarations.Count; i++) + + var list = declarations.Declarations; + var dictionary = env._dictionary ??= new HybridDictionary(list.Count, checkExistingKeys: !declarations.AllLexicalScoped); + dictionary.EnsureCapacity(list.Count); + + for (var i = 0; i < list.Count; i++) { - var d = declarations[i]; - boundNames.Clear(); - d.GetBoundNames(boundNames); - for (var j = 0; j < boundNames.Count; j++) + var declaration = list[i]; + foreach (var bn in declaration.BoundNames) { - var dn = boundNames[j]; - if (d is VariableDeclaration { Kind: VariableDeclarationKind.Const }) + if (declaration.IsConstantDeclaration) { - env.CreateImmutableBinding(dn, strict: true); + dictionary.CreateImmutableBinding(bn, strict: true); } else { - env.CreateMutableBinding(dn, canBeDeleted: false); + dictionary.CreateMutableBinding(bn, canBeDeleted: false); } } - if (d is FunctionDeclaration functionDeclaration) + if (declaration.Declaration is FunctionDeclaration functionDeclaration) { var definition = new JintFunctionDefinition(functionDeclaration); var fn = definition.Name!; @@ -211,6 +214,8 @@ internal static void BlockDeclarationInstantiation(Environment env, List _index == _jintStatements?.Length; @@ -219,4 +224,4 @@ public void Reset() { _index = 0; } -} \ No newline at end of file +} diff --git a/Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs b/Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs index 6c33589cd..c6e1f0cba 100644 --- a/Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs +++ b/Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs @@ -7,7 +7,7 @@ internal sealed class JintBlockStatement : JintStatement { private JintStatementList? _statementList; private JintStatement? _singleStatement; - private List? _lexicalDeclarations; + private DeclarationCache _lexicalDeclarations; public JintBlockStatement(NestedBlockStatement blockStatement) : base(blockStatement) { @@ -15,7 +15,8 @@ public JintBlockStatement(NestedBlockStatement blockStatement) : base(blockState protected override void Initialize(EvaluationContext context) { - _lexicalDeclarations = HoistingScope.GetLexicalDeclarations(_statement); + _lexicalDeclarations = DeclarationCacheBuilder.Build(_statement); + if (_statement.Body.Count == 1) { _singleStatement = Build(_statement.Body[0]); @@ -38,7 +39,7 @@ public Completion ExecuteBlock(EvaluationContext context) Environment? oldEnv = null; var engine = context.Engine; - if (_lexicalDeclarations != null) + if (_lexicalDeclarations.Declarations.Count > 0) { oldEnv = engine.ExecutionContext.LexicalEnvironment; var blockEnv = JintEnvironment.NewDeclarativeEnvironment(engine, engine.ExecutionContext.LexicalEnvironment); @@ -94,4 +95,4 @@ protected override Completion ExecuteInternal(EvaluationContext context) { return ExecuteBlock(context); } -} \ No newline at end of file +} diff --git a/Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs b/Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs index ceaff081c..78233ad78 100644 --- a/Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs +++ b/Jint/Runtime/Interpreter/Statements/JintSwitchBlock.cs @@ -8,7 +8,7 @@ namespace Jint.Runtime.Interpreter.Statements; internal sealed class JintSwitchBlock { private readonly NodeList _switchBlock; - private JintSwitchCase[] _jintSwitchBlock = Array.Empty(); + private JintSwitchCase[] _jintSwitchBlock = []; private bool _initialized; public JintSwitchBlock(NodeList switchBlock) @@ -48,7 +48,7 @@ public Completion Execute(EvaluationContext context, JsValue input) for (; i < temp.Length; i++) { var clause = temp[i]; - if (clause.LexicalDeclarations is not null && oldEnv is null) + if (clause.LexicalDeclarations.Declarations.Count > 0 && oldEnv is null) { oldEnv = context.Engine.ExecutionContext.LexicalEnvironment; blockEnv ??= JintEnvironment.NewDeclarativeEnvironment(context.Engine, oldEnv); @@ -120,12 +120,12 @@ private sealed class JintSwitchCase { internal readonly JintStatementList Consequent; internal readonly JintExpression? Test; - internal readonly List? LexicalDeclarations; + internal readonly DeclarationCache LexicalDeclarations; public JintSwitchCase(SwitchCase switchCase) { Consequent = new JintStatementList(statement: null, switchCase.Consequent); - LexicalDeclarations = HoistingScope.GetLexicalDeclarations(switchCase); + LexicalDeclarations = DeclarationCacheBuilder.Build(switchCase); if (switchCase.Test != null) { @@ -133,4 +133,4 @@ public JintSwitchCase(SwitchCase switchCase) } } } -} \ No newline at end of file +} diff --git a/Jint/Runtime/Modules/SourceTextModule.cs b/Jint/Runtime/Modules/SourceTextModule.cs index 7c18d1f41..9e1183f39 100644 --- a/Jint/Runtime/Modules/SourceTextModule.cs +++ b/Jint/Runtime/Modules/SourceTextModule.cs @@ -221,7 +221,7 @@ protected override void InitializeEnvironment() if (string.Equals(ie.ImportName, "*", StringComparison.Ordinal)) { var ns = GetModuleNamespace(importedModule); - env.CreateImmutableBinding(ie.LocalName, true); + env.CreateImmutableBinding(ie.LocalName, strict: true); env.InitializeBinding(ie.LocalName, ns); } else @@ -235,7 +235,7 @@ protected override void InitializeEnvironment() if (string.Equals(resolution.BindingName, "*namespace*", StringComparison.Ordinal)) { var ns = GetModuleNamespace(resolution.Module); - env.CreateImmutableBinding(ie.LocalName, true); + env.CreateImmutableBinding(ie.LocalName, strict: true); env.InitializeBinding(ie.LocalName, ns); } else @@ -275,26 +275,21 @@ protected override void InitializeEnvironment() } } - var lexDeclarations = hoistingScope._lexicalDeclarations; - - if (lexDeclarations != null) + if (hoistingScope._lexicalDeclarations != null) { - var boundNames = new List(); - for (var i = 0; i < lexDeclarations.Count; i++) + var cache = DeclarationCacheBuilder.Build(hoistingScope._lexicalDeclarations); + for (var i = 0; i < cache.Declarations.Count; i++) { - var d = lexDeclarations[i]; - boundNames.Clear(); - d.GetBoundNames(boundNames); - for (var j = 0; j < boundNames.Count; j++) + var declaration = cache.Declarations[i]; + foreach (var bn in declaration.BoundNames) { - var dn = boundNames[j]; - if (d.IsConstantDeclaration()) + if (declaration.IsConstantDeclaration) { - env.CreateImmutableBinding(dn); + env.CreateImmutableBinding(bn); } else { - env.CreateMutableBinding(dn); + env.CreateMutableBinding(bn); } } }