Przeglądaj źródła

Backport var declaration and reference resolving performance improvements (#1841)

* Update test262 suite and fix issues
* Run GH Actions workflows for 3.x branch
Marko Lahma 1 rok temu
rodzic
commit
78bc1ecd3c

+ 1 - 1
.github/workflows/build.yml

@@ -3,7 +3,7 @@ name: Build
 on:
 
   push:
-    branches: [ main ]
+    branches: [ main, 3.x ]
     paths-ignore:
     - 'doc/**'
     - '**.md'

+ 1 - 1
.github/workflows/pr.yml

@@ -3,7 +3,7 @@ name: PR Check
 on:
 
   pull_request:
-    branches: [ main, release/2.x ]
+    branches: [ main, release/2.x, 3.x ]
 
 jobs:
 

+ 7 - 2
Jint.Tests.Test262/Test262Harness.settings.json

@@ -1,5 +1,5 @@
 {
-  "SuiteGitSha": "45e740928f9ac3c2144307fee20bb58570fb9588",
+  "SuiteGitSha": "c2ae5ed5e90d86e17730730b003e9b6fb050693e",
   //"SuiteDirectory": "//mnt/c/work/test262",
   "TargetPath": "./Generated",
   "Namespace": "Jint.Tests.Test262",
@@ -8,10 +8,12 @@
     "Array.fromAsync",
     "async-iteration",
     "Atomics",
+    "Float16Array",
     "import-assertions",
     "iterator-helpers",
     "regexp-duplicate-named-groups",
     "regexp-lookbehind",
+    "regexp-modifiers",
     "regexp-unicode-property-escapes",
     "regexp-v-flag",
     "tail-call-optimization",
@@ -44,6 +46,9 @@
     "built-ins/RegExp/lookahead-quantifier-match-groups.js",
     "built-ins/RegExp/unicode_full_case_folding.js",
 
+    // needs "small" async rewrite
+    "language/module-code/top-level-await/async-module-does-not-block-sibling-modules.js",
+
     // requires investigation how to process complex function name evaluation for property
     "built-ins/Function/prototype/toString/method-computed-property-name.js",
     "language/expressions/class/elements/class-name-static-initializer-anonymous.js",
@@ -68,7 +73,7 @@
     // C# can't distinguish 1.797693134862315808e+308 and 1.797693134862315708145274237317e+308
     "language/types/number/8.5.1.js",
 
-    // inner binding is immutable (from parameters) Expected SameValue(«null», «function() {{ ... }}») to be true
+    // inner binding is immutable (from parameters) Expected SameValue(«null», «function() {{ ... }}») to be true
     "language/expressions/function/scope-name-var-open-non-strict.js",
     "language/expressions/function/scope-name-var-open-strict.js",
 

+ 16 - 10
Jint/Engine.cs

@@ -570,6 +570,9 @@ namespace Jint
             return GetValue(reference, returnReferenceToPool);
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-getvalue
+        /// </summary>
         internal JsValue GetValue(Reference reference, bool returnReferenceToPool)
         {
             var baseValue = reference.Base;
@@ -607,7 +610,8 @@ namespace Jint
                         return baseObj.PrivateGet((PrivateName) reference.ReferencedName);
                     }
 
-                    var v = baseObj.Get(property, reference.ThisValue);
+                    reference.EvaluateAndCachePropertyKey();
+                    var v = baseObj.Get(reference.ReferencedName, reference.ThisValue);
                     return v;
                 }
 
@@ -684,33 +688,35 @@ namespace Jint
         /// </summary>
         internal void PutValue(Reference reference, JsValue value)
         {
+            var property = reference.ReferencedName;
             if (reference.IsUnresolvableReference)
             {
-                if (reference.Strict && reference.ReferencedName != CommonProperties.Arguments)
+                if (reference.Strict && property != CommonProperties.Arguments)
                 {
                     ExceptionHelper.ThrowReferenceError(Realm, reference);
                 }
 
-                Realm.GlobalObject.Set(reference.ReferencedName, value, throwOnError: false);
+                Realm.GlobalObject.Set(property, value, throwOnError: false);
             }
             else if (reference.IsPropertyReference)
             {
                 var baseObject = Runtime.TypeConverter.ToObject(Realm, reference.Base);
                 if (reference.IsPrivateReference)
                 {
-                    baseObject.PrivateSet((PrivateName) reference.ReferencedName, value);
+                    baseObject.PrivateSet((PrivateName) property, value);
                     return;
                 }
 
+                reference.EvaluateAndCachePropertyKey();
                 var succeeded = baseObject.Set(reference.ReferencedName, value, reference.ThisValue);
                 if (!succeeded && reference.Strict)
                 {
-                    ExceptionHelper.ThrowTypeError(Realm, "Cannot assign to read only property '" + reference.ReferencedName + "' of " + baseObject);
+                    ExceptionHelper.ThrowTypeError(Realm, "Cannot assign to read only property '" + property + "' of " + baseObject);
                 }
             }
             else
             {
-                ((Environment) reference.Base).SetMutableBinding(Runtime.TypeConverter.ToString(reference.ReferencedName), value, reference.Strict);
+                ((Environment) reference.Base).SetMutableBinding(Runtime.TypeConverter.ToString(property), value, reference.Strict);
             }
         }
 
@@ -891,7 +897,7 @@ namespace Jint
         public JsValue GetValue(JsValue scope, JsValue property)
         {
             var reference = _referencePool.Rent(scope, property, _isStrict, thisValue: null);
-            var jsValue = GetValue(reference, false);
+            var jsValue = GetValue(reference, returnReferenceToPool: false);
             _referencePool.Return(reference);
             return jsValue;
         }
@@ -1002,7 +1008,7 @@ namespace Jint
             for (var i = 0; i < lexNames.Count; i++)
             {
                 var (dn, constant) = lexNames[i];
-                if (env.HasVarDeclaration(dn) || env.HasLexicalDeclaration(dn) || env.HasRestrictedGlobalProperty(dn))
+                if (env.HasLexicalDeclaration(dn) || env.HasRestrictedGlobalProperty(dn))
                 {
                     ExceptionHelper.ThrowSyntaxError(realm, $"Identifier '{dn}' has already been declared");
                 }
@@ -1017,7 +1023,7 @@ namespace Jint
                 }
             }
 
-            // we need to go trough in reverse order to handle the hoisting correctly
+            // we need to go through in reverse order to handle the hoisting correctly
             for (var i = functionToInitialize.Count - 1; i > -1; i--)
             {
                 var f = functionToInitialize[i];
@@ -1367,7 +1373,7 @@ namespace Jint
             {
                 if (varEnvRec is GlobalEnvironment ger)
                 {
-                    ger.CreateGlobalVarBinding(vn, true);
+                    ger.CreateGlobalVarBinding(vn, canBeDeleted: true);
                 }
                 else
                 {

+ 12 - 1
Jint/Native/Array/ArrayIteratorPrototype.cs

@@ -13,6 +13,8 @@ namespace Jint.Native.Array;
 /// </summary>
 internal sealed class ArrayIteratorPrototype : IteratorPrototype
 {
+    private ClrFunction? _originalNextFunction;
+
     internal ArrayIteratorPrototype(
         Engine engine,
         Realm realm,
@@ -22,9 +24,10 @@ internal sealed class ArrayIteratorPrototype : IteratorPrototype
 
     protected override void Initialize()
     {
+        _originalNextFunction = new ClrFunction(Engine, "next", Next, 0, PropertyFlag.Configurable);
         var properties = new PropertyDictionary(1, checkExistingKeys: false)
         {
-            [KnownKeys.Next] = new(new ClrFunction(Engine, "next", Next, 0, PropertyFlag.Configurable), true, false, true)
+            [KnownKeys.Next] = new(_originalNextFunction, PropertyFlag.NonEnumerable)
         };
         SetProperties(properties);
 
@@ -37,6 +40,11 @@ internal sealed class ArrayIteratorPrototype : IteratorPrototype
 
     internal IteratorInstance Construct(ObjectInstance array, ArrayIteratorType kind)
     {
+        if (!HasOriginalNext)
+        {
+            return new IteratorInstance.ObjectIterator(this);
+        }
+
         IteratorInstance instance = array is JsArray jsArray
             ? new ArrayIterator(Engine, jsArray, kind)  { _prototype = this }
             : new ArrayLikeIterator(Engine, array, kind) { _prototype = this };
@@ -44,6 +52,9 @@ internal sealed class ArrayIteratorPrototype : IteratorPrototype
         return instance;
     }
 
+    internal bool HasOriginalNext
+        => ReferenceEquals(Get(CommonProperties.Next), _originalNextFunction);
+
     private sealed class ArrayIterator : IteratorInstance
     {
         private readonly ArrayIteratorType _kind;

+ 47 - 30
Jint/Native/TypedArray/IntrinsicTypedArrayPrototype.cs

@@ -1313,56 +1313,73 @@ namespace Jint.Native.TypedArray
                 ExceptionHelper.ThrowTypeError(_realm);
             }
 
-            var begin = arguments.At(0);
+            var start = arguments.At(0);
             var end = arguments.At(1);
 
             var buffer = o._viewedArrayBuffer;
-            var srcLength = o.GetLength();
-            var relativeBegin = TypeConverter.ToIntegerOrInfinity(begin);
+            var srcRecord = MakeTypedArrayWithBufferWitnessRecord(o, ArrayBufferOrder.SeqCst);
 
-            double beginIndex;
-            if (double.IsNegativeInfinity(relativeBegin))
-            {
-                beginIndex = 0;
-            }
-            else if (relativeBegin < 0)
+            uint srcLength = 0;
+            if (!srcRecord.IsTypedArrayOutOfBounds)
             {
-                beginIndex = System.Math.Max(srcLength + relativeBegin, 0);
+                srcLength = srcRecord.TypedArrayLength;
             }
-            else
+
+            var relativeStart = TypeConverter.ToIntegerOrInfinity(start);
+
+            double startIndex;
+            if (double.IsNegativeInfinity(relativeStart))
             {
-                beginIndex = System.Math.Min(relativeBegin, srcLength);
+                startIndex = 0;
             }
-
-            double relativeEnd;
-            if (end.IsUndefined())
+            else if (relativeStart < 0)
             {
-                relativeEnd = srcLength;
+                startIndex = System.Math.Max(srcLength + relativeStart, 0);
             }
             else
             {
-                relativeEnd = TypeConverter.ToIntegerOrInfinity(end);
+                startIndex = System.Math.Min(relativeStart, srcLength);
             }
 
-            double endIndex;
-            if (double.IsNegativeInfinity(relativeEnd))
-            {
-                endIndex = 0;
-            }
-            else if (relativeEnd < 0)
+            var elementSize = o._arrayElementType.GetElementSize();
+            var srcByteOffset = o._byteOffset;
+            var beginByteOffset = srcByteOffset + startIndex * elementSize;
+
+            JsValue[] argumentsList;
+            if (o._arrayLength == JsTypedArray.LengthAuto && end.IsUndefined())
             {
-                endIndex = System.Math.Max(srcLength + relativeEnd, 0);
+                argumentsList = [buffer, beginByteOffset];
             }
             else
             {
-                endIndex = System.Math.Min(relativeEnd, srcLength);
+                double relativeEnd;
+                if (end.IsUndefined())
+                {
+                    relativeEnd = srcLength;
+                }
+                else
+                {
+                    relativeEnd = TypeConverter.ToIntegerOrInfinity(end);
+                }
+
+                double endIndex;
+                if (double.IsNegativeInfinity(relativeEnd))
+                {
+                    endIndex = 0;
+                }
+                else if (relativeEnd < 0)
+                {
+                    endIndex = System.Math.Max(srcLength + relativeEnd, 0);
+                }
+                else
+                {
+                    endIndex = System.Math.Min(relativeEnd, srcLength);
+                }
+
+                var newLength = System.Math.Max(endIndex - startIndex, 0);
+                argumentsList = [buffer, beginByteOffset, newLength];
             }
 
-            var newLength = System.Math.Max(endIndex - beginIndex, 0);
-            var elementSize = o._arrayElementType.GetElementSize();
-            var srcByteOffset = o._byteOffset;
-            var beginByteOffset = srcByteOffset + beginIndex * elementSize;
-            var argumentsList = new JsValue[] { buffer, beginByteOffset, newLength };
             return _realm.Intrinsics.TypedArray.TypedArraySpeciesCreate(o, argumentsList);
         }
 

+ 9 - 18
Jint/Runtime/Environments/GlobalEnvironment.cs

@@ -29,7 +29,6 @@ namespace Jint.Runtime.Environments
 
         // Environment records are needed by debugger
         internal readonly GlobalDeclarativeEnvironment _declarativeRecord;
-        private readonly HashSet<Key> _varNames = [];
 
         public GlobalEnvironment(Engine engine, ObjectInstance global) : base(engine)
         {
@@ -258,15 +257,10 @@ namespace Jint.Runtime.Environments
                 return _declarativeRecord.DeleteBinding(name);
             }
 
-            if (_global.HasOwnProperty(name.Name))
+            var n = JsString.Create(name.Name);
+            if (_global.HasOwnProperty(n))
             {
-                var status = _global.Delete(name.Name);
-                if (status)
-                {
-                    _varNames.Remove(name);
-                }
-
-                return status;
+                return _global.Delete(n);
             }
 
             return true;
@@ -280,8 +274,6 @@ namespace Jint.Runtime.Environments
 
         internal override JsValue GetThisBinding() => _global;
 
-        internal bool HasVarDeclaration(Key name) => _varNames.Contains(name);
-
         internal bool HasLexicalDeclaration(Key name) => _declarativeRecord.HasBinding(name);
 
         internal bool HasRestrictedGlobalProperty(Key name)
@@ -334,12 +326,14 @@ namespace Jint.Runtime.Environments
 
         public void CreateGlobalVarBinding(Key name, bool canBeDeleted)
         {
-            if (_global.Extensible && _global._properties!.TryAdd(name, new PropertyDescriptor(Undefined, canBeDeleted
-                    ? PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding
-                    : PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding)))
+            if (!_global.Extensible)
             {
-                _varNames.Add(name);
+                return;
             }
+
+            _global._properties!.TryAdd(name, new PropertyDescriptor(Undefined, canBeDeleted
+                ? PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding
+                : PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding));
         }
 
         internal void CreateGlobalVarBindings(List<Key> names, bool canBeDeleted)
@@ -356,8 +350,6 @@ namespace Jint.Runtime.Environments
                 _global._properties!.TryAdd(name, new PropertyDescriptor(Undefined, canBeDeleted
                     ? PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding
                     : PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding));
-
-                _varNames.Add(name);
             }
         }
 
@@ -381,7 +373,6 @@ namespace Jint.Runtime.Environments
 
             _global.DefinePropertyOrThrow(jsString, desc);
             _global.Set(jsString, value, false);
-            _varNames.Add(name);
         }
 
         internal override bool HasBindings()

+ 1 - 0
Jint/Runtime/Interpreter/Expressions/DestructuringPatternAssignmentExpression.cs

@@ -145,6 +145,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     {
                         close = true;
                         var reference = GetReferenceFromMember(context, me);
+
                         JsValue value;
                         if (arrayOperations != null)
                         {

+ 1 - 6
Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs

@@ -127,12 +127,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 return MakePrivateReference(engine, baseValue, property);
             }
 
-            // only convert if necessary
-            var propertyKey = property.IsInteger() && baseValue.IsIntegerIndexedArray
-                ? property
-                : TypeConverter.ToPropertyKey(property);
-
-            return context.Engine._referencePool.Rent(baseValue, propertyKey, isStrictModeCode, thisValue: actualThis);
+            return context.Engine._referencePool.Rent(baseValue, property, isStrictModeCode, thisValue: actualThis);
         }
 
         /// <summary>

+ 8 - 7
Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs

@@ -207,7 +207,6 @@ namespace Jint.Runtime.Interpreter.Expressions
                         return JsBoolean.True;
                     }
 
-                    var referencedName = r.ReferencedName;
                     if (r.IsPropertyReference)
                     {
                         if (r.IsSuperReference)
@@ -216,17 +215,20 @@ namespace Jint.Runtime.Interpreter.Expressions
                         }
 
                         var o = TypeConverter.ToObject(engine.Realm, r.Base);
-                        var deleteStatus = o.Delete(referencedName);
+
+                        r.EvaluateAndCachePropertyKey();
+                        var deleteStatus = o.Delete(r.ReferencedName);
+
                         if (!deleteStatus)
                         {
                             if (r.Strict)
                             {
-                                ExceptionHelper.ThrowTypeError(engine.Realm, $"Cannot delete property '{referencedName}' of {o}");
+                                ExceptionHelper.ThrowTypeError(engine.Realm, $"Cannot delete property '{r.ReferencedName}' of {o}");
                             }
 
-                            if (StrictModeScope.IsStrictModeCode && !r.Base.AsObject().GetOwnProperty(referencedName).Configurable)
+                            if (StrictModeScope.IsStrictModeCode && !r.Base.AsObject().GetOwnProperty(r.ReferencedName).Configurable)
                             {
-                                ExceptionHelper.ThrowTypeError(engine.Realm, $"Cannot delete property '{referencedName}' of {o}");
+                                ExceptionHelper.ThrowTypeError(engine.Realm, $"Cannot delete property '{r.ReferencedName}' of {o}");
                             }
                         }
 
@@ -240,10 +242,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                     }
 
                     var bindings = (Environment) r.Base;
-                    var property = referencedName;
                     engine._referencePool.Return(r);
 
-                    return bindings.DeleteBinding(property.ToString()) ? JsBoolean.True : JsBoolean.False;
+                    return bindings.DeleteBinding(r.ReferencedName.ToString()) ? JsBoolean.True : JsBoolean.False;
 
                 case Operator.Void:
                     _argument.GetValue(context);

+ 8 - 0
Jint/Runtime/Reference.cs

@@ -108,4 +108,12 @@ public sealed class Reference
     {
         ((Environment) _base).InitializeBinding(TypeConverter.ToString(_referencedName), value);
     }
+
+    internal void EvaluateAndCachePropertyKey()
+    {
+        if (!(_referencedName.IsInteger() && _base.IsIntegerIndexedArray))
+        {
+            _referencedName = Runtime.TypeConverter.ToPropertyKey(_referencedName);
+        }
+    }
 }