Przeglądaj źródła

Enable top-level await tests and fix issues (#1552)

Marko Lahma 2 lat temu
rodzic
commit
48c2992689

+ 10 - 55
Jint.Tests.Test262/Test262Harness.settings.json

@@ -18,7 +18,6 @@
     "resizable-arraybuffer",
     "SharedArrayBuffer",
     "tail-call-optimization",
-    "top-level-await",
     "Temporal",
     "u180e"
   ],
@@ -123,42 +122,24 @@
     "language/expressions/dynamic-import/assignment-expression/yield-expr.js",
     "language/statements/class/subclass/builtin-objects/GeneratorFunction/*.js",
     "language/**/*-yield-*.js",
+    "language/module-code/instn-local-bndng-gen.js",
+    "language/module-code/instn-local-bndng-export-gen.js",
 
-    // JavaScriptParser cannot handle direct 'super.property' script code
-    "language/expressions/super/prop-dot-cls-val-from-eval.js",
-    "language/expressions/super/prop-dot-obj-val-from-eval.js",
-    "language/expressions/super/prop-expr-cls-val-from-eval.js",
-    "language/expressions/super/prop-expr-obj-val-from-eval.js",
-
-    // Esprima problem
-
-    "language/literals/regexp/u-surrogate-pairs-atom-escape-decimal.js",
-    "language/literals/regexp/u-unicode-esc.js",
-    "language/statements/class/definition/methods-async-super-call-param.js",
-    "built-ins/AsyncFunction/AsyncFunction-construct.js\n",
-    "built-ins/String/prototype/split/separator-regexp.js",
-
-
-    "language/expressions/object/method-definition/async-super-call-param.js",
-    "language/expressions/object/method-definition/name-super-prop-param.js",
-    "language/expressions/optional-chaining/member-expression.js",
-    "language/expressions/tagged-template/invalid-escape-sequences.js",
-    "language/statements/for-of/dstr-obj-id-init-let.js",
-    "language/statements/for/head-lhs-let.js",
-    "language/expressions/object/yield-non-strict-access.js",
-    "language/expressions/object/yield-non-strict-syntax.js",
+     // Esprima problem
+     "built-ins/String/prototype/split/separator-regexp.js",
     "language/expressions/object/let-non-strict-access.js",
     "language/expressions/object/let-non-strict-syntax.js",
-    "language/expressions/assignment/dstr-obj-id-identifier-yield-ident-valid.js",
+    "language/expressions/object/yield-non-strict-access.js",
+    "language/expressions/object/yield-non-strict-syntax.js",
+    "language/expressions/tagged-template/invalid-escape-sequences.js",
+    "language/literals/regexp/u-surrogate-pairs-atom-escape-decimal.js",
+    "language/literals/regexp/u-unicode-esc.js",
     "language/statements/for-of/dstr-obj-id-identifier-yield-ident-valid.js",
-    "language/white-space/mongolian-vowel-separator-eval.js",
+    "language/statements/for/head-lhs-let.js",
 
     // SharedArrayBuffer not implemented
     "built-ins/SharedArrayBuffer/prototype/prop-desc.js",
 
-    // Esprima has parsing problems with weirdish unicode identifiers
-    "language/identifiers/*-unicode-*.js",
-
     // special casing data
     "built-ins/**/special_casing*.js",
 
@@ -366,32 +347,6 @@
     "language/literals/string/line-separator.js",
     "language/literals/string/paragraph-separator-eval.js",
     "language/literals/string/paragraph-separator.js",
-    "language/module-code/eval-self-once.js",
-    "language/module-code/export-expname-binding-index.js",
-    "language/module-code/instn-iee-bndng-cls.js",
-    "language/module-code/instn-iee-bndng-const.js",
-    "language/module-code/instn-iee-bndng-fun.js",
-    "language/module-code/instn-iee-bndng-let.js",
-    "language/module-code/instn-iee-bndng-var.js",
-    "language/module-code/instn-local-bndng-export-gen.js",
-    "language/module-code/instn-local-bndng-gen.js",
-    "language/module-code/instn-named-bndng-cls.js",
-    "language/module-code/instn-named-bndng-const.js",
-    "language/module-code/instn-named-bndng-dflt-cls.js",
-    "language/module-code/instn-named-bndng-dflt-expr.js",
-    "language/module-code/instn-named-bndng-dflt-fun-anon.js",
-    "language/module-code/instn-named-bndng-dflt-named.js",
-    "language/module-code/instn-named-bndng-dflt-star.js",
-    "language/module-code/instn-named-bndng-let.js",
-    "language/module-code/instn-named-bndng-trlng-comma.js",
-    "language/module-code/instn-named-bndng-var.js",
-    "language/module-code/namespace/internals/delete-exported-uninit.js",
-    "language/module-code/namespace/internals/enumerate-binding-uninit.js",
-    "language/module-code/namespace/internals/get-own-property-str-found-uninit.js",
-    "language/module-code/namespace/internals/get-str-found-uninit.js",
-    "language/module-code/namespace/internals/object-hasOwnProperty-binding-uninit.js",
-    "language/module-code/namespace/internals/object-keys-binding-uninit.js",
-    "language/module-code/namespace/internals/object-propertyIsEnumerable-binding-uninit.js",
     "language/statementList/eval-block-with-statment-block.js",
     "language/statements/for-of/dstr/array-elem-put-obj-literal-prop-ref-init-active.js",
     "language/statements/for-of/dstr/array-elem-put-obj-literal-prop-ref-init.js",

+ 3 - 2
Jint.Tests.Test262/Test262Test.cs

@@ -88,8 +88,9 @@ public abstract partial class Test262Test
     {
         if (file.Type == ProgramType.Module)
         {
-            engine.AddModule(file.FileName, builder => builder.AddSource(file.Program));
-            engine.ImportModule(file.FileName);
+            var specifier = "./" + Path.GetFileName(file.FileName);
+            engine.AddModule(specifier, builder => builder.AddSource(file.Program));
+            engine.ImportModule(specifier);
         }
         else
         {

+ 0 - 1
Jint.Tests/Runtime/ModuleTests.cs

@@ -98,7 +98,6 @@ public class ModuleTests
         _engine.AddModule("my-module", @"import('imported-module').then(ns => { ns.signal(); });");
 
         _engine.ImportModule("my-module");
-        _engine.RunAvailableContinuations();
 
         Assert.True(received);
     }

+ 3 - 3
Jint/Engine.Modules.cs

@@ -130,14 +130,14 @@ namespace Jint
             module.Link();
         }
 
-        private JsValue EvaluateModule(string specifier, ModuleRecord cyclicModule)
+        private JsValue EvaluateModule(string specifier, ModuleRecord module)
         {
             var ownsContext = _activeEvaluationContext is null;
             _activeEvaluationContext ??= new EvaluationContext(this);
             JsValue evaluationResult;
             try
             {
-                evaluationResult = cyclicModule.Evaluate();
+                evaluationResult = module.Evaluate();
             }
             finally
             {
@@ -154,7 +154,7 @@ namespace Jint
             }
             else if (promise.State == PromiseState.Rejected)
             {
-                var location = cyclicModule is CyclicModuleRecord cyclicModuleRecord
+                var location = module is CyclicModuleRecord cyclicModuleRecord
                     ? cyclicModuleRecord.AbnormalCompletionLocation
                     : Location.From(new Position(), new Position());
 

+ 12 - 3
Jint/Engine.cs

@@ -404,12 +404,12 @@ namespace Jint
                     throw ex;
                 }
 
+                _completionValue = result.GetValueOrDefault();
+
                 // TODO what about callstack and thrown exceptions?
                 RunAvailableContinuations();
 
-                _completionValue = result.GetValueOrDefault();
-
-                return this;
+               return this;
             }
             finally
             {
@@ -459,7 +459,16 @@ namespace Jint
         internal void RunAvailableContinuations()
         {
             var queue = _eventLoop.Events;
+            if (queue.Count == 0)
+            {
+                return;
+            }
 
+            DoProcessEventLoop(queue);
+        }
+
+        private static void DoProcessEventLoop(Queue<Action> queue)
+        {
             while (true)
             {
                 if (queue.Count == 0)

+ 1 - 11
Jint/EsprimaExtensions.cs

@@ -438,17 +438,7 @@ namespace Jint
 
                     break;
                 case VariableDeclaration variableDeclaration:
-                    ref readonly var declarators = ref variableDeclaration.Declarations;
-                    for (var i = 0; i < declarators.Count; i++)
-                    {
-                        var declarator = declarators[i];
-                        var varName = declarator.Id.As<Identifier>()?.Name;
-                        if (varName is not null)
-                        {
-                            result.Add(varName);
-                        }
-                    }
-
+                    variableDeclaration.GetBoundNames(result);
                     break;
             }
 

+ 1 - 3
Jint/HoistingScope.cs

@@ -308,9 +308,7 @@ namespace Jint
                         var import = (ImportDeclaration) childNode;
                         import.GetImportEntries(_importEntries, _requestedModules);
                     }
-                    else if (childNode.Type == Nodes.ExportAllDeclaration ||
-                             childNode.Type == Nodes.ExportDefaultDeclaration ||
-                             childNode.Type == Nodes.ExportNamedDeclaration)
+                    else if (childNode.Type is Nodes.ExportAllDeclaration or Nodes.ExportDefaultDeclaration or Nodes.ExportNamedDeclaration)
                     {
                         _exportEntries ??= new();
                         _requestedModules ??= new();

+ 15 - 11
Jint/Native/Promise/PromiseConstructor.cs

@@ -94,16 +94,9 @@ namespace Jint.Native.Promise
             return promise;
         }
 
-        // The abstract operation PromiseResolve takes arguments C (a constructor) and x (an ECMAScript language value).
-        // It returns a new promise resolved with x. It performs the following steps when called:
-        //
-        // 1. Assert: Type(C) is Object.
-        // 2. If IsPromise(x) is true, then
-        //     a. Let xConstructor be ? Get(x, "constructor").
-        // b. If SameValue(xConstructor, C) is true, return x.
-        // 3. Let promiseCapability be ? NewPromiseCapability(C).
-        //     4. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »).
-        // 5. Return promiseCapability.[[Promise]].
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-promise.resolve
+        /// </summary>
         internal JsValue Resolve(JsValue thisObj, JsValue[] arguments)
         {
             if (!thisObj.IsObject())
@@ -116,7 +109,15 @@ namespace Jint.Native.Promise
                 ExceptionHelper.ThrowTypeError(_realm, "Promise.resolve invoked on a non-constructor value");
             }
 
-            JsValue x = arguments.At(0);
+            var x = arguments.At(0);
+            return PromiseResolve(thisObj, x);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-promise-resolve
+        /// </summary>
+        private JsValue PromiseResolve(JsValue thisObj, JsValue x)
+        {
             if (x.IsPromise())
             {
                 var xConstructor = x.Get(CommonProperties.Constructor);
@@ -133,6 +134,9 @@ namespace Jint.Native.Promise
             return instance;
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-promise.reject
+        /// </summary>
         private JsValue Reject(JsValue thisObj, JsValue[] arguments)
         {
             if (!thisObj.IsObject())

+ 9 - 4
Jint/Native/Promise/PromiseInstance.cs

@@ -85,9 +85,13 @@ internal sealed class PromiseInstance : ObjectInstance
     // https://tc39.es/ecma262/#sec-promise-resolve-functions
     private JsValue Resolve(JsValue thisObj, JsValue[] arguments)
     {
-        // Note that alreadyResolved logic lives in CreateResolvingFunctions method
-
         var result = arguments.At(0);
+        return Resolve(result);
+    }
+
+    internal JsValue Resolve(JsValue result)
+    {
+        // Note that alreadyResolved logic lives in CreateResolvingFunctions method
 
         if (ReferenceEquals(result, this))
         {
@@ -114,8 +118,9 @@ internal sealed class PromiseInstance : ObjectInstance
             return FulfillPromise(result);
         }
 
-        _engine.AddToEventLoop(
-            PromiseOperations.NewPromiseResolveThenableJob(this, resultObj, thenMethod));
+        var realm = _engine.Realm;
+        var job = PromiseOperations.NewPromiseResolveThenableJob(this, resultObj, thenMethod);
+        _engine._host.HostEnqueuePromiseJob(job, realm);
 
         return Undefined;
     }

+ 3 - 3
Jint/Native/ShadowRealm/ShadowRealm.cs

@@ -36,7 +36,9 @@ public sealed class ShadowRealm : ObjectInstance
     public JsValue ImportValue(string specifier, string exportName)
     {
         var callerRealm = _engine.Realm;
-        return ShadowRealmImportValue(specifier, exportName, callerRealm);
+        var value = ShadowRealmImportValue(specifier, exportName, callerRealm);
+        _engine.RunAvailableContinuations();
+        return value;
     }
 
     /// <summary>
@@ -210,8 +212,6 @@ public sealed class ShadowRealm : ObjectInstance
         var promiseCapability = PromiseConstructor.NewPromiseCapability(_engine, _engine.Realm.Intrinsics.Promise);
         var value = PromiseOperations.PerformPromiseThen(_engine, (PromiseInstance) innerCapability.PromiseInstance, onFulfilled, callerRealm.Intrinsics.ThrowTypeError, promiseCapability);
 
-        _engine.RunAvailableContinuations();
-
         return value;
     }
 

+ 8 - 0
Jint/Runtime/Host.cs

@@ -202,6 +202,14 @@ namespace Jint.Runtime
         {
             return new JobCallback(cleanupCallback, null);
         }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-hostenqueuepromisejob
+        /// </summary>
+        internal void HostEnqueuePromiseJob(Action job, Realm realm)
+        {
+            Engine.AddToEventLoop(job);
+        }
     }
 }
 

+ 11 - 0
Jint/Runtime/Interpreter/Expressions/JintAwaitExpression.cs

@@ -1,4 +1,7 @@
 using Esprima.Ast;
+using Jint.Native;
+using Jint.Native.Object;
+using Jint.Native.Promise;
 
 namespace Jint.Runtime.Interpreter.Expressions;
 
@@ -24,6 +27,14 @@ internal sealed class JintAwaitExpression : JintExpression
         try
         {
             var value = _awaitExpression.GetValue(context);
+
+            if (value is not PromiseInstance)
+            {
+                var promiseInstance = new PromiseInstance(engine);
+                promiseInstance.Resolve(value);
+                value = promiseInstance;
+            }
+
             engine.RunAvailableContinuations();
             return value.UnwrapIfPromise();
         }

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

@@ -42,7 +42,6 @@ internal sealed class JintImportExpression : JintExpression
         }
 
         context.Engine._host.ImportModuleDynamically(referencingScriptOrModule, specifierString, promiseCapability);
-        context.Engine.RunAvailableContinuations();
         return promiseCapability.PromiseInstance;
     }
 }

+ 9 - 5
Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs

@@ -218,13 +218,17 @@ namespace Jint.Runtime.Interpreter.Statements
                             close = true;
                             status = context.Completion;
                         }
-                        else if (lhsKind == LhsKind.LexicalBinding)
-                        {
-                            ((Reference) lhsRef).InitializeReferencedBinding(nextValue);
-                        }
                         else
                         {
-                            engine.PutValue((Reference) lhsRef, nextValue);
+                            var reference = (Reference) lhsRef;
+                            if (lhsKind == LhsKind.LexicalBinding || _leftNode.Type == Nodes.Identifier && !reference.IsUnresolvableReference)
+                            {
+                                reference.InitializeReferencedBinding(nextValue);
+                            }
+                            else
+                            {
+                                engine.PutValue(reference, nextValue);
+                            }
                         }
                     }
                     else

+ 2 - 2
Jint/Runtime/Modules/CyclicModuleRecord.cs

@@ -124,7 +124,7 @@ public abstract class CyclicModuleRecord : ModuleRecord
         module._topLevelCapability = capability;
 
         var result = module.InnerModuleEvaluation(stack, 0, ref asyncEvalOrder);
-        
+
         if (result.Type != CompletionType.Normal)
         {
             foreach (var m in stack)
@@ -389,7 +389,7 @@ public abstract class CyclicModuleRecord : ModuleRecord
                     requiredModule.Status = ModuleStatus.EvaluatingAsync;
                 }
 
-                done = requiredModule == this;
+                done = ReferenceEquals(requiredModule, this);
                 requiredModule._cycleRoot = this;
             }
         }

+ 11 - 5
Jint/Runtime/Modules/SourceTextModuleRecord.cs

@@ -342,11 +342,17 @@ internal class SourceTextModuleRecord : CyclicModuleRecord
             using (new StrictModeScope(true, force: true))
             {
                 _engine.EnterExecutionContext(moduleContext);
-                var statementList = new JintStatementList(null, _source.Body);
-                var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
-                var result = statementList.Execute(context); //Create new evaluation context when called from e.g. module tests
-                _engine.LeaveExecutionContext();
-                return result;
+                try
+                {
+                    var statementList = new JintStatementList(null, _source.Body);
+                    var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
+                    var result = statementList.Execute(context); //Create new evaluation context when called from e.g. module tests
+                    return result;
+                }
+                finally
+                {
+                    _engine.LeaveExecutionContext();
+                }
             }
         }
         else

+ 1 - 1
README.md

@@ -99,7 +99,7 @@ The entire execution engine was rebuild with performance in mind, in many cases
 
 - ✔ Class Fields
 - ✔ RegExp Match Indices
--  Top-level await
+-  Top-level await
 - ✔ Ergonomic brand checks for Private Fields
 - ✔ `.at()`
 - ✔ Accessible `Object.prototype.hasOwnProperty` (`Object.hasOwn`)