Skip to content

Commit fb0016d

Browse files
authored
Improve environment initialization performance (#2045)
1 parent 8ebce9a commit fb0016d

File tree

11 files changed

+218
-180
lines changed

11 files changed

+218
-180
lines changed

Jint/Engine.Ast.cs

Lines changed: 8 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,7 @@ public CachedHoistingScope(Program program)
165165
VarNames = new List<Key>();
166166
GatherVarNames(Scope, VarNames);
167167

168-
LexNames = new List<CachedLexicalName>();
169-
GatherLexNames(Scope, LexNames);
168+
LexNames = DeclarationCacheBuilder.Build(Scope._lexicalDeclarations);
170169
}
171170

172171
internal static void GatherVarNames(HoistingScope scope, List<Key> boundNames)
@@ -182,31 +181,9 @@ internal static void GatherVarNames(HoistingScope scope, List<Key> boundNames)
182181
}
183182
}
184183

185-
internal static void GatherLexNames(HoistingScope scope, List<CachedLexicalName> boundNames)
186-
{
187-
var lexDeclarations = scope._lexicalDeclarations;
188-
if (lexDeclarations != null)
189-
{
190-
var temp = new List<Key>();
191-
for (var i = 0; i < lexDeclarations.Count; i++)
192-
{
193-
var d = lexDeclarations[i];
194-
temp.Clear();
195-
d.GetBoundNames(temp);
196-
for (var j = 0; j < temp.Count; j++)
197-
{
198-
boundNames.Add(new CachedLexicalName(temp[j], d.IsConstantDeclaration()));
199-
}
200-
}
201-
}
202-
}
203-
204-
[StructLayout(LayoutKind.Auto)]
205-
internal readonly record struct CachedLexicalName(Key Name, bool Constant);
206-
207184
public HoistingScope Scope { get; }
208185
public List<Key> VarNames { get; }
209-
public List<CachedLexicalName> LexNames { get; }
186+
public DeclarationCache LexNames { get; }
210187
}
211188

212189
internal static class AstPreparationExtensions
@@ -225,26 +202,25 @@ internal static List<Key> GetVarNames(this Program program, HoistingScope hoisti
225202
}
226203
else
227204
{
228-
boundNames = new List<Key>();
205+
boundNames = [];
229206
CachedHoistingScope.GatherVarNames(hoistingScope, boundNames);
230207
}
231208

232209
return boundNames;
233210
}
234211

235-
internal static List<CachedHoistingScope.CachedLexicalName> GetLexNames(this Program program, HoistingScope hoistingScope)
212+
internal static List<ScopedDeclaration> GetLexNames(this Program program, HoistingScope hoistingScope)
236213
{
237-
List<CachedHoistingScope.CachedLexicalName> boundNames;
214+
DeclarationCache cache;
238215
if (program.UserData is CachedHoistingScope cached)
239216
{
240-
boundNames = cached.LexNames;
217+
cache = cached.LexNames;
241218
}
242219
else
243220
{
244-
boundNames = new List<CachedHoistingScope.CachedLexicalName>();
245-
CachedHoistingScope.GatherLexNames(hoistingScope, boundNames);
221+
cache = DeclarationCacheBuilder.Build(hoistingScope._lexicalDeclarations);
246222
}
247223

248-
return boundNames;
224+
return cache.Declarations;
249225
}
250226
}

Jint/Engine.cs

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,19 +1004,22 @@ private void GlobalDeclarationInstantiation(
10041004
var lexNames = script.GetLexNames(hoistingScope);
10051005
for (var i = 0; i < lexNames.Count; i++)
10061006
{
1007-
var (dn, constant) = lexNames[i];
1008-
if (env.HasLexicalDeclaration(dn) || env.HasRestrictedGlobalProperty(dn))
1007+
var declaration = lexNames[i];
1008+
foreach (var dn in declaration.BoundNames)
10091009
{
1010-
ExceptionHelper.ThrowSyntaxError(realm, $"Identifier '{dn}' has already been declared");
1011-
}
1010+
if (env.HasLexicalDeclaration(dn) || env.HasRestrictedGlobalProperty(dn))
1011+
{
1012+
ExceptionHelper.ThrowSyntaxError(realm, $"Identifier '{dn}' has already been declared");
1013+
}
10121014

1013-
if (constant)
1014-
{
1015-
env.CreateImmutableBinding(dn, strict: true);
1016-
}
1017-
else
1018-
{
1019-
env.CreateMutableBinding(dn, canBeDeleted: false);
1015+
if (declaration.IsConstantDeclaration)
1016+
{
1017+
env.CreateImmutableBinding(dn, strict: true);
1018+
}
1019+
else
1020+
{
1021+
env.CreateMutableBinding(dn, canBeDeleted: false);
1022+
}
10201023
}
10211024
}
10221025

@@ -1114,8 +1117,7 @@ private void GlobalDeclarationInstantiation(
11141117
{
11151118
// NOTE: A separate Environment Record is needed to ensure that closures created by expressions
11161119
// in the formal parameter list do not have visibility of declarations in the function body.
1117-
var varEnvRec = JintEnvironment.NewDeclarativeEnvironment(this, env);
1118-
varEnv = varEnvRec;
1120+
varEnv = JintEnvironment.NewDeclarativeEnvironment(this, env);
11191121

11201122
UpdateVariableEnvironment(varEnv);
11211123

@@ -1124,7 +1126,7 @@ private void GlobalDeclarationInstantiation(
11241126
{
11251127
var pair = varsToInitialize[i];
11261128
var initialValue = pair.InitialValue ?? env.GetBindingValue(pair.Name, strict: false);
1127-
varEnvRec.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, initialValue);
1129+
varEnv.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, initialValue);
11281130
}
11291131
}
11301132

@@ -1147,20 +1149,24 @@ private void GlobalDeclarationInstantiation(
11471149

11481150
UpdateLexicalEnvironment(lexEnv);
11491151

1150-
if (configuration.LexicalDeclarations.Count > 0)
1152+
if (configuration.LexicalDeclarations?.Declarations.Count > 0)
11511153
{
1152-
var dictionary = lexEnv._dictionary ??= new HybridDictionary<Binding>(configuration.LexicalDeclarations.Count, checkExistingKeys: true);
1153-
dictionary.EnsureCapacity(dictionary.Count + configuration.LexicalDeclarations.Count);
1154-
for (var i = 0; i < configuration.LexicalDeclarations.Count; i++)
1154+
var lexicalDeclarations = configuration.LexicalDeclarations.Value.Declarations;
1155+
var dictionary = lexEnv._dictionary ??= new HybridDictionary<Binding>(lexicalDeclarations.Count, checkExistingKeys: true);
1156+
dictionary.EnsureCapacity(dictionary.Count + lexicalDeclarations.Count);
1157+
for (var i = 0; i < lexicalDeclarations.Count; i++)
11551158
{
1156-
var d = configuration.LexicalDeclarations[i];
1157-
if (d.IsConstantDeclaration)
1158-
{
1159-
dictionary[d.BoundName] = new Binding(null!, canBeDeleted: false, mutable: false, strict);
1160-
}
1161-
else
1159+
var declaration = lexicalDeclarations[i];
1160+
foreach (var bn in declaration.BoundNames)
11621161
{
1163-
dictionary[d.BoundName] = new Binding(null!, canBeDeleted: false, mutable: true, strict: false);
1162+
if (declaration.IsConstantDeclaration)
1163+
{
1164+
dictionary.CreateImmutableBinding(bn, strict);
1165+
}
1166+
else
1167+
{
1168+
dictionary.CreateMutableBinding(bn, canBeDeleted: false);
1169+
}
11641170
}
11651171
}
11661172
}

Jint/HoistingScope.cs

Lines changed: 1 addition & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -71,55 +71,6 @@ public static HoistingScope GetModuleLevelDeclarations(
7171
treeWalker._lexicalNames);
7272
}
7373

74-
public static List<Declaration>? GetLexicalDeclarations(BlockStatement statement)
75-
{
76-
List<Declaration>? lexicalDeclarations = null;
77-
ref readonly var statementListItems = ref statement.Body;
78-
for (var i = 0; i < statementListItems.Count; i++)
79-
{
80-
var node = statementListItems[i];
81-
if (node.Type != NodeType.VariableDeclaration && node.Type != NodeType.FunctionDeclaration && node.Type != NodeType.ClassDeclaration)
82-
{
83-
continue;
84-
}
85-
86-
if (node is VariableDeclaration { Kind: VariableDeclarationKind.Var })
87-
{
88-
continue;
89-
}
90-
91-
lexicalDeclarations ??= new List<Declaration>();
92-
lexicalDeclarations.Add((Declaration)node);
93-
}
94-
95-
return lexicalDeclarations;
96-
}
97-
98-
public static List<Declaration>? GetLexicalDeclarations(SwitchCase statement)
99-
{
100-
List<Declaration>? lexicalDeclarations = null;
101-
ref readonly var statementListItems = ref statement.Consequent;
102-
for (var i = 0; i < statementListItems.Count; i++)
103-
{
104-
var node = statementListItems[i];
105-
if (node.Type != NodeType.VariableDeclaration)
106-
{
107-
continue;
108-
}
109-
110-
var rootVariable = (VariableDeclaration)node;
111-
if (rootVariable.Kind == VariableDeclarationKind.Var)
112-
{
113-
continue;
114-
}
115-
116-
lexicalDeclarations ??= new List<Declaration>();
117-
lexicalDeclarations.Add(rootVariable);
118-
}
119-
120-
return lexicalDeclarations;
121-
}
122-
12374
public static void GetImportsAndExports(
12475
AstModule module,
12576
out HashSet<ModuleRequest> requestedModules,
@@ -326,4 +277,4 @@ internal void Visit(Node node)
326277
}
327278
}
328279
}
329-
}
280+
}

Jint/Runtime/Environments/DeclarativeEnvironment.cs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,13 @@ internal void CreateImmutableBindingAndInitialize(Key name, bool strict, JsValue
5050
internal sealed override void CreateMutableBinding(Key name, bool canBeDeleted = false)
5151
{
5252
_dictionary ??= new HybridDictionary<Binding>();
53-
_dictionary[name] = new Binding(null!, canBeDeleted, mutable: true, strict: false);
53+
_dictionary.CreateMutableBinding(name, canBeDeleted);
5454
}
5555

5656
internal sealed override void CreateImmutableBinding(Key name, bool strict = true)
5757
{
5858
_dictionary ??= new HybridDictionary<Binding>();
59-
_dictionary[name] = new Binding(null!, canBeDeleted: false, mutable: false, strict);
59+
_dictionary.CreateImmutableBinding(name, strict);
6060
}
6161

6262
internal sealed override void InitializeBinding(Key name, JsValue value)
@@ -69,14 +69,17 @@ internal sealed override void InitializeBinding(Key name, JsValue value)
6969

7070
internal sealed override void SetMutableBinding(Key name, JsValue value, bool strict)
7171
{
72-
if (_dictionary is null || !_dictionary.TryGetValue(name, out var binding))
72+
_dictionary ??= new HybridDictionary<Binding>();
73+
74+
ref var binding = ref _dictionary.GetValueRefOrNullRef(name);
75+
if (Unsafe.IsNullRef(ref binding))
7376
{
7477
if (strict)
7578
{
7679
ExceptionHelper.ThrowReferenceNameError(_engine.Realm, name);
7780
}
7881

79-
CreateMutableBindingAndInitialize(name, canBeDeleted: true, value);
82+
_dictionary[name] = new Binding(value, canBeDeleted: true, mutable: true, strict: false);
8083
return;
8184
}
8285

@@ -93,7 +96,7 @@ internal sealed override void SetMutableBinding(Key name, JsValue value, bool st
9396

9497
if (binding.Mutable)
9598
{
96-
_dictionary[name] = binding.ChangeValue(value);
99+
binding = binding.ChangeValue(value);
97100
}
98101
else
99102
{
@@ -116,7 +119,7 @@ internal override JsValue GetBindingValue(Key name, bool strict)
116119
}
117120

118121
[MethodImpl(MethodImplOptions.NoInlining)]
119-
private void ThrowUninitializedBindingError(string name)
122+
private void ThrowUninitializedBindingError(Key name)
120123
{
121124
ExceptionHelper.ThrowReferenceError(_engine.Realm, $"Cannot access '{name}' before initialization");
122125
}
@@ -151,7 +154,7 @@ internal sealed override string[] GetAllBindingNames()
151154
{
152155
if (_dictionary is null)
153156
{
154-
return Array.Empty<string>();
157+
return [];
155158
}
156159

157160
var keys = new string[_dictionary.Count];
@@ -183,3 +186,18 @@ internal void TransferTo(List<Key> names, DeclarativeEnvironment env)
183186
}
184187
}
185188
}
189+
190+
internal static class DictionaryExtensions
191+
{
192+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
193+
internal static void CreateMutableBinding<T>(this T dictionary, Key name, bool canBeDeleted = false) where T : IEngineDictionary<Key, Binding>
194+
{
195+
dictionary[name] = new Binding(null!, canBeDeleted, mutable: true, strict: false);
196+
}
197+
198+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
199+
internal static void CreateImmutableBinding<T>(this T dictionary, Key name, bool strict = true) where T : IEngineDictionary<Key, Binding>
200+
{
201+
dictionary[name] = new Binding(null!, canBeDeleted: false, mutable: false, strict);
202+
}
203+
}

Jint/Runtime/Environments/FunctionEnvironment.cs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public FunctionEnvironment(
3232
{
3333
_functionObject = functionObject;
3434
NewTarget = newTarget;
35-
if (functionObject._functionDefinition?.Function is ArrowFunctionExpression)
35+
if (functionObject._functionDefinition?.Function.Type is NodeType.ArrowFunctionExpression)
3636
{
3737
_thisBindingStatus = ThisBindingStatus.Lexical;
3838
}
@@ -107,21 +107,19 @@ internal void InitializeParameters(
107107

108108
var value = hasDuplicates ? Undefined : null;
109109
var directSet = !hasDuplicates && (_dictionary is null || _dictionary.Count == 0);
110+
_dictionary ??= new HybridDictionary<Binding>(parameterNames.Length, checkExistingKeys: !directSet);
110111
for (uint i = 0; i < (uint) parameterNames.Length; i++)
111112
{
112113
var paramName = parameterNames[i];
113-
if (directSet || _dictionary is null || !_dictionary.ContainsKey(paramName))
114+
ref var binding = ref _dictionary.GetValueRefOrAddDefault(paramName, out var exists);
115+
if (directSet || !exists)
114116
{
115-
var parameterValue = value;
116-
if (arguments != null)
117-
{
118-
parameterValue = i < (uint) arguments.Length ? arguments[i] : Undefined;
119-
}
120-
121-
_dictionary ??= new HybridDictionary<Binding>();
122-
_dictionary[paramName] = new Binding(parameterValue!, canBeDeleted: false, mutable: true, strict: false);
117+
var parameterValue = arguments?.At((int) i, Undefined) ?? value;
118+
binding = new Binding(parameterValue!, canBeDeleted: false, mutable: true, strict: false);
123119
}
124120
}
121+
122+
_dictionary.CheckExistingKeys = true;
125123
}
126124

127125
internal void AddFunctionParameters(EvaluationContext context, IFunction functionDeclaration, JsValue[] arguments)

0 commit comments

Comments
 (0)