فهرست منبع

Groundwork for generators (#1705)

* basic constructor, prototype and instance,
* update some packages
* remove null value from completion which violates NRT promises
Marko Lahma 1 سال پیش
والد
کامیت
3ab67e416b
51فایلهای تغییر یافته به همراه648 افزوده شده و 219 حذف شده
  1. 8 8
      Directory.Packages.props
  2. 1 1
      Jint.Tests.Test262/.config/dotnet-tools.json
  3. 2 2
      Jint.Tests/Runtime/EngineLimitTests.cs
  4. 14 2
      Jint/Engine.cs
  5. 5 5
      Jint/JsValueExtensions.cs
  6. 2 2
      Jint/Native/Array/ArrayInstance.cs
  7. 2 2
      Jint/Native/Array/ArrayOperations.cs
  8. 1 1
      Jint/Native/Date/DatePrototype.cs
  9. 41 29
      Jint/Native/Function/ClassDefinition.cs
  10. 7 8
      Jint/Native/Function/FunctionKind.cs
  11. 46 0
      Jint/Native/Generator/GeneratorFunctionConstructor.cs
  12. 44 0
      Jint/Native/Generator/GeneratorFunctionPrototype.cs
  13. 155 0
      Jint/Native/Generator/GeneratorInstance.cs
  14. 89 0
      Jint/Native/Generator/GeneratorPrototype.cs
  15. 10 0
      Jint/Native/Generator/GeneratorState.cs
  16. 2 2
      Jint/Native/Global/GlobalObject.cs
  17. 16 0
      Jint/Native/Iterator/IteratorResult.cs
  18. 17 0
      Jint/Native/JsEmpty.cs
  19. 5 3
      Jint/Native/JsValue.cs
  20. 7 7
      Jint/Native/Object/ObjectInstance.cs
  21. 1 1
      Jint/Native/Proxy/JsProxy.cs
  22. 1 1
      Jint/Native/String/StringConstructor.cs
  23. 3 3
      Jint/Native/String/StringInstance.cs
  24. 1 1
      Jint/Native/TypedArray/JsTypedArray.cs
  25. 8 11
      Jint/Runtime/Completion.cs
  26. 72 49
      Jint/Runtime/Environments/ExecutionContext.cs
  27. 9 0
      Jint/Runtime/ExecutionContextStack.cs
  28. 2 2
      Jint/Runtime/Interop/ObjectWrapper.cs
  29. 1 1
      Jint/Runtime/Interpreter/Expressions/JintExpression.cs
  30. 3 7
      Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
  31. 28 25
      Jint/Runtime/Interpreter/JintStatementList.cs
  32. 0 2
      Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs
  33. 2 1
      Jint/Runtime/Interpreter/Statements/JintBreakStatement.cs
  34. 2 1
      Jint/Runtime/Interpreter/Statements/JintClassDeclarationStatement.cs
  35. 2 1
      Jint/Runtime/Interpreter/Statements/JintContinueStatement.cs
  36. 2 1
      Jint/Runtime/Interpreter/Statements/JintDebuggerStatement.cs
  37. 1 1
      Jint/Runtime/Interpreter/Statements/JintDoWhileStatement.cs
  38. 2 1
      Jint/Runtime/Interpreter/Statements/JintEmptyStatement.cs
  39. 1 1
      Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs
  40. 2 2
      Jint/Runtime/Interpreter/Statements/JintForStatement.cs
  41. 2 1
      Jint/Runtime/Interpreter/Statements/JintFunctionDeclarationStatement.cs
  42. 1 1
      Jint/Runtime/Interpreter/Statements/JintIfStatement.cs
  43. 2 13
      Jint/Runtime/Interpreter/Statements/JintStatement.cs
  44. 0 2
      Jint/Runtime/Interpreter/Statements/JintTryStatement.cs
  45. 1 1
      Jint/Runtime/Interpreter/Statements/JintWhileStatement.cs
  46. 5 0
      Jint/Runtime/Intrinsics.cs
  47. 5 4
      Jint/Runtime/KnownKeys.cs
  48. 1 1
      Jint/Runtime/Modules/ModuleNamespace.cs
  49. 2 2
      Jint/Runtime/Modules/SourceTextModuleRecord.cs
  50. 3 3
      Jint/Runtime/References/Reference.cs
  51. 9 7
      Jint/Runtime/TypeConverter.cs

+ 8 - 8
Directory.Packages.props

@@ -4,31 +4,31 @@
     <CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
   </PropertyGroup>
   <ItemGroup>
-    <PackageVersion Include="BenchmarkDotNet" Version="0.13.10" />
+    <PackageVersion Include="BenchmarkDotNet" Version="0.13.11" />
     <PackageVersion Include="Esprima" Version="3.0.2" />
     <PackageVersion Include="Flurl.Http.Signed" Version="3.2.4" />
     <PackageVersion Include="Jurassic" Version="3.2.7" />
-    <PackageVersion Include="Meziantou.Analyzer" Version="2.0.116" />
+    <PackageVersion Include="Meziantou.Analyzer" Version="2.0.132" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="8.0.0" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
     <PackageVersion Include="MongoDB.Bson.signed" Version="2.19.0" />
     <PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
-    <PackageVersion Include="NiL.JS" Version="2.5.1674" />
+    <PackageVersion Include="NiL.JS" Version="2.5.1677" />
     <PackageVersion Include="NodaTime" Version="3.1.9" />
-    <PackageVersion Include="NUnit" Version="4.0.0" />
+    <PackageVersion Include="NUnit" Version="4.0.1" />
     <PackageVersion Include="NUnit3TestAdapter" Version="4.5.0" />
     <PackageVersion Include="SharpZipLib" Version="1.4.0" />
     <PackageVersion Include="Spectre.Console.Cli" Version="0.45.0" />
     <PackageVersion Include="System.Text.Json" Version="6.0.8" />
-    <PackageVersion Include="Test262Harness" Version="0.0.22" />
-    <PackageVersion Include="xunit" Version="2.6.2" />
-    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.4" PrivateAssets="all" />
+    <PackageVersion Include="Test262Harness" Version="0.0.23" />
+    <PackageVersion Include="xunit" Version="2.6.4" />
+    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6" PrivateAssets="all" />
     <PackageVersion Include="YantraJS.Core" Version="1.2.206" />
   </ItemGroup>
   <ItemGroup>
     <GlobalPackageReference Include="GitHubActionsTestLogger" Version="2.3.3" />
     <GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
-    <GlobalPackageReference Include="PolySharp" Version="1.14.0" />
+    <GlobalPackageReference Include="PolySharp" Version="1.14.1" />
   </ItemGroup>
 </Project>

+ 1 - 1
Jint.Tests.Test262/.config/dotnet-tools.json

@@ -3,7 +3,7 @@
   "isRoot": true,
   "tools": {
     "test262harness.console": {
-      "version": "0.0.22",
+      "version": "0.0.23",
       "commands": [
         "test262"
       ]

+ 2 - 2
Jint.Tests/Runtime/EngineLimitTests.cs

@@ -9,9 +9,9 @@ public class EngineLimitTests
 {
 
 #if RELEASE
-    const int FunctionNestingCount = 1010;
+    const int FunctionNestingCount = 1000;
 #else
-    const int FunctionNestingCount = 510;
+    const int FunctionNestingCount = 495;
 #endif
 
     [Fact]

+ 14 - 2
Jint/Engine.cs

@@ -5,6 +5,7 @@ using Esprima.Ast;
 using Jint.Native;
 using Jint.Native.Argument;
 using Jint.Native.Function;
+using Jint.Native.Generator;
 using Jint.Native.Object;
 using Jint.Native.Promise;
 using Jint.Native.Symbol;
@@ -553,7 +554,7 @@ namespace Jint
                 ExceptionHelper.ThrowReferenceError(Realm, reference);
             }
 
-            if ((baseValue._type & InternalTypes.ObjectEnvironmentRecord) == InternalTypes.None
+            if ((baseValue._type & InternalTypes.ObjectEnvironmentRecord) == InternalTypes.Empty
                 && _referenceResolver.TryPropertyReference(this, reference, ref baseValue))
             {
                 return baseValue;
@@ -584,7 +585,7 @@ namespace Jint
                     // check if we are accessing a string, boxing operation can be costly to do index access
                     // we have good chance to have fast path with integer or string indexer
                     ObjectInstance? o = null;
-                    if ((property._type & (InternalTypes.String | InternalTypes.Integer)) != InternalTypes.None
+                    if ((property._type & (InternalTypes.String | InternalTypes.Integer)) != InternalTypes.Empty
                         && baseValue is JsString s
                         && TryHandleStringValue(property, s, ref o, out var jsValue))
                     {
@@ -1370,6 +1371,12 @@ namespace Jint
             _executionContexts.ReplaceTopPrivateEnvironment(newEnv);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal ref readonly ExecutionContext UpdateGenerator(GeneratorInstance generator)
+        {
+            return ref _executionContexts.ReplaceTopGenerator(generator);
+        }
+
         /// <summary>
         /// Invokes the named callable and returns the resulting object.
         /// </summary>
@@ -1547,6 +1554,11 @@ namespace Jint
             _typeReferences[reference.ReferenceType] = reference;
         }
 
+        internal ref readonly ExecutionContext GetExecutionContext(int fromTop)
+        {
+            return ref _executionContexts.Peek(fromTop);
+        }
+
         public void Dispose()
         {
             if (_objectWrapperCache is null)

+ 5 - 5
Jint/JsValueExtensions.cs

@@ -19,7 +19,7 @@ namespace Jint
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static bool IsPrimitive(this JsValue value)
         {
-            return (value._type & (InternalTypes.Primitive | InternalTypes.Undefined | InternalTypes.Null)) != InternalTypes.None;
+            return (value._type & (InternalTypes.Primitive | InternalTypes.Undefined | InternalTypes.Null)) != InternalTypes.Empty;
         }
 
         [Pure]
@@ -76,28 +76,28 @@ namespace Jint
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static bool IsObject(this JsValue value)
         {
-            return (value._type & InternalTypes.Object) != InternalTypes.None;
+            return (value._type & InternalTypes.Object) != InternalTypes.Empty;
         }
 
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static bool IsString(this JsValue value)
         {
-            return (value._type & InternalTypes.String) != InternalTypes.None;
+            return (value._type & InternalTypes.String) != InternalTypes.Empty;
         }
 
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static bool IsNumber(this JsValue value)
         {
-            return (value._type & (InternalTypes.Number | InternalTypes.Integer)) != InternalTypes.None;
+            return (value._type & (InternalTypes.Number | InternalTypes.Integer)) != InternalTypes.Empty;
         }
 
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static bool IsBigInt(this JsValue value)
         {
-            return (value._type & InternalTypes.BigInt) != InternalTypes.None;
+            return (value._type & InternalTypes.BigInt) != InternalTypes.Empty;
         }
 
         [Pure]

+ 2 - 2
Jint/Native/Array/ArrayInstance.cs

@@ -314,9 +314,9 @@ namespace Jint.Native.Array
             return base.TryGetProperty(property, out descriptor);
         }
 
-        public sealed override List<JsValue> GetOwnPropertyKeys(Types types = Types.None | Types.String | Types.Symbol)
+        public sealed override List<JsValue> GetOwnPropertyKeys(Types types = Types.Empty | Types.String | Types.Symbol)
         {
-            if ((types & Types.String) == Types.None)
+            if ((types & Types.String) == Types.Empty)
             {
                 return base.GetOwnPropertyKeys(types);
             }

+ 2 - 2
Jint/Native/Array/ArrayOperations.cs

@@ -56,7 +56,7 @@ namespace Jint.Native.Array
             for (uint i = 0; i < (uint) jsValues.Length; i++)
             {
                 var jsValue = skipHoles && !HasProperty(i) ? JsValue.Undefined : Get(i);
-                if ((jsValue.Type & elementTypes) == Types.None)
+                if ((jsValue.Type & elementTypes) == Types.Empty)
                 {
                     ExceptionHelper.ThrowTypeErrorNoEngine("invalid type");
                 }
@@ -251,7 +251,7 @@ namespace Jint.Native.Array
                         value = _target.Prototype?.Get(i) ?? JsValue.Undefined;
                     }
 
-                    if ((value.Type & elementTypes) == Types.None)
+                    if ((value.Type & elementTypes) == Types.Empty)
                     {
                         ExceptionHelper.ThrowTypeErrorNoEngine("invalid type");
                     }

+ 1 - 1
Jint/Native/Date/DatePrototype.cs

@@ -118,7 +118,7 @@ namespace Jint.Native.Date
             }
 
             var hintString = hint.ToString();
-            var tryFirst = Types.None;
+            var tryFirst = Types.Empty;
             if (string.Equals(hintString, "default", StringComparison.Ordinal) || string.Equals(hintString, "string", StringComparison.Ordinal))
             {
                 tryFirst = Types.String;

+ 41 - 29
Jint/Native/Function/ClassDefinition.cs

@@ -89,7 +89,7 @@ internal sealed class ClassDefinition
             }
             else
             {
-                var temp = superclass.Get("prototype");
+                var temp = superclass.Get(CommonProperties.Prototype);
                 if (temp is ObjectInstance protoParentObject)
                 {
                     protoParent = protoParentObject;
@@ -342,25 +342,25 @@ internal sealed class ClassDefinition
     /// <summary>
     /// https://tc39.es/ecma262/#sec-runtime-semantics-methoddefinitionevaluation
     /// </summary>
-    private static PrivateElement? MethodDefinitionEvaluation(
+    internal static PrivateElement? MethodDefinitionEvaluation<T>(
         Engine engine,
         ObjectInstance obj,
-        MethodDefinition method,
-        bool enumerable)
+        T method,
+        bool enumerable) where T : IProperty
     {
-        if (method.Kind != PropertyKind.Get && method.Kind != PropertyKind.Set)
-        {
-            var methodDef = method.DefineMethod(obj);
-            methodDef.Closure.SetFunctionName(methodDef.Key);
-            return DefineMethodProperty(obj, methodDef.Key, methodDef.Closure, enumerable);
-        }
-
         var function = method.Value as IFunction;
         if (function is null)
         {
             ExceptionHelper.ThrowSyntaxError(obj.Engine.Realm);
         }
 
+        if (method.Kind != PropertyKind.Get && method.Kind != PropertyKind.Set && !function.Generator)
+        {
+            var methodDef = method.DefineMethod(obj);
+            methodDef.Closure.SetFunctionName(methodDef.Key);
+            return DefineMethodProperty(obj, methodDef.Key, methodDef.Closure, enumerable);
+        }
+
         var getter = method.Kind == PropertyKind.Get;
 
         var definition = new JintFunctionDefinition(function);
@@ -371,27 +371,39 @@ internal sealed class ClassDefinition
         var env = engine.ExecutionContext.LexicalEnvironment;
         var privateEnv = engine.ExecutionContext.PrivateEnvironment;
 
-        var closure = intrinsics.Function.OrdinaryFunctionCreate(intrinsics.Function.PrototypeObject, definition, definition.ThisMode, env, privateEnv);
-        closure.MakeMethod(obj);
-        closure.SetFunctionName(propKey, getter ? "get" : "set");
-
-        if (method.Key is PrivateIdentifier privateIdentifier)
+        if (function.Generator)
         {
-            return new PrivateElement
-            {
-                Key = privateEnv!.Names[privateIdentifier],
-                Kind = PrivateElementKind.Accessor,
-                Get = getter ? closure : null,
-                Set = !getter ? closure : null
-            };
+            var closure = intrinsics.Function.OrdinaryFunctionCreate(intrinsics.GeneratorFunction.PrototypeObject, definition, definition.ThisMode, env, privateEnv);
+            closure.MakeMethod(obj);
+            closure.SetFunctionName(propKey);
+            var prototype = ObjectInstance.OrdinaryObjectCreate(engine, intrinsics.GeneratorFunction.PrototypeObject.PrototypeObject);
+            closure.DefinePropertyOrThrow(CommonProperties.Prototype, new PropertyDescriptor(prototype, PropertyFlag.Writable));
+            return DefineMethodProperty(obj, propKey, closure, enumerable);
         }
+        else
+        {
+            var closure = intrinsics.Function.OrdinaryFunctionCreate(intrinsics.Function.PrototypeObject, definition, definition.ThisMode, env, privateEnv);
+            closure.MakeMethod(obj);
+            closure.SetFunctionName(propKey, getter ? "get" : "set");
 
-        var propDesc = new GetSetPropertyDescriptor(
-            getter ? closure : null,
-            !getter ? closure : null,
-            PropertyFlag.Configurable);
+            if (method.Key is PrivateIdentifier privateIdentifier)
+            {
+                return new PrivateElement
+                {
+                    Key = privateEnv!.Names[privateIdentifier],
+                    Kind = PrivateElementKind.Accessor,
+                    Get = getter ? closure : null,
+                    Set = !getter ? closure : null
+                };
+            }
 
-        obj.DefinePropertyOrThrow(propKey, propDesc);
+            var propDesc = new GetSetPropertyDescriptor(
+                getter ? closure : null,
+                !getter ? closure : null,
+                PropertyFlag.Configurable);
+
+            obj.DefinePropertyOrThrow(propKey, propDesc);
+        }
 
         return null;
     }
@@ -406,7 +418,7 @@ internal sealed class ClassDefinition
             return new PrivateElement { Key = (PrivateName) key, Kind = PrivateElementKind.Method, Value = closure };
         }
 
-        var desc = new PropertyDescriptor(closure, enumerable ? PropertyFlag.Enumerable : PropertyFlag.NonEnumerable);
+        var desc = new PropertyDescriptor(closure, enumerable ? PropertyFlag.ConfigurableEnumerableWritable : PropertyFlag.NonEnumerable);
         homeObject.DefinePropertyOrThrow(key, desc);
         return null;
     }

+ 7 - 8
Jint/Native/Function/FunctionKind.cs

@@ -1,10 +1,9 @@
-namespace Jint.Native.Function
+namespace Jint.Native.Function;
+
+internal enum FunctionKind
 {
-    internal enum FunctionKind
-    {
-        Normal,
-        Async,
-        Generator,
-        AsyncGenerator
-    }
+    Normal,
+    Async,
+    Generator,
+    AsyncGenerator
 }

+ 46 - 0
Jint/Native/Generator/GeneratorFunctionConstructor.cs

@@ -0,0 +1,46 @@
+using Jint.Native.Function;
+using Jint.Native.Iterator;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.Generator;
+
+/// <summary>
+/// https://tc39.es/ecma262/#sec-generatorfunction-constructor
+/// </summary>
+internal sealed class GeneratorFunctionConstructor : Constructor
+{
+    private static readonly JsString _functionName = new("GeneratorFunction");
+
+    internal GeneratorFunctionConstructor(
+        Engine engine,
+        Realm realm,
+        FunctionPrototype prototype,
+        IteratorPrototype iteratorPrototype)
+        : base(engine, realm, _functionName)
+    {
+        PrototypeObject = new GeneratorFunctionPrototype(engine, this, prototype, iteratorPrototype);
+        _prototype = PrototypeObject;
+        _prototypeDescriptor = new PropertyDescriptor(PrototypeObject, PropertyFlag.AllForbidden);
+        _length = new PropertyDescriptor(JsNumber.PositiveOne, PropertyFlag.Configurable);
+    }
+
+    public GeneratorFunctionPrototype PrototypeObject { get; }
+
+    protected internal override JsValue Call(JsValue thisObject, JsValue[] arguments)
+    {
+        return Construct(arguments, thisObject);
+    }
+
+    public override ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
+    {
+        var function = _realm.Intrinsics.Function.CreateDynamicFunction(
+            this,
+            newTarget,
+            FunctionKind.Generator,
+            arguments);
+
+        return function;
+    }
+}

+ 44 - 0
Jint/Native/Generator/GeneratorFunctionPrototype.cs

@@ -0,0 +1,44 @@
+using Jint.Collections;
+using Jint.Native.Function;
+using Jint.Native.Iterator;
+using Jint.Native.Symbol;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.Generator;
+
+/// <summary>
+/// https://tc39.es/ecma262/#sec-properties-of-the-generatorfunction-prototype-object
+/// </summary>
+internal sealed class GeneratorFunctionPrototype : Prototype
+{
+    private readonly GeneratorFunctionConstructor? _constructor;
+
+    internal GeneratorFunctionPrototype(
+        Engine engine,
+        GeneratorFunctionConstructor constructor,
+        FunctionPrototype prototype,
+        IteratorPrototype iteratorPrototype) : base(engine, engine.Realm)
+    {
+        _constructor = constructor;
+        _prototype = prototype;
+        PrototypeObject = new GeneratorPrototype(engine, this, iteratorPrototype);
+    }
+
+    public GeneratorPrototype PrototypeObject { get; }
+
+    protected override void Initialize()
+    {
+        var properties = new PropertyDictionary(2, checkExistingKeys: false)
+        {
+            [KnownKeys.Constructor] = new PropertyDescriptor(_constructor, PropertyFlag.Configurable),
+            [KnownKeys.Prototype] = new PropertyDescriptor(PrototypeObject, PropertyFlag.Configurable)
+        };
+        SetProperties(properties);
+        var symbols = new SymbolDictionary(1)
+        {
+            [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("GeneratorFunction", PropertyFlag.Configurable)
+        };
+        SetSymbols(symbols);
+    }
+}

+ 155 - 0
Jint/Native/Generator/GeneratorInstance.cs

@@ -0,0 +1,155 @@
+using Esprima;
+using Jint.Native.Iterator;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Environments;
+using Jint.Runtime.Interpreter;
+
+namespace Jint.Native.Generator;
+
+/// <summary>
+/// https://tc39.es/ecma262/#sec-properties-of-generator-instances
+/// </summary>
+internal sealed class GeneratorInstance : ObjectInstance
+{
+    internal GeneratorState _generatorState;
+    private ExecutionContext _generatorContext;
+    private readonly JsValue? _generatorBrand;
+    private JintStatementList _generatorBody = null!;
+
+    public JsValue? _nextValue;
+    public JsValue? _error;
+
+    public GeneratorInstance(Engine engine) : base(engine)
+    {
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-generatorstart
+    /// </summary>
+    public JsValue GeneratorStart(JintStatementList generatorBody)
+    {
+        var genContext = _engine.UpdateGenerator(this);
+        _generatorBody = generatorBody;
+
+        _generatorContext = genContext;
+        _generatorState = GeneratorState.SuspendedStart;
+
+        return Undefined;
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-generatorresume
+    /// </summary>
+    public ObjectInstance GeneratorResume(JsValue? value, JsValue? generatorBrand)
+    {
+        var state = GeneratorValidate(generatorBrand);
+        if (state == GeneratorState.Completed)
+        {
+            return new IteratorResult(_engine, Undefined, JsBoolean.True);
+        }
+
+        var genContext = _generatorContext;
+        var methodContext = _engine.ExecutionContext;
+
+        // 6. Suspend methodContext.
+
+        _nextValue = value;
+
+        var context = _engine._activeEvaluationContext;
+        return ResumeExecution(genContext, context!);
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-generatorresumeabrupt
+    /// </summary>
+    public JsValue GeneratorResumeAbrupt(in Completion abruptCompletion, JsValue? generatorBrand)
+    {
+        var state = GeneratorValidate(generatorBrand);
+        if (state == GeneratorState.SuspendedStart)
+        {
+            _generatorState = GeneratorState.Completed;
+            state = GeneratorState.Completed;
+        }
+
+        if (state == GeneratorState.Completed)
+        {
+            if (abruptCompletion.Type == CompletionType.Return)
+            {
+                return new IteratorResult(_engine, abruptCompletion.Value, JsBoolean.True);
+            }
+
+            ExceptionHelper.ThrowJavaScriptException(_engine, abruptCompletion.Value, new Location());
+        }
+
+        var genContext = _generatorContext;
+        var methodContext = _engine.ExecutionContext;
+
+        // Suspend methodContext
+        _nextValue = abruptCompletion.Type == CompletionType.Return
+            ? abruptCompletion.Value
+            : null;
+
+        _error = abruptCompletion.Type == CompletionType.Throw
+            ? abruptCompletion.Value
+            : null;
+
+        if (_error is not null)
+        {
+            ExceptionHelper.ThrowJavaScriptException(_engine, _error, new Location());
+        }
+
+        return ResumeExecution(genContext, new EvaluationContext(_engine));
+    }
+
+    private ObjectInstance ResumeExecution(ExecutionContext genContext, EvaluationContext context)
+    {
+        _generatorState = GeneratorState.Executing;
+        _engine.EnterExecutionContext(genContext);
+
+        var result = _generatorBody.Execute(context);
+        _engine.LeaveExecutionContext();
+
+        ObjectInstance? resultValue = null;
+        if (result.Type == CompletionType.Normal)
+        {
+            _generatorState = GeneratorState.Completed;
+            resultValue = IteratorResult.CreateValueIteratorPosition(_engine, result.Value, done: JsBoolean.True);
+        }
+        else if (result.Type == CompletionType.Return)
+        {
+            if (_generatorState == GeneratorState.SuspendedYield)
+            {
+                resultValue = IteratorResult.CreateValueIteratorPosition(_engine, result.Value, done: JsBoolean.False);
+            }
+            else
+            {
+                _generatorState = GeneratorState.Completed;
+                resultValue = IteratorResult.CreateValueIteratorPosition(_engine, result.Value, done: JsBoolean.True);
+            }
+        }
+
+        if (result.Type == CompletionType.Throw)
+        {
+            _generatorState = GeneratorState.Completed;
+            ExceptionHelper.ThrowJavaScriptException(_engine, result.Value, result);
+        }
+
+        return resultValue!;
+    }
+
+    private GeneratorState GeneratorValidate(JsValue? generatorBrand)
+    {
+        if (!ReferenceEquals(generatorBrand, _generatorBrand))
+        {
+            ExceptionHelper.ThrowTypeError(_engine.Realm, "Generator brand differs from attached brand");
+        }
+
+        if (_generatorState == GeneratorState.Executing)
+        {
+            ExceptionHelper.ThrowTypeError(_engine.Realm, "Generator state was unexpectedly executing");
+        }
+
+        return _generatorState;
+    }
+}

+ 89 - 0
Jint/Native/Generator/GeneratorPrototype.cs

@@ -0,0 +1,89 @@
+using Jint.Collections;
+using Jint.Native.Iterator;
+using Jint.Native.Object;
+using Jint.Native.Symbol;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.Generator;
+
+/// <summary>
+/// https://tc39.es/ecma262/#sec-generator-objects
+/// </summary>
+internal sealed class GeneratorPrototype : ObjectInstance
+{
+    private readonly GeneratorFunctionPrototype _constructor;
+
+    internal GeneratorPrototype(
+        Engine engine,
+        GeneratorFunctionPrototype constructor,
+        IteratorPrototype iteratorPrototype) : base(engine)
+    {
+        _constructor = constructor;
+        _prototype = iteratorPrototype;
+    }
+
+    protected override void Initialize()
+    {
+        const PropertyFlag PropertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
+        const PropertyFlag LengthFlags = PropertyFlag.Configurable;
+        var properties = new PropertyDictionary(4, false)
+        {
+            ["constructor"] = new(_constructor, PropertyFlag.Configurable),
+            ["next"] = new(new ClrFunctionInstance(Engine, "next", Next, 1, LengthFlags), PropertyFlags),
+            ["return"] = new(new ClrFunctionInstance(Engine, "return", Return, 1, LengthFlags), PropertyFlags),
+            ["throw"] = new(new ClrFunctionInstance(Engine, "throw", Throw, 1, LengthFlags), PropertyFlags)
+        };
+        SetProperties(properties);
+
+        var symbols = new SymbolDictionary(1)
+        {
+            [GlobalSymbolRegistry.ToStringTag] = new("Generator", PropertyFlag.Configurable)
+        };
+        SetSymbols(symbols);
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-generator.prototype.next
+    /// </summary>
+    private ObjectInstance Next(JsValue thisObject, JsValue[] arguments)
+    {
+        var g = AssertGeneratorInstance(thisObject);
+        var value = arguments.At(0, null!);
+        return g.GeneratorResume(value, null);
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-generator.prototype.return
+    /// </summary>
+    private JsValue Return(JsValue thisObject, JsValue[] arguments)
+    {
+        var g = AssertGeneratorInstance(thisObject);
+        var value = arguments.At(0);
+        var C = new Completion(CompletionType.Return, value, null!);
+        return g.GeneratorResumeAbrupt(C, null);
+    }
+
+    /// <summary>
+    /// https://tc39.es/ecma262/#sec-generator.prototype.throw
+    /// </summary>
+    private JsValue Throw(JsValue thisObject, JsValue[] arguments)
+    {
+        var g = AssertGeneratorInstance(thisObject);
+        var exception = arguments.At(0);
+        var C = new Completion(CompletionType.Throw, exception, null!);
+        return g.GeneratorResumeAbrupt(C, null);
+    }
+
+    private GeneratorInstance AssertGeneratorInstance(JsValue thisObj)
+    {
+        var generatorInstance = thisObj as GeneratorInstance;
+        if (generatorInstance is null)
+        {
+            ExceptionHelper.ThrowTypeError(_engine.Realm, "object must be a Generator instance");
+        }
+
+        return generatorInstance;
+    }
+}

+ 10 - 0
Jint/Native/Generator/GeneratorState.cs

@@ -0,0 +1,10 @@
+namespace Jint.Native.Generator;
+
+internal enum GeneratorState
+{
+    Undefined,
+    SuspendedStart,
+    SuspendedYield,
+    Executing,
+    Completed
+}

+ 2 - 2
Jint/Native/Global/GlobalObject.cs

@@ -406,7 +406,7 @@ namespace Jint.Native.Global
 
 uriError:
             _engine.SignalError(ExceptionHelper.CreateUriError(_realm, "URI malformed"));
-            return null!;
+            return JsEmpty.Instance;
         }
 
         public JsValue DecodeUri(JsValue thisObject, JsValue[] arguments)
@@ -535,7 +535,7 @@ uriError:
 
 uriError:
             _engine.SignalError(ExceptionHelper.CreateUriError(_realm, "URI malformed"));
-            return null!;
+            return JsEmpty.Instance;
         }
 
         private static byte StringToIntBase16(ReadOnlySpan<char> s)

+ 16 - 0
Jint/Native/Iterator/IteratorResult.cs

@@ -1,5 +1,6 @@
 using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Descriptors;
 
 namespace Jint.Native.Iterator;
 
@@ -45,5 +46,20 @@ internal sealed class IteratorResult : ObjectInstance
         return base.Get(property, receiver);
     }
 
+    public override PropertyDescriptor GetOwnProperty(JsValue property)
+    {
+        if (CommonProperties.Value.Equals(property))
+        {
+            return new PropertyDescriptor(_value, PropertyFlag.AllForbidden);
+        }
+
+        if (CommonProperties.Done.Equals(property))
+        {
+            return new PropertyDescriptor(_done, PropertyFlag.AllForbidden);
+        }
+
+        return base.GetOwnProperty(property);
+    }
+
     public override object ToObject() => this;
 }

+ 17 - 0
Jint/Native/JsEmpty.cs

@@ -0,0 +1,17 @@
+using Jint.Runtime;
+
+namespace Jint.Native;
+
+/// <summary>
+/// Special null object pattern for spec's EMPTY.
+/// </summary>
+internal sealed class JsEmpty : JsValue
+{
+    internal static readonly JsValue Instance = new JsEmpty();
+
+    private JsEmpty() : base(Types.Empty)
+    {
+    }
+
+    public override object? ToObject() => null;
+}

+ 5 - 3
Jint/Native/JsValue.cs

@@ -40,6 +40,8 @@ namespace Jint.Native
         [DebuggerBrowsable(DebuggerBrowsableState.Never)]
         internal virtual bool IsConstructor => false;
 
+        internal bool IsEmpty => ReferenceEquals(this, JsEmpty.Instance);
+
         [Pure]
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal IteratorInstance GetIterator(Realm realm, GeneratorKind hint = GeneratorKind.Sync, ICallable? method = null)
@@ -345,12 +347,12 @@ namespace Jint.Native
                 return x.IsLooselyEqual(TypeConverter.ToNumber(y));
             }
 
-            if (y.IsObject() && (x._type & InternalTypes.Primitive) != InternalTypes.None)
+            if (y.IsObject() && (x._type & InternalTypes.Primitive) != InternalTypes.Empty)
             {
                 return x.IsLooselyEqual(TypeConverter.ToPrimitive(y));
             }
 
-            if (x.IsObject() && (y._type & InternalTypes.Primitive) != InternalTypes.None)
+            if (x.IsObject() && (y._type & InternalTypes.Primitive) != InternalTypes.Empty)
             {
                 return y.IsLooselyEqual(TypeConverter.ToPrimitive(x));
             }
@@ -377,7 +379,7 @@ namespace Jint.Native
         internal JsValue Clone()
         {
             // concatenated string and arguments currently may require cloning
-            return (_type & InternalTypes.RequiresCloning) == InternalTypes.None
+            return (_type & InternalTypes.RequiresCloning) == InternalTypes.Empty
                 ? this
                 : DoClone();
         }

+ 7 - 7
Jint/Native/Object/ObjectInstance.cs

@@ -232,11 +232,11 @@ namespace Jint.Native.Object
         {
             EnsureInitialized();
 
-            var returningSymbols = (types & Types.Symbol) != Types.None && _symbols?.Count > 0;
-            var returningStringKeys = (types & Types.String) != Types.None && _properties?.Count > 0;
+            var returningSymbols = (types & Types.Symbol) != Types.Empty && _symbols?.Count > 0;
+            var returningStringKeys = (types & Types.String) != Types.Empty && _properties?.Count > 0;
 
             var propertyKeys = new List<JsValue>();
-            if ((types & Types.String) != Types.None)
+            if ((types & Types.String) != Types.Empty)
             {
                 var initialOwnStringPropertyKeys = GetInitialOwnStringPropertyKeys();
                 if (!ReferenceEquals(initialOwnStringPropertyKeys, System.Linq.Enumerable.Empty<JsValue>()))
@@ -272,7 +272,7 @@ namespace Jint.Native.Object
                 return propertyKeys;
             }
 
-            if ((types & Types.String) == Types.None && (types & Types.Symbol) != Types.None)
+            if ((types & Types.String) == Types.Empty && (types & Types.Symbol) != Types.Empty)
             {
                 // only symbols requested
                 if (_symbols != null)
@@ -365,7 +365,7 @@ namespace Jint.Native.Object
 
         public override JsValue Get(JsValue property, JsValue receiver)
         {
-            if ((_type & InternalTypes.PlainObject) != InternalTypes.None && ReferenceEquals(this, receiver) && property is JsString jsString)
+            if ((_type & InternalTypes.PlainObject) != InternalTypes.Empty && ReferenceEquals(this, receiver) && property is JsString jsString)
             {
                 EnsureInitialized();
                 if (_properties?.TryGetValue(jsString.ToString(), out var ownDesc) == true)
@@ -521,7 +521,7 @@ namespace Jint.Native.Object
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool Set(JsValue property, JsValue value)
         {
-            if ((_type & InternalTypes.PlainObject) != InternalTypes.None && property is JsString jsString)
+            if ((_type & InternalTypes.PlainObject) != InternalTypes.Empty && property is JsString jsString)
             {
                 var key = (Key) jsString.ToString();
                 if (_properties?.TryGetValue(key, out var ownDesc) == true)
@@ -544,7 +544,7 @@ namespace Jint.Native.Object
         /// </summary>
         public override bool Set(JsValue property, JsValue value, JsValue receiver)
         {
-            if ((_type & InternalTypes.PlainObject) != InternalTypes.None && ReferenceEquals(this, receiver) && property is JsString jsString)
+            if ((_type & InternalTypes.PlainObject) != InternalTypes.Empty && ReferenceEquals(this, receiver) && property is JsString jsString)
             {
                 var key = (Key) jsString.ToString();
                 if (_properties?.TryGetValue(key, out var ownDesc) == true)

+ 1 - 1
Jint/Native/Proxy/JsProxy.cs

@@ -160,7 +160,7 @@ namespace Jint.Native.Proxy
         /// <summary>
         /// https://tc39.es/ecma262/#sec-proxy-object-internal-methods-and-internal-slots-ownpropertykeys
         /// </summary>
-        public override List<JsValue> GetOwnPropertyKeys(Types types = Types.None | Types.String | Types.Symbol)
+        public override List<JsValue> GetOwnPropertyKeys(Types types = Types.Empty | Types.String | Types.Symbol)
         {
             if (!TryCallHandler(TrapOwnKeys, new[] { _target }, out var result))
             {

+ 1 - 1
Jint/Native/String/StringConstructor.cs

@@ -125,7 +125,7 @@ namespace Jint.Native.String
 
             rangeError:
             _engine.SignalError(ExceptionHelper.CreateRangeError(_realm, "Invalid code point " + codePoint));
-            return null!;
+            return JsEmpty.Instance;
         }
 
         /// <summary>

+ 3 - 3
Jint/Native/String/StringInstance.cs

@@ -51,7 +51,7 @@ internal class StringInstance : ObjectInstance, IPrimitiveInstance
             return desc;
         }
 
-        if ((property._type & (InternalTypes.Number | InternalTypes.Integer | InternalTypes.String)) == InternalTypes.None)
+        if ((property._type & (InternalTypes.Number | InternalTypes.Integer | InternalTypes.String)) == InternalTypes.Empty)
         {
             return PropertyDescriptor.Undefined;
         }
@@ -87,7 +87,7 @@ internal class StringInstance : ObjectInstance, IPrimitiveInstance
     public sealed override List<JsValue> GetOwnPropertyKeys(Types types = Types.String | Types.Symbol)
     {
         var keys = new List<JsValue>(StringData.Length + 1);
-        if ((types & Types.String) != Types.None)
+        if ((types & Types.String) != Types.Empty)
         {
             for (uint i = 0; i < StringData.Length; ++i)
             {
@@ -97,7 +97,7 @@ internal class StringInstance : ObjectInstance, IPrimitiveInstance
             keys.AddRange(base.GetOwnPropertyKeys(Types.String));
         }
 
-        if ((types & Types.Symbol) != Types.None)
+        if ((types & Types.Symbol) != Types.Empty)
         {
             keys.AddRange(base.GetOwnPropertyKeys(Types.Symbol));
         }

+ 1 - 1
Jint/Native/TypedArray/JsTypedArray.cs

@@ -185,7 +185,7 @@ namespace Jint.Native.TypedArray
         /// <summary>
         /// https://tc39.es/ecma262/#sec-integer-indexed-exotic-objects-ownpropertykeys
         /// </summary>
-        public override List<JsValue> GetOwnPropertyKeys(Types types = Types.None | Types.String | Types.Symbol)
+        public override List<JsValue> GetOwnPropertyKeys(Types types = Types.Empty | Types.String | Types.Symbol)
         {
             var keys = new List<JsValue>();
             if (!_viewedArrayBuffer.IsDetachedBuffer)

+ 8 - 11
Jint/Runtime/Completion.cs

@@ -1,3 +1,4 @@
+using System.Diagnostics;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Esprima;
@@ -22,14 +23,16 @@ public enum CompletionType : byte
 public readonly struct Completion
 {
     private static readonly Node _emptyNode = new Identifier("");
-    private static readonly Completion _emptyCompletion = new(CompletionType.Normal, null!, _emptyNode);
+    private static readonly Completion _emptyCompletion = new(CompletionType.Normal, JsEmpty.Instance, _emptyNode);
 
     internal readonly SyntaxElement _source;
 
     public Completion(CompletionType type, JsValue value, SyntaxElement source)
     {
+        Debug.Assert(value is not null);
+
         Type = type;
-        Value = value;
+        Value = value!;
         _source = source;
     }
 
@@ -40,23 +43,17 @@ public readonly struct Completion
     public static ref readonly Completion Empty() => ref _emptyCompletion;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public JsValue GetValueOrDefault()
-    {
-        return Value ?? JsValue.Undefined;
-    }
+    public JsValue GetValueOrDefault() => Value.IsEmpty ? JsValue.Undefined : Value;
 
     [MethodImpl(MethodImplOptions.AggressiveInlining)]
-    public bool IsAbrupt()
-    {
-        return Type != CompletionType.Normal;
-    }
+    public bool IsAbrupt() => Type != CompletionType.Normal;
 
     /// <summary>
     /// https://tc39.es/ecma262/#sec-updateempty
     /// </summary>
     internal Completion UpdateEmpty(JsValue value)
     {
-        if (Value is not null)
+        if (Value?._type != InternalTypes.Empty)
         {
             return this;
         }

+ 72 - 49
Jint/Runtime/Environments/ExecutionContext.cs

@@ -1,69 +1,92 @@
-using System.Runtime.InteropServices;
 using Jint.Native.Function;
 
-namespace Jint.Runtime.Environments;
+using Jint.Native.Generator;
 
-[StructLayout(LayoutKind.Auto)]
-internal readonly struct ExecutionContext
+namespace Jint.Runtime.Environments
 {
-    internal ExecutionContext(
-        IScriptOrModule? scriptOrModule,
-        EnvironmentRecord lexicalEnvironment,
-        EnvironmentRecord variableEnvironment,
-        PrivateEnvironmentRecord? privateEnvironment,
-        Realm realm,
-        FunctionInstance? function = null)
+    internal readonly struct ExecutionContext
     {
-        ScriptOrModule = scriptOrModule;
-        LexicalEnvironment = lexicalEnvironment;
-        VariableEnvironment = variableEnvironment;
-        PrivateEnvironment = privateEnvironment;
-        Realm = realm;
-        Function = function;
-    }
+        internal ExecutionContext(
+            IScriptOrModule? scriptOrModule,
+            EnvironmentRecord lexicalEnvironment,
+            EnvironmentRecord variableEnvironment,
+            PrivateEnvironmentRecord? privateEnvironment,
+            Realm realm,
+            GeneratorInstance? generator = null,
+            FunctionInstance? function = null)
+        {
+            ScriptOrModule = scriptOrModule;
+            LexicalEnvironment = lexicalEnvironment;
+            VariableEnvironment = variableEnvironment;
+            PrivateEnvironment = privateEnvironment;
+            Realm = realm;
+            Function = function;
+            Generator = generator;
+        }
 
-    public readonly IScriptOrModule? ScriptOrModule;
-    public readonly EnvironmentRecord LexicalEnvironment;
-    public readonly EnvironmentRecord VariableEnvironment;
-    public readonly PrivateEnvironmentRecord? PrivateEnvironment;
-    public readonly Realm Realm;
-    public readonly FunctionInstance? Function;
+        public readonly IScriptOrModule? ScriptOrModule;
+        public readonly EnvironmentRecord LexicalEnvironment;
+        public readonly EnvironmentRecord VariableEnvironment;
+        public readonly PrivateEnvironmentRecord? PrivateEnvironment;
+        public readonly Realm Realm;
+        public readonly FunctionInstance? Function;
+        public readonly GeneratorInstance? Generator;
 
-    public ExecutionContext UpdateLexicalEnvironment(EnvironmentRecord lexicalEnvironment)
-    {
-        return new ExecutionContext(ScriptOrModule, lexicalEnvironment, VariableEnvironment, PrivateEnvironment, Realm, Function);
-    }
+        public bool Suspended => Generator?._generatorState == GeneratorState.SuspendedYield;
 
-    public ExecutionContext UpdateVariableEnvironment(EnvironmentRecord variableEnvironment)
-    {
-        return new ExecutionContext(ScriptOrModule, LexicalEnvironment, variableEnvironment, PrivateEnvironment, Realm, Function);
-    }
+        public ExecutionContext UpdateLexicalEnvironment(EnvironmentRecord lexicalEnvironment)
+        {
+            return new ExecutionContext(ScriptOrModule, lexicalEnvironment, VariableEnvironment, PrivateEnvironment, Realm, Generator, Function);
+        }
 
-    public ExecutionContext UpdatePrivateEnvironment(PrivateEnvironmentRecord? privateEnvironment)
-    {
-        return new ExecutionContext(ScriptOrModule, LexicalEnvironment, VariableEnvironment, privateEnvironment, Realm, Function);
-    }
+        public ExecutionContext UpdateVariableEnvironment(EnvironmentRecord variableEnvironment)
+        {
+            return new ExecutionContext(ScriptOrModule, LexicalEnvironment, variableEnvironment, PrivateEnvironment, Realm, Generator, Function);
+        }
 
-    /// <summary>
-    /// https://tc39.es/ecma262/#sec-getthisenvironment
-    /// </summary>
-    internal EnvironmentRecord GetThisEnvironment()
-    {
-        // The loop will always terminate because the list of environments always
-        // ends with the global environment which has a this binding.
-        var lex = LexicalEnvironment;
-        while (true)
+        public ExecutionContext UpdatePrivateEnvironment(PrivateEnvironmentRecord? privateEnvironment)
         {
-            if (lex is not null)
+            return new ExecutionContext(ScriptOrModule, LexicalEnvironment, VariableEnvironment, privateEnvironment, Realm, Generator, Function);
+        }
+
+        public ExecutionContext UpdateGenerator(GeneratorInstance generator)
+        {
+            return new ExecutionContext(ScriptOrModule, LexicalEnvironment, VariableEnvironment, PrivateEnvironment, Realm, generator, Function);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-getthisenvironment
+        /// </summary>
+        internal EnvironmentRecord GetThisEnvironment()
+        {
+            // The loop will always terminate because the list of environments always
+            // ends with the global environment which has a this binding.
+            var lex = LexicalEnvironment;
+            while (true)
             {
-                if (lex.HasThisBinding())
+                if (lex is not null)
                 {
-                    return lex;
+                    if (lex.HasThisBinding())
+                    {
+                        return lex;
+
+                    }
 
+                    lex = lex._outerEnv;
                 }
+            }
+        }
 
-                lex = lex._outerEnv;
+        internal GeneratorKind GetGeneratorKind()
+        {
+            if (Generator is null)
+            {
+                return GeneratorKind.NonGenerator;
             }
+
+            // TODO If generator has an [[AsyncGeneratorState]] internal slot, return async.
+
+            return GeneratorKind.Sync;
         }
     }
 }

+ 9 - 0
Jint/Runtime/ExecutionContextStack.cs

@@ -1,5 +1,6 @@
 using System.Runtime.CompilerServices;
 using Jint.Collections;
+using Jint.Native.Generator;
 using Jint.Runtime.Environments;
 
 namespace Jint.Runtime
@@ -34,6 +35,14 @@ namespace Jint.Runtime
             array[size - 1] = array[size - 1].UpdatePrivateEnvironment(newEnv);
         }
 
+        public ref readonly ExecutionContext ReplaceTopGenerator(GeneratorInstance newEnv)
+        {
+            var array = _stack._array;
+            var size = _stack._size;
+            array[size - 1] = array[size - 1].UpdateGenerator(newEnv);
+            return ref array[size - 1];
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public ref readonly ExecutionContext Peek() => ref _stack.Peek();
 

+ 2 - 2
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -125,7 +125,7 @@ namespace Jint.Runtime.Interop
             return Prototype?.Get(property, receiver) ?? Undefined;
         }
 
-        public override List<JsValue> GetOwnPropertyKeys(Types types = Types.None | Types.String | Types.Symbol)
+        public override List<JsValue> GetOwnPropertyKeys(Types types = Types.Empty | Types.String | Types.Symbol)
         {
             return new List<JsValue>(EnumerateOwnPropertyKeys(types));
         }
@@ -141,7 +141,7 @@ namespace Jint.Runtime.Interop
         private IEnumerable<JsValue> EnumerateOwnPropertyKeys(Types types)
         {
             // prefer object order, add possible other properties after
-            var includeStrings = (types & Types.String) != Types.None;
+            var includeStrings = (types & Types.String) != Types.Empty;
             if (includeStrings && _typeDescriptor.IsStringKeyedGenericDictionary) // expando object for instance
             {
                 var keys = _typeDescriptor.GetKeys(Target);

+ 1 - 1
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -31,7 +31,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 return (JsValue) result;
             }
 
-            return context.Engine.GetValue(reference, true);
+            return context.Engine.GetValue(reference, returnReferenceToPool: true);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining | (MethodImplOptions) 512)]

+ 3 - 7
Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs

@@ -177,10 +177,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                 if (property.Method)
                 {
-                    var methodDef = property.DefineMethod(obj);
-                    methodDef.Closure.SetFunctionName(methodDef.Key);
-                    var desc = new PropertyDescriptor(methodDef.Closure, PropertyFlag.ConfigurableEnumerableWritable);
-                    obj.DefinePropertyOrThrow(methodDef.Key, desc);
+                    ClassDefinition.MethodDefinitionEvaluation(engine, obj, property, enumerable: true);
                     continue;
                 }
 
@@ -221,10 +218,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                         closure.SetFunctionName(propName);
                     }
 
-                    var propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
-                    obj.DefinePropertyOrThrow(propName, propDesc);
+                    obj.CreateDataPropertyOrThrow(propName, propValue);
                 }
-                else if (property.Kind == PropertyKind.Get || property.Kind == PropertyKind.Set)
+                else if (property.Kind is PropertyKind.Get or PropertyKind.Set)
                 {
                     var function = objectProperty.GetFunctionDefinition(engine);
                     var closure = engine.Realm.Intrinsics.Function.OrdinaryFunctionCreate(

+ 28 - 25
Jint/Runtime/Interpreter/JintStatementList.cs

@@ -9,11 +9,7 @@ namespace Jint.Runtime.Interpreter
 {
     internal sealed class JintStatementList
     {
-        private sealed class Pair
-        {
-            internal JintStatement Statement = null!;
-            internal Completion? Value;
-        }
+        private readonly record struct Pair(JintStatement Statement, JsValue? Value);
 
         private readonly Statement? _statement;
         private readonly NodeList<Statement> _statements;
@@ -54,11 +50,7 @@ namespace Jint.Runtime.Interpreter
                 var statement = JintStatement.Build(esprimaStatement);
                 // When in debug mode, don't do FastResolve: Stepping requires each statement to be actually executed.
                 var value = context.DebugMode ? null : JintStatement.FastResolve(esprimaStatement);
-                jintStatements[i] = new Pair
-                {
-                    Statement = statement,
-                    Value = value
-                };
+                jintStatements[i] = new Pair(statement, value);
             }
 
             _jintStatements = jintStatements;
@@ -80,33 +72,40 @@ namespace Jint.Runtime.Interpreter
                 context.RunBeforeExecuteStatementChecks(_statement);
             }
 
-            JintStatement? s = null;
-            Completion c = default;
+            Completion c = Completion.Empty();
             Completion sl = c;
 
             // The value of a StatementList is the value of the last value-producing item in the StatementList
-            JsValue? lastValue = null;
+            var lastValue = JsEmpty.Instance;
+            var i = _index;
+            var temp = _jintStatements!;
             try
             {
-                foreach (var pair in _jintStatements!)
+                for (i = 0; i < (uint) temp.Length; i++)
                 {
-                    s = pair.Statement;
-                    c = pair.Value.GetValueOrDefault();
-                    if (c.Value is null)
+                    ref readonly var pair = ref temp[i];
+
+                    if (pair.Value is null)
                     {
-                        c = s.Execute(context);
+                        c = pair.Statement.Execute(context);
                         if (context.Engine._error is not null)
                         {
-                            return HandleError(context.Engine, s);
+                            c = HandleError(context.Engine, pair.Statement);
+                            break;
                         }
                     }
+                    else
+                    {
+                        c = new Completion(CompletionType.Return, pair.Value, pair.Statement._statement);
+                    }
 
                     if (c.Type != CompletionType.Normal)
                     {
-                        return new Completion(c.Type, c.Value ?? sl.Value!, c._source);
+                        return c.UpdateEmpty(sl.Value);
                     }
+
                     sl = c;
-                    if (c.Value is not null)
+                    if (!c.Value.IsEmpty)
                     {
                         lastValue = c.Value;
                     }
@@ -116,13 +115,15 @@ namespace Jint.Runtime.Interpreter
             {
                 if (ex is JintException)
                 {
-                    return HandleException(context, ex, s);
+                    c = HandleException(context, ex, temp[i].Statement);
+                }
+                else
+                {
+                    throw;
                 }
-
-                throw;
             }
 
-            return new Completion(c.Type, lastValue ?? JsValue.Undefined, c._source!);
+            return c.UpdateEmpty(lastValue).UpdateEmpty(JsValue.Undefined);
         }
 
         private static Completion HandleException(EvaluationContext context, Exception exception, JintStatement? s)
@@ -131,11 +132,13 @@ namespace Jint.Runtime.Interpreter
             {
                 return CreateThrowCompletion(s, javaScriptException);
             }
+
             if (exception is TypeErrorException typeErrorException)
             {
                 var node = typeErrorException.Node ?? s!._statement;
                 return CreateThrowCompletion(context.Engine.Realm.Intrinsics.TypeError, typeErrorException, node);
             }
+
             if (exception is RangeErrorException rangeErrorException)
             {
                 return CreateThrowCompletion(context.Engine.Realm.Intrinsics.RangeError, rangeErrorException, s!._statement);

+ 0 - 2
Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs

@@ -18,8 +18,6 @@ namespace Jint.Runtime.Interpreter.Statements
             _lexicalDeclarations = HoistingScope.GetLexicalDeclarations(_statement);
         }
 
-        internal override bool SupportsResume => true;
-
         /// <summary>
         /// Optimized for direct access without virtual dispatch.
         /// </summary>

+ 2 - 1
Jint/Runtime/Interpreter/Statements/JintBreakStatement.cs

@@ -1,4 +1,5 @@
 using Esprima.Ast;
+using Jint.Native;
 
 namespace Jint.Runtime.Interpreter.Statements;
 
@@ -14,6 +15,6 @@ internal sealed class JintBreakStatement : JintStatement<BreakStatement>
     protected override Completion ExecuteInternal(EvaluationContext context)
     {
         context.Target = _statement.Label?.Name;
-        return new Completion(CompletionType.Break, null!, _statement);
+        return new Completion(CompletionType.Break, JsEmpty.Instance, _statement);
     }
 }

+ 2 - 1
Jint/Runtime/Interpreter/Statements/JintClassDeclarationStatement.cs

@@ -1,4 +1,5 @@
 using Esprima.Ast;
+using Jint.Native;
 using Jint.Native.Function;
 
 namespace Jint.Runtime.Interpreter.Statements
@@ -29,7 +30,7 @@ namespace Jint.Runtime.Interpreter.Statements
                 env.InitializeBinding(classBinding, value);
             }
 
-            return new Completion(CompletionType.Normal, null!, _statement);
+            return new Completion(CompletionType.Normal, JsEmpty.Instance, _statement);
         }
     }
 }

+ 2 - 1
Jint/Runtime/Interpreter/Statements/JintContinueStatement.cs

@@ -1,4 +1,5 @@
 using Esprima.Ast;
+using Jint.Native;
 
 namespace Jint.Runtime.Interpreter.Statements;
 
@@ -14,6 +15,6 @@ internal sealed class JintContinueStatement : JintStatement<ContinueStatement>
     protected override Completion ExecuteInternal(EvaluationContext context)
     {
         context.Target = _statement.Label?.Name;
-        return new Completion(CompletionType.Continue, null!, _statement);
+        return new Completion(CompletionType.Continue, JsEmpty.Instance, _statement);
     }
 }

+ 2 - 1
Jint/Runtime/Interpreter/Statements/JintDebuggerStatement.cs

@@ -1,4 +1,5 @@
 using Esprima.Ast;
+using Jint.Native;
 using Jint.Runtime.Debugger;
 
 namespace Jint.Runtime.Interpreter.Statements
@@ -28,7 +29,7 @@ namespace Jint.Runtime.Interpreter.Statements
                     break;
             }
 
-            return new Completion(CompletionType.Normal, null!, _statement);
+            return new Completion(CompletionType.Normal, JsEmpty.Instance, _statement);
         }
     }
 }

+ 1 - 1
Jint/Runtime/Interpreter/Statements/JintDoWhileStatement.cs

@@ -32,7 +32,7 @@ internal sealed class JintDoWhileStatement : JintStatement<DoWhileStatement>
         do
         {
             var completion = _body.Execute(context);
-            if (!ReferenceEquals(completion.Value, null))
+            if (!completion.Value.IsEmpty)
             {
                 v = completion.Value;
             }

+ 2 - 1
Jint/Runtime/Interpreter/Statements/JintEmptyStatement.cs

@@ -1,4 +1,5 @@
 using Esprima.Ast;
+using Jint.Native;
 
 namespace Jint.Runtime.Interpreter.Statements;
 
@@ -10,6 +11,6 @@ internal sealed class JintEmptyStatement : JintStatement<EmptyStatement>
 
     protected override Completion ExecuteInternal(EvaluationContext context)
     {
-        return new Completion(CompletionType.Normal, null!, _statement);
+        return new Completion(CompletionType.Normal, JsEmpty.Instance, _statement);
     }
 }

+ 1 - 1
Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs

@@ -279,7 +279,7 @@ namespace Jint.Runtime.Interpreter.Statements
                     var result = stmt.Execute(context);
                     engine.UpdateLexicalEnvironment(oldEnv);
 
-                    if (!ReferenceEquals(result.Value, null))
+                    if (!result.Value.IsEmpty)
                     {
                         v = result.Value;
                     }

+ 2 - 2
Jint/Runtime/Interpreter/Statements/JintForStatement.cs

@@ -136,14 +136,14 @@ namespace Jint.Runtime.Interpreter.Statements
                 }
 
                 var result = _body.Execute(context);
-                if (!ReferenceEquals(result.Value, null))
+                if (!result.Value.IsEmpty)
                 {
                     v = result.Value;
                 }
 
                 if (result.Type == CompletionType.Break && (context.Target == null || string.Equals(context.Target, _statement?.LabelSet?.Name, StringComparison.Ordinal)))
                 {
-                    return new Completion(CompletionType.Normal, result.Value!, ((JintStatement) this)._statement);
+                    return new Completion(CompletionType.Normal, result.Value, ((JintStatement) this)._statement);
                 }
 
                 if (result.Type != CompletionType.Continue || (context.Target != null && !string.Equals(context.Target, _statement?.LabelSet?.Name, StringComparison.Ordinal)))

+ 2 - 1
Jint/Runtime/Interpreter/Statements/JintFunctionDeclarationStatement.cs

@@ -1,4 +1,5 @@
 using Esprima.Ast;
+using Jint.Native;
 
 namespace Jint.Runtime.Interpreter.Statements;
 
@@ -10,6 +11,6 @@ internal sealed class JintFunctionDeclarationStatement : JintStatement<FunctionD
 
     protected override Completion ExecuteInternal(EvaluationContext context)
     {
-        return new Completion(CompletionType.Normal, null!, ((JintStatement) this)._statement);
+        return new Completion(CompletionType.Normal, JsEmpty.Instance, ((JintStatement) this)._statement);
     }
 }

+ 1 - 1
Jint/Runtime/Interpreter/Statements/JintIfStatement.cs

@@ -23,7 +23,7 @@ internal sealed class JintIfStatement : JintStatement<IfStatement>
 
     protected override Completion ExecuteInternal(EvaluationContext context)
     {
-        Completion result = default;
+        var result = Completion.Empty();
         if (TypeConverter.ToBoolean(_test.GetValue(context)))
         {
             result = _statementConsequent.Execute(context);

+ 2 - 13
Jint/Runtime/Interpreter/Statements/JintStatement.cs

@@ -41,16 +41,9 @@ namespace Jint.Runtime.Interpreter.Statements
                 _initialized = true;
             }
 
-            if (context.ResumedCompletion.IsAbrupt() && !SupportsResume)
-            {
-                return new Completion(CompletionType.Normal, JsValue.Undefined, _statement);
-            }
-
             return ExecuteInternal(context);
         }
 
-        internal virtual bool SupportsResume => false;
-
         protected abstract Completion ExecuteInternal(EvaluationContext context);
 
         public ref readonly Location Location => ref _statement.Location;
@@ -103,15 +96,11 @@ namespace Jint.Runtime.Interpreter.Statements
             return result;
         }
 
-        internal static Completion? FastResolve(StatementListItem statement)
+        internal static JsValue? FastResolve(StatementListItem statement)
         {
             if (statement is ReturnStatement rs && rs.Argument is Literal l)
             {
-                var jsValue = JintLiteralExpression.ConvertToJsValue(l);
-                if (jsValue is not null)
-                {
-                    return new Completion(CompletionType.Return, jsValue, rs);
-                }
+                return JintLiteralExpression.ConvertToJsValue(l);
             }
 
             return null;

+ 0 - 2
Jint/Runtime/Interpreter/Statements/JintTryStatement.cs

@@ -27,8 +27,6 @@ namespace Jint.Runtime.Interpreter.Statements
             }
         }
 
-        internal override bool SupportsResume => true;
-
         protected override Completion ExecuteInternal(EvaluationContext context)
         {
             var engine = context.Engine;

+ 1 - 1
Jint/Runtime/Interpreter/Statements/JintWhileStatement.cs

@@ -42,7 +42,7 @@ namespace Jint.Runtime.Interpreter.Statements
 
                 var completion = _body.Execute(context);
 
-                if (!ReferenceEquals(completion.Value, null))
+                if (!completion.Value.IsEmpty)
                 {
                     v = completion.Value;
                 }

+ 5 - 0
Jint/Runtime/Intrinsics.cs

@@ -10,6 +10,7 @@ using Jint.Native.Date;
 using Jint.Native.Error;
 using Jint.Native.FinalizationRegistry;
 using Jint.Native.Function;
+using Jint.Native.Generator;
 using Jint.Native.Iterator;
 using Jint.Native.Json;
 using Jint.Native.Map;
@@ -66,6 +67,7 @@ namespace Jint.Runtime
         private MathInstance? _math;
         private JsonInstance? _json;
         private SymbolConstructor? _symbol;
+        private GeneratorFunctionConstructor? _generatorFunction;
         private RegExpConstructor? _regExp;
         private RegExpStringIteratorPrototype? _regExpStringIteratorPrototype;
         private NumberConstructor? _number;
@@ -244,6 +246,9 @@ namespace Jint.Runtime
         public ShadowRealmConstructor ShadowRealm =>
             _shadowRealm ??= new ShadowRealmConstructor(_engine, _realm, Function.PrototypeObject, Object.PrototypeObject);
 
+        internal GeneratorFunctionConstructor GeneratorFunction =>
+            _generatorFunction ??= new GeneratorFunctionConstructor(_engine, _realm, Function.PrototypeObject, IteratorPrototype);
+
         internal EvalFunctionInstance Eval =>
             _eval ??= new EvalFunctionInstance(_engine, _realm, Function.PrototypeObject);
 

+ 5 - 4
Jint/Runtime/KnownKeys.cs

@@ -4,11 +4,12 @@ internal static class KnownKeys
 {
     internal static readonly Key Arguments = "arguments";
     internal static readonly Key Caller = "caller";
+    internal static readonly Key Constructor = "constructor";
+    internal static readonly Key Done = "done";
     internal static readonly Key Eval = "eval";
     internal static readonly Key Length = "length";
-    internal static readonly Key Done = "done";
-    internal static readonly Key Value = "value";
-    internal static readonly Key Undefined = "undefined";
-    internal static readonly Key Constructor = "constructor";
     internal static readonly Key Next = "next";
+    internal static readonly Key Prototype = "prototype";
+    internal static readonly Key Undefined = "undefined";
+    internal static readonly Key Value = "value";
 }

+ 1 - 1
Jint/Runtime/Modules/ModuleNamespace.cs

@@ -204,7 +204,7 @@ internal sealed class ModuleNamespace : ObjectInstance
     public override List<JsValue> GetOwnPropertyKeys(Types types = Types.String | Types.Symbol)
     {
         var result = new List<JsValue>();
-        if ((types & Types.String) != Types.None)
+        if ((types & Types.String) != Types.Empty)
         {
             result.Capacity = _exports.Count;
             foreach (var export in _exports)

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

@@ -336,7 +336,7 @@ internal class SourceTextModuleRecord : CyclicModuleRecord
     /// </summary>
     internal override Completion ExecuteModule(PromiseCapability capability = null)
     {
-        var moduleContext = new ExecutionContext(this, _environment, _environment, null, _realm);
+        var moduleContext = new ExecutionContext(this, _environment, _environment, privateEnvironment: null, _realm);
         if (!_hasTLA)
         {
             using (new StrictModeScope(true, force: true))
@@ -344,7 +344,7 @@ internal class SourceTextModuleRecord : CyclicModuleRecord
                 _engine.EnterExecutionContext(moduleContext);
                 try
                 {
-                    var statementList = new JintStatementList(null, _source.Body);
+                    var statementList = new JintStatementList(statement: 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;

+ 3 - 3
Jint/Runtime/References/Reference.cs

@@ -52,7 +52,7 @@ public sealed class Reference
     public bool HasPrimitiveBase
     {
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        get => (_base._type & InternalTypes.Primitive) != InternalTypes.None;
+        get => (_base._type & InternalTypes.Primitive) != InternalTypes.Empty;
     }
 
     public bool IsUnresolvableReference
@@ -68,7 +68,7 @@ public sealed class Reference
     public bool IsPropertyReference
     {
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        get => (_base._type & (InternalTypes.Primitive | InternalTypes.Object)) != InternalTypes.None;
+        get => (_base._type & (InternalTypes.Primitive | InternalTypes.Object)) != InternalTypes.Empty;
     }
 
     public JsValue ThisValue
@@ -96,7 +96,7 @@ public sealed class Reference
     internal void AssertValid(Realm realm)
     {
         if (_strict
-            && (_base._type & InternalTypes.ObjectEnvironmentRecord) != InternalTypes.None
+            && (_base._type & InternalTypes.ObjectEnvironmentRecord) != InternalTypes.Empty
             && (CommonProperties.Eval.Equals(_referencedName) || CommonProperties.Arguments.Equals(_referencedName)))
         {
             ExceptionHelper.ThrowSyntaxError(realm);

+ 9 - 7
Jint/Runtime/TypeConverter.cs

@@ -17,7 +17,7 @@ namespace Jint.Runtime
     [Flags]
     public enum Types
     {
-        None = 0,
+        Empty = 0,
         Undefined = 1,
         Null = 2,
         Boolean = 4,
@@ -32,7 +32,7 @@ namespace Jint.Runtime
     internal enum InternalTypes
     {
         // should not be used, used for empty match
-        None = 0,
+        Empty = 0,
 
         Undefined = 1,
         Null = 2,
@@ -90,7 +90,7 @@ namespace Jint.Runtime
         /// https://tc39.es/ecma262/#sec-toprimitive
         /// </summary>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.None)
+        public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.Empty)
         {
             return input is not ObjectInstance oi
                 ? input
@@ -121,13 +121,13 @@ namespace Jint.Runtime
                 }
             }
 
-            return OrdinaryToPrimitive(oi, preferredType == Types.None ? Types.Number : preferredType);
+            return OrdinaryToPrimitive(oi, preferredType == Types.Empty ? Types.Number : preferredType);
         }
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-ordinarytoprimitive
         /// </summary>
-        internal static JsValue OrdinaryToPrimitive(ObjectInstance input, Types hint = Types.None)
+        internal static JsValue OrdinaryToPrimitive(ObjectInstance input, Types hint = Types.Empty)
         {
             JsString property1;
             JsString property2;
@@ -221,6 +221,7 @@ namespace Jint.Runtime
                     return ToNumber(o.ToString());
                 case InternalTypes.Symbol:
                 case InternalTypes.BigInt:
+                case InternalTypes.Empty:
                     // TODO proper TypeError would require Engine instance and a lot of API changes
                     ExceptionHelper.ThrowTypeErrorNoEngine("Cannot convert a " + type + " value to a number");
                     return 0;
@@ -251,6 +252,7 @@ namespace Jint.Runtime
                     return new JsNumber(ToNumber(o.ToString()));
                 case InternalTypes.Symbol:
                 case InternalTypes.BigInt:
+                case InternalTypes.Empty:
                     // TODO proper TypeError would require Engine instance and a lot of API changes
                     ExceptionHelper.ThrowTypeErrorNoEngine("Cannot convert a " + type + " value to a number");
                     return JsNumber.PositiveZero;
@@ -920,7 +922,7 @@ namespace Jint.Runtime
         public static JsValue ToPropertyKey(JsValue o)
         {
             const InternalTypes PropertyKeys = InternalTypes.String | InternalTypes.Symbol | InternalTypes.PrivateName;
-            return (o._type & PropertyKeys) != InternalTypes.None
+            return (o._type & PropertyKeys) != InternalTypes.Empty
                 ? o
                 : ToPropertyKeyNonString(o);
         }
@@ -930,7 +932,7 @@ namespace Jint.Runtime
         {
             const InternalTypes PropertyKeys = InternalTypes.String | InternalTypes.Symbol | InternalTypes.PrivateName;
             var primitive = ToPrimitive(o, Types.String);
-            return (primitive._type & PropertyKeys) != InternalTypes.None
+            return (primitive._type & PropertyKeys) != InternalTypes.Empty
                 ? primitive
                 : ToStringNonString(primitive);
         }