diff --git a/Jint/Collections/HybridDictionary.cs b/Jint/Collections/HybridDictionary.cs index 6ea1935257..10c67b0586 100644 --- a/Jint/Collections/HybridDictionary.cs +++ b/Jint/Collections/HybridDictionary.cs @@ -119,40 +119,39 @@ public void SetOrUpdateValue(Key key, Func updat currentValue = updater(currentValue, state); } - private bool SwitchToDictionary(Key key, TValue value, bool tryAdd) + private bool SwitchToDictionary(Key key, TValue value, bool tryAdd, int capacity = InitialDictionarySize) { - var dictionary = new StringDictionarySlim(InitialDictionarySize); - foreach (var pair in _list) - { - dictionary[pair.Key] = pair.Value; - } + SwitchToDictionary(capacity); - bool result; if (tryAdd) { - result = dictionary.TryAdd(key, value); - } - else - { - dictionary[key] = value; - result = true; + return _dictionary.TryAdd(key, value); } - _dictionary = dictionary; - _list = null; - return result; + _dictionary[key] = value; + return true; + } + + private ref TValue SwitchToDictionary(Key key, int capacity = InitialDictionarySize) + { + SwitchToDictionary(capacity); + return ref _dictionary[key]; } - private ref TValue SwitchToDictionary(Key key) + private void SwitchToDictionary(int capacity = InitialDictionarySize) { - var dictionary = new StringDictionarySlim(InitialDictionarySize); - foreach (var pair in _list) + var dictionary = new StringDictionarySlim(capacity); + + if (_list is not null) { - dictionary[pair.Key] = pair.Value; + foreach (var pair in _list) + { + dictionary[pair.Key] = pair.Value; + } } + _dictionary = dictionary; _list = null; - return ref dictionary[key]; } public int Count @@ -161,6 +160,21 @@ public int Count get => _dictionary?.Count ?? _list?.Count ?? 0; } + public void EnsureCapacity(int capacity) + { + if (_dictionary is not null) + { + // not implemented yet + return; + } + + if (capacity >= CutoverPoint) + { + SwitchToDictionary(capacity); + } + } + + public bool TryAdd(Key key, TValue value) { if (_dictionary != null) diff --git a/Jint/Engine.cs b/Jint/Engine.cs index 932ed400ca..c25b174a9f 100644 --- a/Jint/Engine.cs +++ b/Jint/Engine.cs @@ -2,6 +2,7 @@ using System.Collections.Concurrent; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; +using Jint.Collections; using Jint.Native; using Jint.Native.Function; using Jint.Native.Generator; @@ -1096,7 +1097,7 @@ private void GlobalDeclarationInstantiation( // Else, // Perform ? IteratorBindingInitialization for formals with iteratorRecord and env as arguments. - Environment varEnv; + DeclarativeEnvironment varEnv; if (!hasParameterExpressions) { // NOTE: Only a single lexical environment is needed for the parameters and top-level vars. @@ -1130,7 +1131,7 @@ private void GlobalDeclarationInstantiation( // NOTE: Annex B.3.3.1 adds additional steps at this point. // A https://tc39.es/ecma262/#sec-web-compat-functiondeclarationinstantiation - Environment lexEnv; + DeclarativeEnvironment lexEnv; if (!strict) { lexEnv = JintEnvironment.NewDeclarativeEnvironment(this, varEnv); @@ -1146,21 +1147,20 @@ private void GlobalDeclarationInstantiation( UpdateLexicalEnvironment(lexEnv); - if (configuration.LexicalDeclarations.Length > 0) + if (configuration.LexicalDeclarations.Count > 0) { - foreach (var d in configuration.LexicalDeclarations) + 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++) { - for (var j = 0; j < d.BoundNames.Count; j++) + var d = configuration.LexicalDeclarations[i]; + if (d.IsConstantDeclaration) { - var dn = d.BoundNames[j]; - if (d.IsConstantDeclaration) - { - lexEnv.CreateImmutableBinding(dn, strict: true); - } - else - { - lexEnv.CreateMutableBinding(dn, canBeDeleted: false); - } + dictionary[d.BoundName] = new Binding(null!, canBeDeleted: false, mutable: false, strict); + } + else + { + dictionary[d.BoundName] = new Binding(null!, canBeDeleted: false, mutable: true, strict: false); } } } diff --git a/Jint/Native/Function/Function.cs b/Jint/Native/Function/Function.cs index 8742342aa8..c0bc1f0650 100644 --- a/Jint/Native/Function/Function.cs +++ b/Jint/Native/Function/Function.cs @@ -318,6 +318,7 @@ internal void OrdinaryCallBindThis(in ExecutionContext calleeContext, JsValue th /// /// https://tc39.es/ecma262/#sec-prepareforordinarycall /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ref readonly ExecutionContext PrepareForOrdinaryCall(JsValue newTarget) { var callerContext = _engine.ExecutionContext; diff --git a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs index 4fdc2decc1..1b8373565b 100644 --- a/Jint/Runtime/Interpreter/JintFunctionDefinition.cs +++ b/Jint/Runtime/Interpreter/JintFunctionDefinition.cs @@ -196,21 +196,14 @@ internal sealed class State public List? VarNames; public LinkedList? FunctionsToInitialize; public readonly HashSet FunctionNames = new(); - public LexicalVariableDeclaration[] LexicalDeclarations = Array.Empty(); + public List LexicalDeclarations = []; public HashSet? ParameterBindings; public List? VarsToInitialize; - internal struct VariableValuePair - { - public Key Name; - public JsValue? InitialValue; - } + internal readonly record struct VariableValuePair(Key Name, JsValue? InitialValue); - internal struct LexicalVariableDeclaration - { - public bool IsConstantDeclaration; - public List BoundNames; - } + [StructLayout(LayoutKind.Auto)] + internal readonly record struct LexicalVariableDeclaration(Key BoundName, bool IsConstantDeclaration); } internal static State BuildState(IFunction function) @@ -332,19 +325,19 @@ internal static State BuildState(IFunction function) if (hoistingScope._lexicalDeclarations != null) { - var _lexicalDeclarations = hoistingScope._lexicalDeclarations; - var lexicalDeclarationsCount = _lexicalDeclarations.Count; - var declarations = new State.LexicalVariableDeclaration[lexicalDeclarationsCount]; + 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]; - var boundNames = new List(); + var d = lexicalDeclarations[i]; + boundNames.Clear(); d.GetBoundNames(boundNames); - declarations[i] = new State.LexicalVariableDeclaration + for (var j = 0; j < boundNames.Count; j++) { - IsConstantDeclaration = d.IsConstantDeclaration(), - BoundNames = boundNames - }; + declarations.Add(new State.LexicalVariableDeclaration(boundNames[j], d.IsConstantDeclaration())); + } } state.LexicalDeclarations = declarations; }