Forráskód Böngészése

Bring function construction closer to the spec (#1170)

Marko Lahma 3 éve
szülő
commit
3bc93cf385
32 módosított fájl, 622 hozzáadás és 255 törlés
  1. 1 1
      Jint.Tests.Test262/.config/dotnet-tools.json
  2. 1 1
      Jint.Tests.Test262/Jint.Tests.Test262.csproj
  3. 5 0
      Jint.Tests.Test262/Test262Test.cs
  4. 10 8
      Jint/Collections/RefStack.cs
  5. 10 3
      Jint/Engine.cs
  6. 49 30
      Jint/EsprimaExtensions.cs
  7. 51 8
      Jint/Native/Function/ClassDefinition.cs
  8. 171 38
      Jint/Native/Function/FunctionConstructor.cs
  9. 109 20
      Jint/Native/Function/FunctionInstance.cs
  10. 10 0
      Jint/Native/Function/FunctionKind.cs
  11. 17 85
      Jint/Native/Function/ScriptFunctionInstance.cs
  12. 2 1
      Jint/Native/Iterator/IteratorInstance.cs
  13. 1 1
      Jint/Native/Json/JsonParser.cs
  14. 2 2
      Jint/Native/Number/Dtoa/CachePowers.cs
  15. 4 2
      Jint/Native/Reflect/ReflectInstance.cs
  16. 6 1
      Jint/Runtime/Environments/ExecutionContext.cs
  17. 11 0
      Jint/Runtime/ExecutionContextStack.cs
  18. 8 8
      Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs
  19. 1 1
      Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs
  20. 1 1
      Jint/Runtime/Interpreter/Expressions/JintClassExpression.cs
  21. 51 12
      Jint/Runtime/Interpreter/Expressions/JintFunctionExpression.cs
  22. 8 2
      Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
  23. 53 9
      Jint/Runtime/Interpreter/JintFunctionDefinition.cs
  24. 17 8
      Jint/Runtime/Interpreter/JintStatementList.cs
  25. 1 1
      Jint/Runtime/Interpreter/Statements/JintBlockStatement.cs
  26. 10 4
      Jint/Runtime/Interpreter/Statements/JintClassDeclarationStatement.cs
  27. 2 2
      Jint/Runtime/Interpreter/Statements/JintExportDefaultDeclaration.cs
  28. 1 1
      Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs
  29. 1 1
      Jint/Runtime/Interpreter/Statements/JintStatement.cs
  30. 1 1
      Jint/Runtime/Interpreter/Statements/JintTryStatement.cs
  31. 1 1
      Jint/Runtime/Interpreter/Statements/JintVariableDeclaration.cs
  32. 6 2
      Jint/Runtime/Modules/SourceTextModuleRecord.cs

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

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

+ 1 - 1
Jint.Tests.Test262/Jint.Tests.Test262.csproj

@@ -14,7 +14,7 @@
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
     <PackageReference Include="NUnit" Version="3.13.3" />
     <PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
-    <PackageReference Include="Test262Harness" Version="0.0.11" />
+    <PackageReference Include="Test262Harness" Version="0.0.13" />
   </ItemGroup>
   <ItemGroup>
     <Using Include="NUnit.Framework" />

+ 5 - 0
Jint.Tests.Test262/Test262Test.cs

@@ -80,4 +80,9 @@ public abstract partial class Test262Test
             engine.Execute(new JavaScriptParser(file.Program, new ParserOptions(file.FileName)).ParseScript());
         }
     }
+    
+    private partial bool ShouldThrow(Test262File testCase, bool strict)
+    {
+        return testCase.Negative;
+    }
 }

+ 10 - 8
Jint/Collections/RefStack.cs

@@ -28,14 +28,16 @@ namespace Jint.Collections
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public ref readonly T Peek()
         {
-            if (_size == 0)
-            {
-                ExceptionHelper.ThrowInvalidOperationException("stack is empty");
-            }
-
             return ref _array[_size - 1];
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public ref readonly T Peek(int fromTop)
+        {
+            var index = _size - 1 - fromTop;
+            return ref _array[index];
+        }
+
         public T this[int index] => _array[index];
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -155,7 +157,7 @@ namespace Jint.Collections
             {
                 bool returnValue;
                 if (_index == -2)
-                { 
+                {
                     // First call to enumerator.
                     _index = _stack._size - 1;
                     returnValue = (_index >= 0);
@@ -167,7 +169,7 @@ namespace Jint.Collections
                 }
 
                 if (_index == -1)
-                { 
+                {
                     // End of enumeration.
                     return false;
                 }
@@ -192,7 +194,7 @@ namespace Jint.Collections
             {
                 _index = -2;
                 _currentElement = default;
-            }        
+            }
         }
     }
 }

+ 10 - 3
Jint/Engine.cs

@@ -841,6 +841,7 @@ namespace Jint
                 }
             }
 
+            PrivateEnvironmentRecord privateEnv = null;
             if (lexDeclarations != null)
             {
                 for (var i = 0; i < lexDeclarations.Count; i++)
@@ -879,7 +880,7 @@ namespace Jint
                     ExceptionHelper.ThrowSyntaxError(realm, $"Identifier '{fn}' has already been declared");
                 }
 
-                var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, env);
+                var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, env, privateEnv);
                 env.CreateGlobalFunctionBinding(fn, fo, canBeDeleted: false);
             }
 
@@ -1023,7 +1024,7 @@ namespace Jint
                 foreach (var f in configuration.FunctionsToInitialize)
                 {
                     var fn = f.Name;
-                    var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, lexEnv);
+                    var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, lexEnv, privateEnv);
                     varEnv.SetMutableBinding(fn, fo, strict: false);
                 }
             }
@@ -1180,7 +1181,7 @@ namespace Jint
             foreach (var f in functionsToInitialize)
             {
                 var fn = f.Name;
-                var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, lexEnv);
+                var fo = realm.Intrinsics.Function.InstantiateFunctionObject(f, lexEnv, privateEnv);
                 if (varEnvRec is GlobalEnvironmentRecord ger)
                 {
                     ger.CreateGlobalFunctionBinding(fn, fo, canBeDeleted: true);
@@ -1230,6 +1231,12 @@ namespace Jint
             _executionContexts.ReplaceTopVariableEnvironment(newEnv);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void UpdatePrivateEnvironment(PrivateEnvironmentRecord newEnv)
+        {
+            _executionContexts.ReplaceTopPrivateEnvironment(newEnv);
+        }
+
         /// <summary>
         /// Invokes the named callable and returns the resulting object.
         /// </summary>

+ 49 - 30
Jint/EsprimaExtensions.cs

@@ -22,31 +22,51 @@ namespace Jint
 
         public static JsValue GetKey(this Expression expression, Engine engine, bool resolveComputed = false)
         {
+            var completion = TryGetKey(expression, engine, resolveComputed);
+            if (completion.Value is not null)
+            {
+                return TypeConverter.ToPropertyKey(completion.Value);
+            }
+
+            ExceptionHelper.ThrowArgumentException("Unable to extract correct key, node type: " + expression.Type);
+            return JsValue.Undefined;
+        }
+
+        internal static Completion TryGetKey(this ClassProperty property, Engine engine)
+        {
+            return TryGetKey(property.Key, engine, property.Computed);
+        }
+
+        internal static Completion TryGetKey(this Expression expression, Engine engine, bool resolveComputed)
+        {
+            JsValue key;
             if (expression is Literal literal)
             {
                 if (literal.TokenType == TokenType.NullLiteral)
                 {
-                    return JsValue.Null;
+                    key = JsValue.Null;
+                }
+                else
+                {
+                    key = LiteralKeyToString(literal);
                 }
-
-                return LiteralKeyToString(literal);
             }
-
-            if (!resolveComputed && expression is Identifier identifier)
+            else if (!resolveComputed && expression is Identifier identifier)
             {
-                return identifier.Name;
+                key = identifier.Name;
             }
-
-            if (!resolveComputed || !TryGetComputedPropertyKey(expression, engine, out var propertyKey))
+            else if (resolveComputed)
             {
-                ExceptionHelper.ThrowArgumentException("Unable to extract correct key, node type: " + expression.Type);
-                return null;
+                return TryGetComputedPropertyKey(expression, engine);
             }
-
-            return propertyKey;
+            else
+            {
+                key = JsValue.Undefined;
+            }
+            return new Completion(CompletionType.Normal, key, expression.Location);
         }
 
-        private static bool TryGetComputedPropertyKey<T>(T expression, Engine engine, out JsValue propertyKey)
+        private static Completion TryGetComputedPropertyKey<T>(T expression, Engine engine)
             where T : Expression
         {
             if (expression.Type is Nodes.Identifier
@@ -59,15 +79,14 @@ namespace Jint
                 or Nodes.LogicalExpression
                 or Nodes.ConditionalExpression
                 or Nodes.ArrowFunctionExpression
-                or Nodes.FunctionExpression)
+                or Nodes.FunctionExpression
+                or Nodes.YieldExpression)
             {
                 var context = engine._activeEvaluationContext;
-                propertyKey = TypeConverter.ToPropertyKey(JintExpression.Build(engine, expression).GetValue(context).Value);
-                return true;
+                return JintExpression.Build(engine, expression).GetValue(context);
             }
 
-            propertyKey = string.Empty;
-            return false;
+            return new Completion(CompletionType.Normal, JsValue.Undefined, expression.Location);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -266,25 +285,25 @@ namespace Jint
         internal static Record DefineMethod(this ClassProperty m, ObjectInstance obj, ObjectInstance? functionPrototype = null)
         {
             var engine = obj.Engine;
-            var property = TypeConverter.ToPropertyKey(m.GetKey(engine));
-            var prototype = functionPrototype ?? engine.Realm.Intrinsics.Function.PrototypeObject;
+            var propKey = TypeConverter.ToPropertyKey(m.GetKey(engine));
+            var intrinsics = engine.Realm.Intrinsics;
+
+            var runningExecutionContext = engine.ExecutionContext;
+            var scope = runningExecutionContext.LexicalEnvironment;
+            var privateScope= runningExecutionContext.PrivateEnvironment;
+
+            var prototype = functionPrototype ?? intrinsics.Function.PrototypeObject;
             var function = m.Value as IFunction;
             if (function is null)
             {
                 ExceptionHelper.ThrowSyntaxError(engine.Realm);
             }
 
-            var functionDefinition = new JintFunctionDefinition(engine, function);
-            var closure = new ScriptFunctionInstance(
-                engine,
-                functionDefinition,
-                engine.ExecutionContext.LexicalEnvironment,
-                functionDefinition.ThisMode,
-                prototype);
-
+            var definition = new JintFunctionDefinition(engine, function);
+            var closure = intrinsics.Function.OrdinaryFunctionCreate(prototype, definition, definition.ThisMode, scope, privateScope);
             closure.MakeMethod(obj);
 
-            return new Record(property, closure);
+            return new Record(propKey, closure);
         }
 
         internal static void GetImportEntries(this ImportDeclaration import, List<ImportEntry> importEntries, HashSet<string> requestedModules)
@@ -422,4 +441,4 @@ namespace Jint
 
         internal readonly record struct Record(JsValue Key, ScriptFunctionInstance Closure);
     }
-}
+}

+ 51 - 8
Jint/Native/Function/ClassDefinition.cs

@@ -44,10 +44,15 @@ namespace Jint.Native.Function
             _body = body;
         }
 
+        public void Initialize()
+        {
+
+        }
+
         /// <summary>
         /// https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
         /// </summary>
-        public ScriptFunctionInstance BuildConstructor(
+        public Completion BuildConstructor(
             EvaluationContext context,
             EnvironmentRecord env)
         {
@@ -55,7 +60,6 @@ namespace Jint.Native.Function
             using var _ = new StrictModeScope(true, true);
 
             var engine = context.Engine;
-
             var classScope = JintEnvironment.NewDeclarativeEnvironment(engine, env);
 
             if (_className is not null)
@@ -63,6 +67,19 @@ namespace Jint.Native.Function
                 classScope.CreateImmutableBinding(_className, true);
             }
 
+            var outerPrivateEnvironment = engine.ExecutionContext.PrivateEnvironment;
+            var classPrivateEnvironment = JintEnvironment.NewPrivateEnvironment(engine, outerPrivateEnvironment);
+
+            /*
+                6. If ClassBodyopt is present, then
+                    a. For each String dn of the PrivateBoundIdentifiers of ClassBodyopt, do
+                    i. If classPrivateEnvironment.[[Names]] contains a Private Name whose [[Description]] is dn, then
+                    1. Assert: This is only possible for getter/setter pairs.
+                ii. Else,
+                    1. Let name be a new Private Name whose [[Description]] value is dn.
+                    2. Append name to classPrivateEnvironment.[[Names]].
+             */
+
             ObjectInstance? protoParent = null;
             ObjectInstance? constructorParent = null;
             if (_superClass is null)
@@ -99,7 +116,7 @@ namespace Jint.Native.Function
                     else
                     {
                         ExceptionHelper.ThrowTypeError(engine.Realm, "cannot resolve super class prototype chain");
-                        return null!;
+                        return default;
                     }
 
                     constructorParent = (ObjectInstance) superclass;
@@ -151,12 +168,17 @@ namespace Jint.Native.Function
                     }
 
                     var target = !m.Static ? proto : F;
-                    PropertyDefinitionEvaluation(engine, target, m);
+                    var completion = MethodDefinitionEvaluation(engine, target, m);
+                    if (completion.IsAbrupt())
+                    {
+                        return completion;
+                    }
                 }
             }
             finally
             {
                 engine.UpdateLexicalEnvironment(env);
+                engine.UpdatePrivateEnvironment(outerPrivateEnvironment);
             }
 
             if (_className is not null)
@@ -164,13 +186,27 @@ namespace Jint.Native.Function
                 classScope.InitializeBinding(_className, F);
             }
 
-            return F;
+            /*
+            28. Set F.[[PrivateMethods]] to instancePrivateMethods.
+            29. Set F.[[Fields]] to instanceFields.
+            30. For each PrivateElement method of staticPrivateMethods, do
+                a. Perform ! PrivateMethodOrAccessorAdd(method, F).
+            31. For each element fieldRecord of staticFields, do
+                a. Let result be DefineField(F, fieldRecord).
+                b. If result is an abrupt completion, then
+            i. Set the running execution context's PrivateEnvironment to outerPrivateEnvironment.
+                ii. Return result.
+            */
+
+            engine.UpdatePrivateEnvironment(outerPrivateEnvironment);
+
+            return new Completion(CompletionType.Normal, F, _body.Location);
         }
 
         /// <summary>
-        /// https://tc39.es/ecma262/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-methoddefinitionevaluation
         /// </summary>
-        private static void PropertyDefinitionEvaluation(
+        private static Completion MethodDefinitionEvaluation(
             Engine engine,
             ObjectInstance obj,
             MethodDefinition method)
@@ -184,7 +220,12 @@ namespace Jint.Native.Function
             }
             else
             {
-                var propKey = TypeConverter.ToPropertyKey(method.GetKey(engine));
+                var completion = method.TryGetKey(engine);
+                if (completion.IsAbrupt())
+                {
+                    return completion;
+                }
+                var propKey = TypeConverter.ToPropertyKey(completion.Value);
                 var function = method.Value as IFunction;
                 if (function is null)
                 {
@@ -207,6 +248,8 @@ namespace Jint.Native.Function
 
                 obj.DefinePropertyOrThrow(propKey, propDesc);
             }
+
+            return new Completion(CompletionType.Normal, obj, method.Location);
         }
     }
 }

+ 171 - 38
Jint/Native/Function/FunctionConstructor.cs

@@ -1,4 +1,5 @@
-using Esprima;
+using System;
+using Esprima;
 using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Runtime;
@@ -38,43 +39,119 @@ namespace Jint.Native.Function
 
         ObjectInstance IConstructor.Construct(JsValue[] arguments, JsValue newTarget) => Construct(arguments, newTarget);
 
+        private ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
+        {
+            var function = CreateDynamicFunction(
+                this,
+                newTarget,
+                FunctionKind.Normal,
+                arguments);
+
+            return function;
+        }
+
         /// <summary>
         /// https://tc39.es/ecma262/#sec-createdynamicfunction
         /// </summary>
-        private ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
+        internal FunctionInstance CreateDynamicFunction(
+            ObjectInstance constructor,
+            JsValue newTarget,
+            FunctionKind kind,
+            JsValue[] args)
         {
-            var argCount = arguments.Length;
-            string p = "";
-            string body = "";
+            // TODO var callerContext = _engine.GetExecutionContext(1);
+            var callerContext = _engine.ExecutionContext;
+            var callerRealm = callerContext.Realm;
+            var calleeRealm = _engine.ExecutionContext.Realm;
+
+            _engine._host.EnsureCanCompileStrings(callerRealm, calleeRealm);
+
+            if (newTarget.IsUndefined())
+            {
+                newTarget = constructor;
+            }
+
+            Func<Intrinsics, ObjectInstance> fallbackProto = null;
+            switch (kind)
+            {
+                case FunctionKind.Normal:
+                    fallbackProto = static intrinsics => intrinsics.Function.PrototypeObject;
+                    break;
+                case FunctionKind.Generator:
+                case FunctionKind.AsyncGenerator:
+                case FunctionKind.Async:
+                default:
+                    ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(kind), kind.ToString());
+                    break;
+            }
+
+            var argCount = args.Length;
+            var p = "";
+            var body = "";
 
             if (argCount == 1)
             {
-                body = TypeConverter.ToString(arguments[0]);
+                body = TypeConverter.ToString(args[0]);
             }
             else if (argCount > 1)
             {
-                var firstArg = arguments[0];
+                var firstArg = args[0];
                 p = TypeConverter.ToString(firstArg);
                 for (var k = 1; k < argCount - 1; k++)
                 {
-                    var nextArg = arguments[k];
+                    var nextArg = args[k];
                     p += "," + TypeConverter.ToString(nextArg);
                 }
 
-                body = TypeConverter.ToString(arguments[argCount-1]);
+                body = TypeConverter.ToString(args[argCount - 1]);
             }
 
             IFunction function = null;
             try
             {
-                string functionExpression;
+                string functionExpression = null;
                 if (argCount == 0)
                 {
-                    functionExpression = "function f(){}";
+                    switch (kind)
+                    {
+                        case FunctionKind.Normal:
+                            functionExpression = "function f(){}";
+                            break;
+                        case FunctionKind.Generator:
+                            functionExpression = "function* f(){}";
+                            break;
+                        case FunctionKind.Async:
+                            ExceptionHelper.ThrowNotImplementedException("Async functions not implemented");
+                            break;
+                        case FunctionKind.AsyncGenerator:
+                            ExceptionHelper.ThrowNotImplementedException("Async generators not implemented");
+                            break;
+                        default:
+                            ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(kind), kind.ToString());
+                            break;
+                    }
                 }
                 else
                 {
-                    functionExpression = "function f(";
+                    switch (kind)
+                    {
+                        case FunctionKind.Normal:
+                            functionExpression = "function f(";
+                            break;
+                        case FunctionKind.Generator:
+                            functionExpression = "function* f(";
+                            break;
+                        case FunctionKind.Async:
+                            ExceptionHelper.ThrowNotImplementedException("Async functions not implemented");
+                            break;
+                        case FunctionKind.AsyncGenerator:
+                            ExceptionHelper.ThrowNotImplementedException("Async generators not implemented");
+                            break;
+                        default:
+                            ExceptionHelper.ThrowArgumentOutOfRangeException(nameof(kind), kind.ToString());
+                            break;
+                    }
+
                     if (p.IndexOf('/') != -1)
                     {
                         // ensure comments don't screw up things
@@ -101,49 +178,105 @@ namespace Jint.Native.Function
                 var parser = new JavaScriptParser(functionExpression, ParserOptions);
                 function = (IFunction) parser.ParseScript().Body[0];
             }
-            catch (ParserException)
+            catch (ParserException ex)
             {
-                ExceptionHelper.ThrowSyntaxError(_realm);
+                ExceptionHelper.ThrowSyntaxError(_engine.ExecutionContext.Realm, ex.Message);
             }
 
-            // TODO generators etc, rewrite logic
-            var proto = GetPrototypeFromConstructor(newTarget, static intrinsics => intrinsics.Function.PrototypeObject);
+            var proto = GetPrototypeFromConstructor(newTarget, fallbackProto);
+            var realmF = _realm;
+            var scope = realmF.GlobalEnv;
+            PrivateEnvironmentRecord privateScope = null;
+
+            var definition = new JintFunctionDefinition(_engine, function);
+            FunctionInstance F = OrdinaryFunctionCreate(proto, definition, function.Strict ? FunctionThisMode.Strict : FunctionThisMode.Global, scope, privateScope);
+            F.SetFunctionName(_functionNameAnonymous, force: true);
 
-            var functionObject = new ScriptFunctionInstance(
-                Engine,
+            if (kind == FunctionKind.Generator)
+            {
+                ExceptionHelper.ThrowNotImplementedException("generators not implemented");
+            }
+            else if (kind == FunctionKind.AsyncGenerator)
+            {
+                // TODO
+                // Let prototype be ! OrdinaryObjectCreate(%AsyncGeneratorFunction.prototype.prototype%).
+                // Perform DefinePropertyOrThrow(F, "prototype", PropertyDescriptor { [[Value]]: prototype, [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
+                ExceptionHelper.ThrowNotImplementedException("async generators not implemented");
+            }
+            else if (kind == FunctionKind.Normal)
+            {
+                F.MakeConstructor();
+            }
+
+            return F;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-ordinaryfunctioncreate
+        /// </summary>
+        internal ScriptFunctionInstance OrdinaryFunctionCreate(
+            ObjectInstance functionPrototype,
+            JintFunctionDefinition function,
+            FunctionThisMode thisMode,
+            EnvironmentRecord scope,
+            PrivateEnvironmentRecord privateScope)
+        {
+            return new ScriptFunctionInstance(
+                _engine,
                 function,
-                _realm.GlobalEnv,
-                function.Strict,
-                proto)
+                scope,
+                thisMode,
+                functionPrototype)
             {
+                _privateEnvironment = privateScope,
                 _realm = _realm
             };
-
-            functionObject.MakeConstructor();
-
-            // the function is not actually a named function
-            functionObject.SetFunctionName(_functionNameAnonymous, force: true);
-
-            return functionObject;
         }
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-runtime-semantics-instantiatefunctionobject
         /// </summary>
-        internal FunctionInstance InstantiateFunctionObject(JintFunctionDefinition functionDeclaration, EnvironmentRecord env)
+        internal FunctionInstance InstantiateFunctionObject(
+            JintFunctionDefinition functionDeclaration,
+            EnvironmentRecord scope,
+            PrivateEnvironmentRecord privateScope)
         {
-            var functionObject = new ScriptFunctionInstance(
-                Engine,
+            return !functionDeclaration.Function.Generator
+                ? InstantiateOrdinaryFunctionObject(functionDeclaration, scope, privateScope)
+                : InstantiateGeneratorFunctionObject(functionDeclaration, scope, privateScope);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionobject
+        /// </summary>
+        private FunctionInstance InstantiateOrdinaryFunctionObject(
+            JintFunctionDefinition functionDeclaration,
+            EnvironmentRecord scope,
+            PrivateEnvironmentRecord privateScope)
+        {
+            var F = OrdinaryFunctionCreate(
+                _realm.Intrinsics.Function.PrototypeObject,
                 functionDeclaration,
-                env,
-                functionDeclaration.ThisMode)
-            {
-                _realm = _realm
-            };
+                functionDeclaration.ThisMode,
+                scope,
+                privateScope);
 
-            functionObject.MakeConstructor();
+            var name = functionDeclaration.Name ?? "default";
+            F.SetFunctionName(name);
+            F.MakeConstructor();
+            return F;
+        }
 
-            return functionObject;
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-instantiategeneratorfunctionobject
+        /// </summary>
+        private FunctionInstance InstantiateGeneratorFunctionObject(
+            JintFunctionDefinition functionDeclaration,
+            EnvironmentRecord scope,
+            PrivateEnvironmentRecord privateScope)
+        {
+            // TODO generators
+            return InstantiateOrdinaryFunctionObject(functionDeclaration, scope, privateScope);
         }
     }
 }

+ 109 - 20
Jint/Native/Function/FunctionInstance.cs

@@ -25,7 +25,7 @@ namespace Jint.Native.Function
         internal ConstructorKind _constructorKind = ConstructorKind.Base;
 
         internal Realm _realm;
-        private PrivateEnvironmentRecord _privateEnvironment;
+        internal PrivateEnvironmentRecord _privateEnvironment;
         private readonly IScriptOrModule _scriptOrModule;
 
         protected FunctionInstance(
@@ -198,6 +198,9 @@ namespace Jint.Native.Function
             base.RemoveOwnProperty(property);
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-setfunctionname
+        /// </summary>
         internal void SetFunctionName(JsValue name, string prefix = null, bool force = false)
         {
             if (!force && _nameDescriptor != null && UnwrapJsValue(_nameDescriptor) != JsString.Empty)
@@ -283,18 +286,32 @@ namespace Jint.Native.Function
             return _engine.ExecutionContext.Realm;
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-makemethod
+        /// </summary>
         internal void MakeMethod(ObjectInstance homeObject)
         {
             _homeObject = homeObject;
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-ordinaryobjectcreate
+        /// </summary>
+        internal ObjectInstance OrdinaryObjectCreate(ObjectInstance proto)
+        {
+            var prototype = new ObjectInstance(_engine)
+            {
+                _prototype = proto
+            };
+            return prototype;
+        }
+
         /// <summary>
         /// https://tc39.es/ecma262/#sec-ordinarycallbindthis
         /// </summary>
         internal void OrdinaryCallBindThis(ExecutionContext calleeContext, JsValue thisArgument)
         {
-            var thisMode = _thisMode;
-            if (thisMode == FunctionThisMode.Lexical)
+            if (_thisMode == FunctionThisMode.Lexical)
             {
                 return;
             }
@@ -302,7 +319,6 @@ namespace Jint.Native.Function
             var calleeRealm = _realm;
 
             var localEnv = (FunctionEnvironmentRecord) calleeContext.LexicalEnvironment;
-
             JsValue thisValue;
             if (_thisMode == FunctionThisMode.Strict)
             {
@@ -324,20 +340,12 @@ namespace Jint.Native.Function
             localEnv.BindThisValue(thisValue);
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal Completion OrdinaryCallEvaluateBody(
-            EvaluationContext context,
-            JsValue[] arguments,
-            ExecutionContext calleeContext)
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-ordinarycallevaluatebody
+        /// </summary>
+        internal Completion OrdinaryCallEvaluateBody(EvaluationContext context, JsValue[] arguments)
         {
-            var argumentsInstance = _engine.FunctionDeclarationInstantiation(
-                functionInstance: this,
-                arguments);
-
-            var result = _functionDefinition.Execute(context);
-            argumentsInstance?.FunctionWasCalled();
-
-            return result;
+            return _functionDefinition.EvaluateBody(context, this, arguments);
         }
 
         /// <summary>
@@ -352,11 +360,11 @@ namespace Jint.Native.Function
 
             var calleeContext = new ExecutionContext(
                 _scriptOrModule,
-                localEnv,
-                localEnv,
+                lexicalEnvironment: localEnv,
+                variableEnvironment: localEnv,
                 _privateEnvironment,
                 calleeRealm,
-                this);
+                function: this);
 
             // If callerContext is not already suspended, suspend callerContext.
             // Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
@@ -366,6 +374,20 @@ namespace Jint.Native.Function
             return _engine.EnterExecutionContext(calleeContext);
         }
 
+        internal void MakeConstructor(bool writableProperty = true, ObjectInstance prototype = null)
+        {
+            _constructorKind = ConstructorKind.Base;
+            if (prototype is null)
+            {
+                prototype = new ObjectInstanceWithConstructor(_engine, this)
+                {
+                    _prototype = _realm.Intrinsics.Object.PrototypeObject
+                };
+            }
+
+            _prototypeDescriptor = new PropertyDescriptor(prototype, writableProperty, enumerable: false, configurable: false);
+        }
+
         public override string ToString()
         {
             // TODO no way to extract SourceText from Esprima at the moment, just returning native code
@@ -377,5 +399,72 @@ namespace Jint.Native.Function
             }
             return "function " + name + "() { [native code] }";
         }
+
+        private sealed class ObjectInstanceWithConstructor : ObjectInstance
+        {
+            private PropertyDescriptor _constructor;
+
+            public ObjectInstanceWithConstructor(Engine engine, ObjectInstance thisObj) : base(engine)
+            {
+                _constructor = new PropertyDescriptor(thisObj, PropertyFlag.NonEnumerable);
+            }
+
+            public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
+            {
+                if (_constructor != null)
+                {
+                    yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Constructor, _constructor);
+                }
+
+                foreach (var entry in base.GetOwnProperties())
+                {
+                    yield return entry;
+                }
+            }
+
+            public override PropertyDescriptor GetOwnProperty(JsValue property)
+            {
+                if (property == CommonProperties.Constructor)
+                {
+                    return _constructor ?? PropertyDescriptor.Undefined;
+                }
+
+                return base.GetOwnProperty(property);
+            }
+
+            protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
+            {
+                if (property == CommonProperties.Constructor)
+                {
+                    _constructor = desc;
+                }
+                else
+                {
+                    base.SetOwnProperty(property, desc);
+                }
+            }
+
+            public override bool HasOwnProperty(JsValue property)
+            {
+                if (property == CommonProperties.Constructor)
+                {
+                    return _constructor != null;
+                }
+
+                return base.HasOwnProperty(property);
+            }
+
+            public override void RemoveOwnProperty(JsValue property)
+            {
+                if (property == CommonProperties.Constructor)
+                {
+                    _constructor = null;
+                }
+                else
+                {
+                    base.RemoveOwnProperty(property);
+                }
+            }
+        }
     }
 }

+ 10 - 0
Jint/Native/Function/FunctionKind.cs

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

+ 17 - 85
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-using Esprima.Ast;
+using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -42,7 +41,10 @@ namespace Jint.Native.Function
             _prototype = proto ?? _engine.Realm.Intrinsics.Function.PrototypeObject;
             _length = new LazyPropertyDescriptor(null, _ => JsNumber.Create(function.Initialize(this).Length), PropertyFlag.Configurable);
 
-            if (!function.Strict && !engine._isStrict && function.Function is not ArrowFunctionExpression)
+            if (!function.Strict
+                && !engine._isStrict
+                && function.Function is not ArrowFunctionExpression
+                && !function.Function.Generator)
             {
                 DefineOwnProperty(CommonProperties.Arguments, new GetSetPropertyDescriptor.ThrowerPropertyDescriptor(engine, PropertyFlag.Configurable | PropertyFlag.CustomJsValue));
                 DefineOwnProperty(CommonProperties.Caller, new PropertyDescriptor(Undefined, PropertyFlag.Configurable));
@@ -70,7 +72,7 @@ namespace Jint.Native.Function
 
                     // actual call
                     var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
-                    var result = OrdinaryCallEvaluateBody(context, arguments, calleeContext);
+                    var result = OrdinaryCallEvaluateBody(context, arguments);
 
                     if (result.Type == CompletionType.Throw)
                     {
@@ -104,7 +106,8 @@ namespace Jint.Native.Function
 
         internal override bool IsConstructor =>
             (_homeObject.IsUndefined() || _isClassConstructor)
-            && _functionDefinition?.Function is not ArrowFunctionExpression;
+            && _functionDefinition?.Function is not ArrowFunctionExpression
+            && _functionDefinition?.Function.Generator != true;
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget
@@ -129,16 +132,18 @@ namespace Jint.Native.Function
             if (kind == ConstructorKind.Base)
             {
                 OrdinaryCallBindThis(calleeContext, thisArgument);
+                InitializeInstanceElements(thisArgument, this);
             }
 
             var constructorEnv = (FunctionEnvironmentRecord) calleeContext.LexicalEnvironment;
 
-            var strict = _thisMode == FunctionThisMode.Strict || _engine._isStrict;
+            var strict = _thisMode == FunctionThisMode.Strict;
             using (new StrictModeScope(strict, force: true))
             {
                 try
                 {
-                    var result = OrdinaryCallEvaluateBody(_engine._activeEvaluationContext, arguments, calleeContext);
+                    var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
+                    var result = OrdinaryCallEvaluateBody(context, arguments);
 
                     // The DebugHandler needs the current execution context before the return for stepping through the return point
                     if (_engine._activeEvaluationContext.DebugMode && result.Type != CompletionType.Throw)
@@ -182,90 +187,17 @@ namespace Jint.Native.Function
             return (ObjectInstance) constructorEnv.GetThisBinding();
         }
 
-        internal void MakeConstructor(bool writableProperty = true, ObjectInstance prototype = null)
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-initializeinstanceelements
+        /// </summary>
+        private void InitializeInstanceElements(JsValue o, JsValue constructor)
         {
-            _constructorKind = ConstructorKind.Base;
-            if (prototype is null)
-            {
-                prototype = new ObjectInstanceWithConstructor(_engine, this)
-                {
-                    _prototype = _realm.Intrinsics.Object.PrototypeObject
-                };
-            }
-
-            _prototypeDescriptor = new PropertyDescriptor(prototype, writableProperty, enumerable: false, configurable: false);
+            // TODO private fields
         }
 
         internal void MakeClassConstructor()
         {
             _isClassConstructor = true;
         }
-
-        private class ObjectInstanceWithConstructor : ObjectInstance
-        {
-            private PropertyDescriptor _constructor;
-
-            public ObjectInstanceWithConstructor(Engine engine, ObjectInstance thisObj) : base(engine)
-            {
-                _constructor = new PropertyDescriptor(thisObj, PropertyFlag.NonEnumerable);
-            }
-
-            public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
-            {
-                if (_constructor != null)
-                {
-                    yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Constructor, _constructor);
-                }
-
-                foreach (var entry in base.GetOwnProperties())
-                {
-                    yield return entry;
-                }
-            }
-
-            public override PropertyDescriptor GetOwnProperty(JsValue property)
-            {
-                if (property == CommonProperties.Constructor)
-                {
-                    return _constructor ?? PropertyDescriptor.Undefined;
-                }
-
-                return base.GetOwnProperty(property);
-            }
-
-            protected internal override void SetOwnProperty(JsValue property, PropertyDescriptor desc)
-            {
-                if (property == CommonProperties.Constructor)
-                {
-                    _constructor = desc;
-                }
-                else
-                {
-                    base.SetOwnProperty(property, desc);
-                }
-            }
-
-            public override bool HasOwnProperty(JsValue property)
-            {
-                if (property == CommonProperties.Constructor)
-                {
-                    return _constructor != null;
-                }
-
-                return base.HasOwnProperty(property);
-            }
-
-            public override void RemoveOwnProperty(JsValue property)
-            {
-                if (property == CommonProperties.Constructor)
-                {
-                    _constructor = null;
-                }
-                else
-                {
-                    base.RemoveOwnProperty(property);
-                }
-            }
-        }
     }
 }

+ 2 - 1
Jint/Native/Iterator/IteratorInstance.cs

@@ -80,7 +80,8 @@ namespace Jint.Native.Iterator
 
         internal sealed class ValueIteratorPosition : ObjectInstance
         {
-            internal static ObjectInstance Done(Engine engine) => new ValueIteratorPosition(engine, Undefined, true);
+            internal static ObjectInstance Done(Engine engine, JsValue value = null)
+                => new ValueIteratorPosition(engine, value ?? Undefined, true);
 
             public ValueIteratorPosition(Engine engine, JsValue value, bool? done = null) : base(engine)
             {

+ 1 - 1
Jint/Native/Json/JsonParser.cs

@@ -804,7 +804,7 @@ namespace Jint.Native.Json
             }
         }
 
-        private class Extra
+        private sealed class Extra
         {
             public int? Loc;
             public int[] Range;

+ 2 - 2
Jint/Native/Number/Dtoa/CachePowers.cs

@@ -32,11 +32,11 @@ using System.Diagnostics;
 
 namespace Jint.Native.Number.Dtoa
 {
-    internal sealed class CachedPowers
+    internal static class CachedPowers
     {
         private const double Kd1Log210 = 0.30102999566398114; //  1 / lg(10)
 
-        private class CachedPower
+        private sealed class CachedPower
         {
             internal readonly ulong Significand;
             internal readonly short BinaryExponent;

+ 4 - 2
Jint/Native/Reflect/ReflectInstance.cs

@@ -69,10 +69,12 @@ namespace Jint.Native.Reflect
             return ((ICallable) target).Call(thisArgument, args);
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-reflect.construct
+        /// </summary>
         private JsValue Construct(JsValue thisObject, JsValue[] arguments)
         {
-            var targetArgument = arguments.At(0);
-            var target = AssertConstructor(_engine, targetArgument);
+            var target = AssertConstructor(_engine, arguments.At(0));
 
             var newTargetArgument = arguments.At(2, arguments[0]);
             AssertConstructor(_engine, newTargetArgument);

+ 6 - 1
Jint/Runtime/Environments/ExecutionContext.cs

@@ -39,6 +39,11 @@ namespace Jint.Runtime.Environments
             return new ExecutionContext(ScriptOrModule, LexicalEnvironment, variableEnvironment, PrivateEnvironment, Realm, Function);
         }
 
+        public ExecutionContext UpdatePrivateEnvironment(PrivateEnvironmentRecord privateEnvironment)
+        {
+            return new ExecutionContext(ScriptOrModule, LexicalEnvironment, VariableEnvironment, privateEnvironment, Realm, Function);
+        }
+
         /// <summary>
         /// https://tc39.es/ecma262/#sec-getthisenvironment
         /// </summary>
@@ -54,7 +59,7 @@ namespace Jint.Runtime.Environments
                     if (lex.HasThisBinding())
                     {
                         return lex;
-                        
+
                     }
 
                     lex = lex._outerEnv;

+ 11 - 0
Jint/Runtime/ExecutionContextStack.cs

@@ -2,6 +2,7 @@
 
 using System.Runtime.CompilerServices;
 using Jint.Collections;
+using Jint.Native.Generator;
 using Jint.Runtime.Environments;
 
 namespace Jint.Runtime
@@ -29,9 +30,19 @@ namespace Jint.Runtime
             array[size - 1] = array[size - 1].UpdateVariableEnvironment(newEnv);
         }
 
+        public void ReplaceTopPrivateEnvironment(PrivateEnvironmentRecord newEnv)
+        {
+            var array = _stack._array;
+            var size = _stack._size;
+            array[size - 1] = array[size - 1].UpdatePrivateEnvironment(newEnv);
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public ref readonly ExecutionContext Peek() => ref _stack.Peek();
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public ref readonly ExecutionContext Peek(int fromTop) => ref _stack.Peek(fromTop);
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void Push(in ExecutionContext context) => _stack.Push(in context);
 

+ 8 - 8
Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs

@@ -48,16 +48,16 @@ namespace Jint.Runtime.Interpreter.Expressions
             BindingPattern pattern,
             JsValue argument,
             EnvironmentRecord environment,
-            bool checkObjectPatternPropertyReference = true)
+            bool checkPatternPropertyReference = true)
         {
             if (pattern is ArrayPattern ap)
             {
-                return HandleArrayPattern(context, ap, argument, environment, checkObjectPatternPropertyReference);
+                return HandleArrayPattern(context, ap, argument, environment, checkPatternPropertyReference);
             }
 
             if (pattern is ObjectPattern op)
             {
-                return HandleObjectPattern(context, op, argument, environment, checkObjectPatternPropertyReference);
+                return HandleObjectPattern(context, op, argument, environment, checkPatternPropertyReference);
             }
 
             ExceptionHelper.ThrowArgumentException("Not a pattern");
@@ -216,7 +216,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                         if (restElement.Argument is Identifier leftIdentifier)
                         {
-                            AssignToIdentifier(engine, leftIdentifier.Name, array, environment);
+                            AssignToIdentifier(engine, leftIdentifier.Name, array, environment, checkReference);
                         }
                         else if (restElement.Argument is BindingPattern bp)
                         {
@@ -257,7 +257,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                                 ((FunctionInstance) value).SetFunctionName(new JsString(leftIdentifier.Name));
                             }
 
-                            AssignToIdentifier(engine, leftIdentifier.Name, value, environment);
+                            AssignToIdentifier(engine, leftIdentifier.Name, value, environment, checkReference);
                         }
                         else if (assignmentPattern.Left is BindingPattern bp)
                         {
@@ -351,7 +351,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                             ((FunctionInstance) value).SetFunctionName(target.Name);
                         }
 
-                        AssignToIdentifier(context.Engine, target.Name, value, environment);
+                        AssignToIdentifier(context.Engine, target.Name, value, environment, checkReference);
                     }
                     else if (p.Value is BindingPattern bindingPattern)
                     {
@@ -380,7 +380,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         var count = Math.Max(0, source.Properties?.Count ?? 0) - processedProperties.Count;
                         var rest = context.Engine.Realm.Intrinsics.Object.Construct(count);
                         source.CopyDataProperties(rest, processedProperties);
-                        AssignToIdentifier(context.Engine, leftIdentifier.Name, rest, environment);
+                        AssignToIdentifier(context.Engine, leftIdentifier.Name, rest, environment, checkReference);
                     }
                     else if (restElement.Argument is BindingPattern bp)
                     {
@@ -448,7 +448,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
                 if (checkReference && lhs.IsUnresolvableReference() && StrictModeScope.IsStrictModeCode)
                 {
-                    ExceptionHelper.ThrowReferenceError(engine.Realm, "invalid reference");
+                    ExceptionHelper.ThrowReferenceError(engine.Realm, lhs);
                 }
                 engine.PutValue(lhs, rval);
             }

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

@@ -251,7 +251,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             return arguments;
         }
 
-        private class CachedArgumentsHolder
+        private sealed class CachedArgumentsHolder
         {
             internal JintExpression[] JintArguments;
             internal JsValue[] CachedArguments;

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

@@ -15,7 +15,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         protected override ExpressionResult EvaluateInternal(EvaluationContext context)
         {
             var env = context.Engine.ExecutionContext.LexicalEnvironment;
-            return NormalCompletion(_classDefinition.BuildConstructor(context, env));
+            return _classDefinition.BuildConstructor(context, env);
         }
     }
 }

+ 51 - 12
Jint/Runtime/Interpreter/Expressions/JintFunctionExpression.cs

@@ -8,7 +8,7 @@ namespace Jint.Runtime.Interpreter.Expressions
     {
         private readonly JintFunctionDefinition _function;
 
-        public JintFunctionExpression(Engine engine, IFunction function)
+        public JintFunctionExpression(Engine engine, FunctionExpression function)
             : base(ArrowParameterPlaceHolder.Empty)
         {
             _function = new JintFunctionDefinition(engine, function);
@@ -20,24 +20,63 @@ namespace Jint.Runtime.Interpreter.Expressions
         }
 
         public override Completion GetValue(EvaluationContext context)
+        {
+            var closure = !_function.Function.Generator
+                ? InstantiateOrdinaryFunctionExpression(context, _function.Name)
+                : InstantiateGeneratorFunctionExpression(context, _function.Name);
+
+            return Completion.Normal(closure, _expression.Location);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionexpression
+        /// </summary>
+        private ScriptFunctionInstance InstantiateOrdinaryFunctionExpression(EvaluationContext context, string name = "")
         {
             var engine = context.Engine;
-            var funcEnv = JintEnvironment.NewDeclarativeEnvironment(engine, engine.ExecutionContext.LexicalEnvironment);
+            var runningExecutionContext = engine.ExecutionContext;
+            var scope = runningExecutionContext.LexicalEnvironment;
 
-            var closure = new ScriptFunctionInstance(
-                engine,
-                _function,
-                funcEnv,
-                _function.ThisMode);
+            DeclarativeEnvironmentRecord funcEnv = null;
+            if (!string.IsNullOrWhiteSpace(name))
+            {
+                funcEnv = JintEnvironment.NewDeclarativeEnvironment(engine, engine.ExecutionContext.LexicalEnvironment);
+                funcEnv.CreateImmutableBinding(name, strict: false);
+            }
 
-            closure.MakeConstructor();
+            var privateScope = runningExecutionContext.PrivateEnvironment;
 
-            if (_function.Name != null)
+            var thisMode = _function.Strict || engine._isStrict
+                ? FunctionThisMode.Strict
+                : FunctionThisMode.Global;
+
+            var intrinsics = engine.Realm.Intrinsics;
+            var closure = intrinsics.Function.OrdinaryFunctionCreate(
+                intrinsics.Function.PrototypeObject,
+                _function,
+                thisMode,
+                funcEnv ?? scope,
+                privateScope
+            );
+
+            if (name is not null)
             {
-                funcEnv.CreateMutableBindingAndInitialize(_function.Name, canBeDeleted: false, closure);
+                closure.SetFunctionName(name);
             }
+            closure.MakeConstructor();
 
-            return Completion.Normal(closure, _expression.Location);
+            funcEnv?.InitializeBinding(name, closure);
+
+            return closure;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-instantiategeneratorfunctionexpression
+        /// </summary>
+        private ScriptFunctionInstance InstantiateGeneratorFunctionExpression(EvaluationContext context, string name = "")
+        {
+            // TODO generators
+            return InstantiateOrdinaryFunctionExpression(context, name);
         }
     }
-}
+}

+ 8 - 2
Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs

@@ -21,7 +21,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         // and don't require duplicate checking
         private bool _canBuildFast;
 
-        private class ObjectProperty
+        private sealed class ObjectProperty
         {
             internal readonly string? _key;
             private JsString? _keyJsString;
@@ -186,7 +186,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                 JsValue? propName = objectProperty.KeyJsString;
                 if (propName is null)
                 {
-                    propName = TypeConverter.ToPropertyKey(property.GetKey(engine));
+                    var completion = property.TryGetKey(engine);
+                    if (completion.IsAbrupt())
+                    {
+                        return completion;
+                    }
+
+                    propName = TypeConverter.ToPropertyKey(completion.Value);
                 }
 
                 if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)

+ 53 - 9
Jint/Runtime/Interpreter/JintFunctionDefinition.cs

@@ -35,18 +35,62 @@ namespace Jint.Runtime.Interpreter
 
         public FunctionThisMode ThisMode => Strict || _engine._isStrict ? FunctionThisMode.Strict : FunctionThisMode.Global;
 
-        internal Completion Execute(EvaluationContext context)
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-evaluatebody
+        /// </summary>
+        internal Completion EvaluateBody(EvaluationContext context, FunctionInstance functionObject, JsValue[] argumentsList)
         {
+            Completion result;
             if (Function.Expression)
             {
-                _bodyExpression ??= JintExpression.Build(_engine, (Expression) Function.Body);
-                var jsValue = _bodyExpression?.GetValue(context).Value ?? Undefined.Instance;
-                return new Completion(CompletionType.Return, jsValue, Function.Body.Location);
+                result = EvaluateConciseBody(context, functionObject, argumentsList);
             }
+            else if (Function.Generator)
+            {
+                result = EvaluateFunctionBody(context, functionObject, argumentsList);
+                // TODO generators
+                // result = EvaluateGeneratorBody(functionObject, argumentsList);
+            }
+            else
+            {
+                result = EvaluateFunctionBody(context, functionObject, argumentsList);
+            }
+
+            return new Completion(result.Type, result.GetValueOrDefault().Clone(), result.Target, result.Location);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-evaluategeneratorbody
+        /// </summary>
+        private Completion EvaluateGeneratorBody(FunctionInstance functionObject, JsValue[] argumentsList)
+        {
+            ExceptionHelper.ThrowNotImplementedException("generators not implemented");
+            return default;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-evaluateconcisebody
+        /// </summary>
+        private Completion EvaluateConciseBody(EvaluationContext context, FunctionInstance functionObject, JsValue[] argumentsList)
+        {
+            var argumentsInstance = _engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
+            _bodyExpression ??= JintExpression.Build(_engine, (Expression) Function.Body);
+            var jsValue = _bodyExpression?.GetValue(context).Value ?? Undefined.Instance;
+            argumentsInstance?.FunctionWasCalled();
+            return new Completion(CompletionType.Return, jsValue, null, Function.Body.Location);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-evaluatefunctionbody
+        /// </summary>
+        private Completion EvaluateFunctionBody(EvaluationContext context, FunctionInstance functionObject, JsValue[] argumentsList)
+        {
+            var argumentsInstance = _engine.FunctionDeclarationInstantiation(functionObject, argumentsList);
+            _bodyStatementList ??= new JintStatementList(Function);
+            var completion = _bodyStatementList.Execute(context);
+            argumentsInstance?.FunctionWasCalled();
 
-            var blockStatement = (BlockStatement) Function.Body;
-            _bodyStatementList ??= new JintStatementList(blockStatement, blockStatement.Body);
-            return _bodyStatementList.Execute(context);
+            return completion;
         }
 
         internal State Initialize(FunctionInstance functionInstance)
@@ -65,7 +109,7 @@ namespace Jint.Runtime.Interpreter
             public bool ArgumentsObjectNeeded;
             public List<Key> VarNames;
             public LinkedList<JintFunctionDefinition> FunctionsToInitialize;
-            public readonly HashSet<Key> FunctionNames = new HashSet<Key>();
+            public readonly HashSet<Key> FunctionNames = new();
             public LexicalVariableDeclaration[] LexicalDeclarations = Array.Empty<LexicalVariableDeclaration>();
             public HashSet<Key> ParameterBindings;
             public List<VariableValuePair> VarsToInitialize;
@@ -333,4 +377,4 @@ namespace Jint.Runtime.Interpreter
             state.ParameterNames = parameterNames.ToArray();
         }
     }
-}
+}

+ 17 - 8
Jint/Runtime/Interpreter/JintStatementList.cs

@@ -8,7 +8,7 @@ namespace Jint.Runtime.Interpreter
 {
     internal sealed class JintStatementList
     {
-        private class Pair
+        private sealed class Pair
         {
             internal JintStatement Statement;
             internal Completion? Value;
@@ -19,6 +19,14 @@ namespace Jint.Runtime.Interpreter
 
         private Pair[] _jintStatements;
         private bool _initialized;
+        private uint _index;
+        private readonly bool _generator;
+
+        public JintStatementList(IFunction function)
+            : this((BlockStatement) function.Body)
+        {
+            _generator = function.Generator;
+        }
 
         public JintStatementList(BlockStatement blockStatement)
             : this(blockStatement, blockStatement.Body)
@@ -51,6 +59,7 @@ namespace Jint.Runtime.Interpreter
                     Value = value
                 };
             }
+
             _jintStatements = jintStatements;
         }
 
@@ -127,7 +136,7 @@ namespace Jint.Runtime.Interpreter
             EnvironmentRecord env,
             List<Declaration> declarations)
         {
-            var envRec = env;
+            var privateEnv = env._engine.ExecutionContext.PrivateEnvironment;
             var boundNames = new List<string>();
             for (var i = 0; i < declarations.Count; i++)
             {
@@ -139,20 +148,20 @@ namespace Jint.Runtime.Interpreter
                     var dn = boundNames[j];
                     if (d is VariableDeclaration { Kind: VariableDeclarationKind.Const })
                     {
-                        envRec.CreateImmutableBinding(dn, strict: true);
+                        env.CreateImmutableBinding(dn, strict: true);
                     }
                     else
                     {
-                        envRec.CreateMutableBinding(dn, canBeDeleted: false);
+                        env.CreateMutableBinding(dn, canBeDeleted: false);
                     }
                 }
 
                 if (d is FunctionDeclaration functionDeclaration)
                 {
-                    var fn = functionDeclaration.Id!.Name;
-                    var functionDefinition = new JintFunctionDefinition(engine, functionDeclaration);
-                    var fo = env._engine.Realm.Intrinsics.Function.InstantiateFunctionObject(functionDefinition, env);
-                    envRec.InitializeBinding(fn, fo);
+                    var definition = new JintFunctionDefinition(engine, functionDeclaration);
+                    var fn = definition.Name;
+                    var fo = env._engine.Realm.Intrinsics.Function.InstantiateFunctionObject(definition, env, privateEnv);
+                    env.InitializeBinding(fn, fo);
                 }
             }
         }

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

@@ -19,7 +19,7 @@ namespace Jint.Runtime.Interpreter.Statements
             _lexicalDeclarations = HoistingScope.GetLexicalDeclarations(_statement);
         }
 
-        protected override bool SupportsResume => true;
+        internal override bool SupportsResume => true;
 
         protected override Completion ExecuteInternal(EvaluationContext context)
         {

+ 10 - 4
Jint/Runtime/Interpreter/Statements/JintClassDeclarationStatement.cs

@@ -1,6 +1,7 @@
 #nullable enable
 
 using Esprima.Ast;
+using Jint.Native;
 using Jint.Native.Function;
 
 namespace Jint.Runtime.Interpreter.Statements
@@ -18,15 +19,20 @@ namespace Jint.Runtime.Interpreter.Statements
         {
             var engine = context.Engine;
             var env = engine.ExecutionContext.LexicalEnvironment;
-            var F = _classDefinition.BuildConstructor(context, env);
+            var completion = _classDefinition.BuildConstructor(context, env);
+
+            if (completion.IsAbrupt())
+            {
+                return completion;
+            }
 
             var classBinding = _classDefinition._className;
             if (classBinding != null)
             {
-                env.InitializeBinding(classBinding, F);
+                env.InitializeBinding(classBinding, completion.Value!);
             }
 
-            return Completion.Empty();
+            return new Completion(CompletionType.Normal, null!, null, Location);
         }
     }
-}
+}

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

@@ -49,7 +49,7 @@ internal sealed class JintExportDefaultDeclaration : JintStatement<ExportDefault
         JsValue value;
         if (_classDefinition is not null)
         {
-            value = _classDefinition.BuildConstructor(context, env);
+            value = _classDefinition.BuildConstructor(context, env).GetValueOrDefault();
             var classBinding = _classDefinition._className;
             if (classBinding != null)
             {
@@ -93,4 +93,4 @@ internal sealed class JintExportDefaultDeclaration : JintStatement<ExportDefault
             ExceptionHelper.ThrowNotImplementedException();
         }
     }
-}
+}

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

@@ -236,7 +236,7 @@ namespace Jint.Runtime.Interpreter.Statements
                             _assignmentPattern,
                             nextValue,
                             iterationEnv,
-                            checkObjectPatternPropertyReference: _lhsKind != LhsKind.VarBinding);
+                            checkPatternPropertyReference: _lhsKind != LhsKind.VarBinding);
 
                         if (lhsKind == LhsKind.Assignment)
                         {

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

@@ -50,7 +50,7 @@ namespace Jint.Runtime.Interpreter.Statements
             return ExecuteInternal(context);
         }
 
-        protected virtual bool SupportsResume => false;
+        internal virtual bool SupportsResume => false;
 
         protected abstract Completion ExecuteInternal(EvaluationContext context);
 

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

@@ -28,7 +28,7 @@ namespace Jint.Runtime.Interpreter.Statements
             }
         }
 
-        protected override bool SupportsResume => true;
+        internal override bool SupportsResume => true;
 
         protected override Completion ExecuteInternal(EvaluationContext context)
         {

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

@@ -100,7 +100,7 @@ namespace Jint.Runtime.Interpreter.Statements
                             declaration.LeftPattern,
                             completion.Value,
                             environment,
-                            checkObjectPatternPropertyReference: _statement.Kind != VariableDeclarationKind.Var);
+                            checkPatternPropertyReference: _statement.Kind != VariableDeclarationKind.Var);
                     }
                     else if (declaration.LeftIdentifierExpression == null
                              || JintAssignmentExpression.SimpleAssignmentExpression.AssignToIdentifier(

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

@@ -306,8 +306,12 @@ internal class SourceTextModuleRecord : CyclicModuleRecord
                 var fn = d.Id?.Name ?? "*default*";
                 var fd = new JintFunctionDefinition(_engine, d);
                 env.CreateMutableBinding(fn, true);
-                var fo = realm.Intrinsics.Function.InstantiateFunctionObject(fd, env);
-                if (fn == "*default*") fo.SetFunctionName("default");
+                // TODO private scope
+                var fo = realm.Intrinsics.Function.InstantiateFunctionObject(fd, env, privateScope: null);
+                if (fn == "*default*")
+                {
+                    fo.SetFunctionName("default");
+                }
                 env.InitializeBinding(fn, fo);
             }
         }