浏览代码

Class support (#816)

* enable class tests
* remove ArrowFunctionInstance for now and always use ScriptFunctionInstance
* fix constructors to use OrdinaryCreateFromConstructor
* rely on MakeConstructor instead of always initializing function to have prototype
Marko Lahma 4 年之前
父节点
当前提交
5f1892cd42
共有 64 个文件被更改,包括 1247 次插入753 次删除
  1. 15 0
      Jint.Tests.Test262/Language/Expressions/ClassTests.cs
  2. 15 0
      Jint.Tests.Test262/Language/Statements/ClassTests.cs
  3. 60 9
      Jint.Tests.Test262/Test262Test.cs
  4. 12 244
      Jint.Tests.Test262/test/skipped.json
  5. 1 1
      Jint.Tests/Runtime/Domain/UuidConstructor.cs
  6. 1 1
      Jint.Tests/Runtime/Domain/UuidInstance.cs
  7. 11 11
      Jint.Tests/Runtime/ErrorTests.cs
  8. 69 15
      Jint/Engine.cs
  9. 62 14
      Jint/EsprimaExtensions.cs
  10. 19 4
      Jint/Native/Array/ArrayPrototype.cs
  11. 13 8
      Jint/Native/Boolean/BooleanConstructor.cs
  12. 6 0
      Jint/Native/Boolean/BooleanInstance.cs
  13. 8 5
      Jint/Native/Error/ErrorConstructor.cs
  14. 0 84
      Jint/Native/Function/ArrowFunctionInstance.cs
  15. 7 1
      Jint/Native/Function/BindFunctionInstance.cs
  16. 204 0
      Jint/Native/Function/ClassDefinition.cs
  17. 8 0
      Jint/Native/Function/ConstructorKind.cs
  18. 35 2
      Jint/Native/Function/EvalFunctionInstance.cs
  19. 4 39
      Jint/Native/Function/FunctionConstructor.cs
  20. 105 16
      Jint/Native/Function/FunctionInstance.cs
  21. 18 8
      Jint/Native/Function/FunctionPrototype.cs
  22. 9 0
      Jint/Native/Function/FunctionThisMode.cs
  23. 91 69
      Jint/Native/Function/ScriptFunctionInstance.cs
  24. 5 2
      Jint/Native/Iterator/IteratorInstance.cs
  25. 1 1
      Jint/Native/JsNumber.cs
  26. 1 1
      Jint/Native/JsValue.cs
  27. 7 3
      Jint/Native/Map/MapConstructor.cs
  28. 12 9
      Jint/Native/Number/NumberConstructor.cs
  29. 6 0
      Jint/Native/Number/NumberInstance.cs
  30. 6 1
      Jint/Native/Object/ObjectConstructor.cs
  31. 15 6
      Jint/Native/Object/ObjectInstance.cs
  32. 1 4
      Jint/Native/Object/ObjectPrototype.cs
  33. 1 1
      Jint/Native/Proxy/ProxyConstructor.cs
  34. 11 14
      Jint/Native/Proxy/ProxyInstance.cs
  35. 4 31
      Jint/Native/RegExp/RegExpConstructor.cs
  36. 8 3
      Jint/Native/Set/SetConstructor.cs
  37. 1 0
      Jint/Native/Symbol/GlobalSymbolRegistry.cs
  38. 2 1
      Jint/Native/Symbol/SymbolConstructor.cs
  39. 3 3
      Jint/Pooling/ReferencePool.cs
  40. 25 5
      Jint/Runtime/CallStack/CallStackElement.cs
  41. 21 20
      Jint/Runtime/CallStack/JintCallStack.cs
  42. 2 0
      Jint/Runtime/Environments/EnvironmentRecord.cs
  43. 7 9
      Jint/Runtime/Environments/FunctionEnvironmentRecord.cs
  44. 24 0
      Jint/Runtime/ExceptionHelper.cs
  45. 1 1
      Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs
  46. 6 6
      Jint/Runtime/Interpreter/Expressions/JintArrowFunctionExpression.cs
  47. 78 27
      Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs
  48. 21 0
      Jint/Runtime/Interpreter/Expressions/JintClassExpression.cs
  49. 42 2
      Jint/Runtime/Interpreter/Expressions/JintExpression.cs
  50. 4 2
      Jint/Runtime/Interpreter/Expressions/JintFunctionExpression.cs
  51. 2 2
      Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs
  52. 1 1
      Jint/Runtime/Interpreter/Expressions/JintLiteralExpression.cs
  53. 13 12
      Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs
  54. 6 5
      Jint/Runtime/Interpreter/Expressions/JintNewExpression.cs
  55. 38 30
      Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
  56. 20 0
      Jint/Runtime/Interpreter/Expressions/JintSuperExpression.cs
  57. 5 0
      Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs
  58. 1 1
      Jint/Runtime/Interpreter/JintFunctionDefinition.cs
  59. 32 0
      Jint/Runtime/Interpreter/Statements/JintClassDeclarationStatement.cs
  60. 14 1
      Jint/Runtime/Interpreter/Statements/JintForInForOfStatement.cs
  61. 1 0
      Jint/Runtime/Interpreter/Statements/JintStatement.cs
  62. 7 5
      Jint/Runtime/References/Reference.cs
  63. 18 11
      Jint/Runtime/TypeConverter.cs
  64. 1 2
      README.md

+ 15 - 0
Jint.Tests.Test262/Language/Expressions/ClassTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262.Language.Expressions
+{
+    public class ClassTests : Test262Test
+    {
+        [Theory(DisplayName = "language\\expressions\\class")]
+        [MemberData(nameof(SourceFiles), "language\\expressions\\class", false)]
+        [MemberData(nameof(SourceFiles), "language\\expressions\\class", true, Skip = "Skipped")]
+        protected void Class(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 15 - 0
Jint.Tests.Test262/Language/Statements/ClassTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262.Language.Statements
+{
+    public class ClassTests : Test262Test
+    {
+        [Theory(DisplayName = "language\\statements\\class")]
+        [MemberData(nameof(SourceFiles), "language\\statements\\class", false)]
+        [MemberData(nameof(SourceFiles), "language\\statements\\class", true, Skip = "Skipped")]
+        protected void Class(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 60 - 9
Jint.Tests.Test262/Test262Test.cs

@@ -84,8 +84,8 @@ namespace Jint.Tests.Test262
                 .Strict(strict)
             );
 
-            engine.Execute(Sources["sta.js"]);
-            engine.Execute(Sources["assert.js"]);
+            engine.Execute(Sources["sta.js"], CreateParserOptions("sta.js"));
+            engine.Execute(Sources["assert.js"], CreateParserOptions("assert.js"));
             engine.SetValue("print", new ClrFunctionInstance(engine, "print", (thisObj, args) => TypeConverter.ToString(args.At(0))));
 
             var o = engine.Object.Construct(Arguments.Empty);
@@ -112,13 +112,13 @@ namespace Jint.Tests.Test262
                 var files = includes.Groups[1].Captures[0].Value.Split(',');
                 foreach (var file in files)
                 {
-                    engine.Execute(Sources[file.Trim()]);
+                    engine.Execute(Sources[file.Trim()], CreateParserOptions(file.Trim()));
                 }
             }
 
             if (code.IndexOf("propertyHelper.js", StringComparison.OrdinalIgnoreCase) != -1)
             {
-                engine.Execute(Sources["propertyHelper.js"]);
+                engine.Execute(Sources["propertyHelper.js"], CreateParserOptions("propertyHelper.js"));
             }
             
             string lastError = null;
@@ -210,10 +210,6 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 reason = "tail-calls not implemented";
                                 break;
-                            case "class":
-                                skip = true;
-                                reason = "class keyword not implemented";
-                                break;
                             case "BigInt":
                                 skip = true;
                                 reason = "BigInt not implemented";
@@ -230,6 +226,11 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 reason = "async not implemented";
                                 break;
+                            case "class-fields-private":
+                            case "class-fields-public":
+                                skip = true;
+                                reason = "private/public class fields not implemented in esprima";
+                                break;
                             case "new.target":
                                 skip = true;
                                 reason = "MetaProperty not implemented";
@@ -290,6 +291,48 @@ namespace Jint.Tests.Test262
                     reason = "Unicode support and its special cases need more work";
                 }
 
+                if (name.StartsWith("language/statements/class/subclass/builtin-objects/Promise"))
+                {
+                    skip = true;
+                    reason = "Promise not implemented";
+                }
+                
+                if (name.StartsWith("language/statements/class/subclass/builtin-objects/TypedArray"))
+                {
+                    skip = true;
+                    reason = "TypedArray not implemented";
+                }
+                
+                if (name.StartsWith("language/statements/class/subclass/builtin-objects/WeakMap"))
+                {
+                    skip = true;
+                    reason = "WeakMap not implemented";
+                }
+                
+                if (name.StartsWith("language/statements/class/subclass/builtin-objects/WeakSet"))
+                {
+                    skip = true;
+                    reason = "WeakSet not implemented";
+                }
+                
+                if (name.StartsWith("language/statements/class/subclass/builtin-objects/ArrayBuffer/"))
+                {
+                    skip = true;
+                    reason = "ArrayBuffer not implemented";
+                }
+                
+                if (name.StartsWith("language/statements/class/subclass/builtin-objects/DataView"))
+                {
+                    skip = true;
+                    reason = "DataView not implemented";
+                }
+                                
+                if (name.StartsWith("language/statements/class/subclass/builtins.js"))
+                {
+                    skip = true;
+                    reason = "Uint8Array not implemented";
+                }
+                
                 if (name.StartsWith("built-ins/RegExp/CharacterClassEscapes/"))
                 {
                     skip = true;
@@ -322,8 +365,16 @@ namespace Jint.Tests.Test262
 
             return results;
         }
+        
+        private static ParserOptions CreateParserOptions(string fileName) => 
+            new ParserOptions(fileName)
+            {
+                AdaptRegexp = true,
+                Tolerant = true,
+                Loc = true
+            };
     }
-
+    
     public class SourceFile : IXunitSerializable
     {
         public SourceFile()

+ 12 - 244
Jint.Tests.Test262/test/skipped.json

@@ -94,6 +94,10 @@
     "source": "language/expressions/object/method-definition/object-method-returns-promise.js",
     "reason": "Promise not implemented"
   },
+  {
+    "source": "language/statements/class/definition/class-method-returns-promise.js",
+    "reason": "Promise not implemented"
+  },
   {
     "source": "built-ins/Symbol/species/subclassing.js",
     "reason": "subclassing not implemented"
@@ -319,249 +323,6 @@
     "reason": "inner binding rejects modification (from parameters) Expected a Error to be thrown but no exception was thrown at all"
   },
 
-  // class support
-  {
-    "source": "built-ins/Function/prototype/toString/class-declaration-complex-heritage.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/class-declaration-explicit-ctor.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/class-declaration-implicit-ctor.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/class-expression-explicit-ctor.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/class-expression-implicit-ctor.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/getter-class-expression-static.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/getter-class-expression.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/getter-class-statement-static.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/getter-class-statement.js",
-    "reason": "class not implemented"
-  }, 
-  {
-    "source": "built-ins/Function/prototype/toString/method-class-expression-static.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/method-class-expression.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/method-class-statement-static.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/method-class-statement.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/setter-class-expression-static.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/setter-class-expression.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/setter-class-statement-static.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Function/prototype/toString/setter-class-statement.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/global-code/script-decl-lex.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/global-code/script-decl-lex-deletion.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/global-code/script-decl-var-collision.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/global-code/script-decl-lex-lex.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/global-code/decl-lex.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/let/dstr-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/global-code/decl-lex-deletion.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/let/dstr-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/const/dstr-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/const/dstr-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/arrow-function/dstr-dflt-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "built-ins/Array/prototype/concat/Array.prototype.concat_non-array.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/rest-parameters/with-new-target.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/arrow-function/dstr-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for/dstr-const-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for/dstr-let-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for/dstr-let-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for/dstr-const-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for/dstr-var-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for/dstr-var-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/arrow-function/dstr-dflt-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/arrow-function/dstr-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/arrow-function/lexical-super-property-from-within-constructor.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/arrow-function/lexical-supercall-from-immediately-invoked-arrow.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/arrow-function/lexical-super-property.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/arrow-function/lexical-super-call-from-within-constructor.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/function/dstr-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/function/dstr-dflt-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/function/dstr-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/function/dstr-dflt-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/object/dstr-meth-dflt-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/object/dstr-meth-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/object/dstr-meth-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/object/dstr-meth-dflt-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/object/method-definition/name-invoke-ctor",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/object/method-definition/name-invoke-ctor.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/expressions/object/method-definition/name-prototype-prop.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for-of/dstr-const-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for-of/dstr-const-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for-of/dstr-let-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for-of/dstr-let-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for-of/dstr-var-ary-ptrn-elem-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-  {
-    "source": "language/statements/for-of/dstr-var-obj-ptrn-id-init-fn-name-class.js",
-    "reason": "class not implemented"
-  },
-
-
   {
     "source": "language/expressions/object/accessor-name-computed-yield-id.js",
     "reason": "accessor / yield not implemented"
@@ -623,7 +384,6 @@
 
 
   {
-    "source": "language/expressions/object/fn-name-arrow.js",
     "source": "language/expressions/arrow-function/scope-paramsbody-var-open.js",
     "reason": "not implemented: Creation of new variable environment for the function body (as distinct from that for the function's parameters)"
   },
@@ -644,6 +404,14 @@
     "source": "built-ins/Object/prototype/toString/proxy-function.js",
     "reason": "generators not implemented"
   },
+  {
+    "source": "language/statements/class/subclass/builtin-objects/GeneratorFunction/instance-prototype.js",
+    "reason": "generators not implemented"
+  },
+  {
+    "source": "language/statements/class/subclass/builtin-objects/GeneratorFunction/regular-subclassing.js",
+    "reason": "generators not implemented"
+  },
 
   // Esprima problems
 

+ 1 - 1
Jint.Tests/Runtime/Domain/UuidConstructor.cs

@@ -30,7 +30,7 @@ namespace Jint.Tests.Runtime.Domain
             return Undefined;
         }
 
-        protected override ObjectInstance GetPrototypeOf() => _prototype;
+        protected internal override ObjectInstance GetPrototypeOf() => _prototype;
 
         internal ObjectInstance _prototype;
 

+ 1 - 1
Jint.Tests/Runtime/Domain/UuidInstance.cs

@@ -5,7 +5,7 @@ namespace Jint.Tests.Runtime.Domain
 {
     internal class UuidInstance : ObjectInstance, IObjectWrapper
     {
-        protected override ObjectInstance GetPrototypeOf() => _prototype;
+        protected internal override ObjectInstance GetPrototypeOf() => _prototype;
 
         internal ObjectInstance _prototype;
 

+ 11 - 11
Jint.Tests/Runtime/ErrorTests.cs

@@ -73,9 +73,9 @@ var b = function(v) {
             Assert.Equal("custom.js", e.Location.Source);
 
             var stack = e.StackTrace;
-            EqualIgnoringNewLineDifferences(@"    at a (v) custom.js:2:18
-    at b (v) custom.js:6:9
-    at main.js:1:9", stack);
+            EqualIgnoringNewLineDifferences(@"   at a (v) custom.js:2:18
+   at b (v) custom.js:6:9
+   at main.js:1:9", stack);
         }
 
         private class Folder
@@ -122,11 +122,11 @@ var b = function(v) {
             ));
 
             Assert.Equal("Cannot read property 'Name' of null", javaScriptException.Message);
-            EqualIgnoringNewLineDifferences(@"    at recursive (folderInstance) <anonymous>:6:44
-    at recursive (folderInstance) <anonymous>:8:32
-    at recursive (folderInstance) <anonymous>:8:32
-    at recursive (folderInstance) <anonymous>:8:32
-    at <anonymous>:12:17", javaScriptException.StackTrace);
+            EqualIgnoringNewLineDifferences(@"   at recursive (folderInstance) <anonymous>:6:44
+   at recursive (folderInstance) <anonymous>:8:32
+   at recursive (folderInstance) <anonymous>:8:32
+   at recursive (folderInstance) <anonymous>:8:32
+   at <anonymous>:12:17", javaScriptException.StackTrace);
 
             var expected = new List<string>
             {
@@ -152,9 +152,9 @@ var x = b(7);";
             var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
 
             const string expected = @"Jint.Runtime.JavaScriptException: Cannot read property 'yyy' of undefined
-    at a (v) <anonymous>:2:18
-    at b (v) <anonymous>:6:12
-    at <anonymous>:9:9";
+   at a (v) <anonymous>:2:18
+   at b (v) <anonymous>:6:12
+   at <anonymous>:9:9";
             
             EqualIgnoringNewLineDifferences(expected, ex.ToString());
         }

+ 69 - 15
Jint/Engine.cs

@@ -32,13 +32,14 @@ using Jint.Runtime.Environments;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interop.Reflection;
 using Jint.Runtime.Interpreter;
+using Jint.Runtime.Interpreter.Expressions;
 using Jint.Runtime.References;
 
 namespace Jint
 {
     public class Engine
     {
-        private static readonly ParserOptions DefaultParserOptions = new("<anonymous>")
+        internal static readonly ParserOptions DefaultParserOptions = new("<anonymous>")
         {
             AdaptRegexp = true,
             Tolerant = true,
@@ -100,7 +101,7 @@ namespace Jint
             { typeof(UInt16), (engine, v) => JsNumber.Create((UInt16)v) },
             { typeof(UInt32), (engine, v) => JsNumber.Create((UInt32)v) },
             { typeof(UInt64), (engine, v) => JsNumber.Create((UInt64)v) },
-            { typeof(System.Text.RegularExpressions.Regex), (engine, v) => engine.RegExp.Construct((System.Text.RegularExpressions.Regex)v, "", engine) }
+            { typeof(System.Text.RegularExpressions.Regex), (engine, v) => engine.RegExp.Construct((System.Text.RegularExpressions.Regex)v, "") }
         };
 
         // shared frozen version
@@ -463,7 +464,7 @@ namespace Jint
                 if (baseValue.IsObject())
                 {
                     var o = TypeConverter.ToObject(this, baseValue);
-                    var v = o.Get(property);
+                    var v = o.Get(property, reference.GetThisValue());
                     return v;
                 }
                 else
@@ -680,7 +681,7 @@ namespace Jint
         /// <param name="property">The name of the property to return.</param>
         public JsValue GetValue(JsValue scope, JsValue property)
         {
-            var reference = _referencePool.Rent(scope, property, _isStrict);
+            var reference = _referencePool.Rent(scope, property, _isStrict, thisValue: null);
             var jsValue = GetValue(reference, false);
             _referencePool.Return(reference);
             return jsValue;
@@ -695,20 +696,20 @@ namespace Jint
             return GetIdentifierReference(env, name, StrictModeScope.IsStrictModeCode);
         }
 
-        private Reference GetIdentifierReference(LexicalEnvironment lex, string name, bool strict)
+        private Reference GetIdentifierReference(LexicalEnvironment env, string name, bool strict)
         {
-            if (lex is null)
+            if (env is null)
             {
                 return new Reference(JsValue.Undefined, name, strict);
             }
 
-            var envRec = lex._record;
+            var envRec = env._record;
             if (envRec.HasBinding(name))
             {
                 return new Reference(envRec, name, strict);
             }
 
-            return GetIdentifierReference(lex._outer, name, strict);
+            return GetIdentifierReference(env._outer, name, strict);
         }
 
         /// <summary>
@@ -791,6 +792,12 @@ namespace Jint
                     for (var j = 0; j < boundNames.Count; j++)
                     {
                         var vn = boundNames[j];
+
+                        if (envRec.HasLexicalDeclaration(vn))
+                        {
+                            ExceptionHelper.ThrowSyntaxError(this, $"Identifier '{vn}' has already been declared");
+                        }
+                        
                         if (!declaredFunctionNames.Contains(vn))
                         {
                             var vnDefinable = envRec.CanDeclareGlobalVar(vn);
@@ -836,7 +843,13 @@ namespace Jint
 
             foreach (var f in functionToInitialize)
             {
-                var fn = f.Id.Name;
+                var fn = f.Id!.Name;
+                
+                if (envRec.HasLexicalDeclaration(fn))
+                {
+                    ExceptionHelper.ThrowSyntaxError(this, $"Identifier '{fn}' has already been declared");
+                }
+                
                 var fo = Function.CreateFunctionObject(f, env);
                 envRec.CreateGlobalFunctionBinding(fn, fo, canBeDeleted: false);
             }
@@ -1201,25 +1214,33 @@ namespace Jint
             _executionContexts.ReplaceTopVariableEnvironment(newEnv);
         }
 
-        internal JsValue Call(ICallable callable, JsValue thisObject, JsValue[] arguments, Location? location)
+        internal JsValue Call(ICallable callable, JsValue thisObject, JsValue[] arguments, JintExpression expression)
         {
             if (callable is FunctionInstance functionInstance)
             {
-                return Call(functionInstance, thisObject, arguments, location);
+                return Call(functionInstance, thisObject, arguments, expression);
             }
             
             return callable.Call(thisObject, arguments);
         }
 
+        internal JsValue Construct(IConstructor constructor, JsValue[] arguments, JsValue newTarget, JintExpression expression)
+        {
+            if (constructor is FunctionInstance functionInstance)
+            {
+                return Construct(functionInstance, arguments, newTarget, expression);
+            }
+            
+            return constructor.Construct(arguments, newTarget);
+        }
+
         internal JsValue Call(
             FunctionInstance functionInstance,
             JsValue thisObject,
             JsValue[] arguments,
-            Location? location)
+            JintExpression expression)
         {
-            location ??= ((Node) functionInstance._functionDefinition?.Function)?.Location;
-
-            var callStackElement = new CallStackElement(functionInstance, location);
+            var callStackElement = new CallStackElement(functionInstance, expression);
             var recursionDepth = CallStack.Push(callStackElement);
 
             if (recursionDepth > Options.MaxRecursionDepth)
@@ -1245,5 +1266,38 @@ namespace Jint
 
             return result;
         }
+
+        internal JsValue Construct(
+            FunctionInstance functionInstance,
+            JsValue[] arguments,
+            JsValue newTarget,
+            JintExpression expression)
+        {
+            var callStackElement = new CallStackElement(functionInstance, expression);
+            var recursionDepth = CallStack.Push(callStackElement);
+
+            if (recursionDepth > Options.MaxRecursionDepth)
+            {
+                // pop the current element as it was never reached
+                CallStack.Pop();
+                ExceptionHelper.ThrowRecursionDepthOverflowException(CallStack, callStackElement.ToString());
+            }
+
+            if (_isDebugMode)
+            {
+                DebugHandler.AddToDebugCallStack(functionInstance);
+            }
+
+            var result = ((IConstructor) functionInstance).Construct(arguments, newTarget);
+
+            if (_isDebugMode)
+            {
+                DebugHandler.PopDebugCallStack();
+            }
+
+            CallStack.Pop();
+
+            return result;
+        }
     }
 }

+ 62 - 14
Jint/EsprimaExtensions.cs

@@ -1,19 +1,23 @@
+#nullable enable
+
 using System;
 using System.Collections.Generic;
-using System.Globalization;
 using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Native;
+using Jint.Native.Function;
+using Jint.Native.Object;
 using Jint.Runtime;
+using Jint.Runtime.Interpreter;
 using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint
 {
     public static class EsprimaExtensions
     {
-        public static JsValue GetKey(this Property property, Engine engine) => GetKey(property.Key, engine, property.Computed);
+        public static JsValue GetKey(this ClassProperty property, Engine engine) => GetKey(property.Key, engine, property.Computed);
 
-        public static JsValue GetKey<T>(this T expression, Engine engine, bool resolveComputed = false) where T : Expression
+        public static JsValue GetKey(this Expression expression, Engine engine, bool resolveComputed = false)
         {
             if (expression is Literal literal)
             {
@@ -40,6 +44,7 @@ namespace Jint
                 || expression.Type == Nodes.CallExpression
                 || expression.Type == Nodes.BinaryExpression
                 || expression.Type == Nodes.UpdateExpression
+                || expression.Type == Nodes.AssignmentExpression
                 || expression is StaticMemberExpression)
             {
                 propertyKey = TypeConverter.ToPropertyKey(JintExpression.Build(engine, expression).GetValue());
@@ -54,7 +59,10 @@ namespace Jint
         internal static bool IsFunctionWithName<T>(this T node) where T : Node
         {
             var type = node.Type;
-            return type == Nodes.FunctionExpression || type == Nodes.ArrowFunctionExpression || type == Nodes.ArrowParameterPlaceHolder;
+            return type == Nodes.FunctionExpression 
+                   || type == Nodes.ArrowFunctionExpression 
+                   || type == Nodes.ArrowParameterPlaceHolder 
+                   || type == Nodes.ClassExpression;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -63,16 +71,10 @@ namespace Jint
             // prevent conversion to scientific notation
             if (literal.Value is double d)
             {
-                return DoubleToString(d);
+                return TypeConverter.ToString(d);
             }
             return literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
         }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static string DoubleToString(double d)
-        {
-            return (d - (long) d) == 0 ? ((long) d).ToString() : d.ToString(CultureInfo.InvariantCulture);
-        }
         
         internal static void GetBoundNames(this VariableDeclaration variableDeclaration, List<string> target)
         {
@@ -84,7 +86,7 @@ namespace Jint
             }
         }
 
-        internal static void GetBoundNames(this Node parameter, List<string> target)
+        internal static void GetBoundNames(this Node? parameter, List<string> target)
         {
             if (parameter is null || parameter.Type == Nodes.Literal)
             {
@@ -94,7 +96,7 @@ namespace Jint
             // try to get away without a loop
             if (parameter is Identifier id)
             {
-                target.Add(id.Name);
+                target.Add(id.Name!);
                 return;
             }
 
@@ -102,7 +104,7 @@ namespace Jint
             {
                 if (parameter is Identifier identifier)
                 {
-                    target.Add(identifier.Name);
+                    target.Add(identifier.Name!);
                     return;
                 }
                 
@@ -139,10 +141,56 @@ namespace Jint
                 else if (parameter is AssignmentPattern assignmentPattern)
                 {
                     parameter = assignmentPattern.Left;
+                    if (assignmentPattern.Right is ClassExpression classExpression)
+                    {
+                        // TODO check if there's more generic rule
+                        if (classExpression.Id is not null)
+                        {
+                            target.Add(classExpression.Id.Name!);
+                        }
+                    }
                     continue;
                 }
                 break;
             }
         }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-definemethod
+        /// </summary>
+        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.Function.PrototypeObject;
+            var function = m.Value as IFunction ?? ExceptionHelper.ThrowSyntaxError<IFunction>(engine);
+            var functionDefinition = new JintFunctionDefinition(engine, function);
+            var functionThisMode = functionDefinition.Strict || engine._isStrict
+                ? FunctionThisMode.Strict 
+                : FunctionThisMode.Global;
+
+            var closure = new ScriptFunctionInstance(
+                engine,
+                functionDefinition,
+                engine.ExecutionContext.LexicalEnvironment,
+                functionThisMode,
+                prototype);
+
+            closure.MakeMethod(obj);
+
+            return new Record(property, closure);
+        }
+
+        internal readonly struct Record
+        {
+            public Record(JsValue key, ScriptFunctionInstance closure)
+            {
+                Key = key;
+                Closure = closure;
+            }
+
+            public readonly JsValue Key;
+            public readonly ScriptFunctionInstance Closure;
+        }
     }
 }

+ 19 - 4
Jint/Native/Array/ArrayPrototype.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using Jint.Collections;
+using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.Symbol;
 using Jint.Pooling;
@@ -20,6 +21,7 @@ namespace Jint.Native.Array
     public sealed class ArrayPrototype : ArrayInstance
     {
         private ArrayConstructor _arrayConstructor;
+        internal ClrFunctionInstance _originalIteratorFunction;
 
         private ArrayPrototype(Engine engine) : base(engine)
         {
@@ -90,9 +92,10 @@ namespace Jint.Native.Array
             };
             SetProperties(properties);
 
+            _originalIteratorFunction = new ClrFunctionInstance(Engine, "iterator", Values, 1);
             var symbols = new SymbolDictionary(2)
             {
-                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Values, 1), propertyFlags),
+                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(_originalIteratorFunction, propertyFlags),
                 [GlobalSymbolRegistry.Unscopables] = new PropertyDescriptor(unscopables, PropertyFlag.Configurable)
             };
             SetSymbols(symbols);
@@ -1059,10 +1062,10 @@ namespace Jint.Native.Array
 
             // try to find best capacity
             bool hasObjectSpreadables = false;
-            uint capacity = 0;
+            ulong capacity = 0;
             for (var i = 0; i < items.Count; i++)
             {
-                uint increment;
+                ulong increment;
                 if (!(items[i] is ObjectInstance objectInstance))
                 {
                     increment = 1;
@@ -1071,11 +1074,23 @@ namespace Jint.Native.Array
                 {
                     var isConcatSpreadable = objectInstance.IsConcatSpreadable;
                     hasObjectSpreadables |= isConcatSpreadable;
-                    increment = isConcatSpreadable ? ArrayOperations.For(objectInstance).GetLength() : 1; 
+                    if (isConcatSpreadable)
+                    {
+                        increment = ArrayOperations.For(objectInstance).GetLongLength();
+                    }
+                    else
+                    {
+                        increment = 1;
+                    }
                 }
                 capacity += increment;
             }
 
+            if (capacity > NumberConstructor.MaxSafeInteger)
+            {
+                ExceptionHelper.ThrowTypeError(_engine, "Invalid array length");
+            }
+
             uint n = 0;
             var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), capacity);
             var aOperations = ArrayOperations.For(a);

+ 13 - 8
Jint/Native/Boolean/BooleanConstructor.cs

@@ -13,6 +13,8 @@ namespace Jint.Native.Boolean
             : base(engine, _functionName)
         {
         }
+        
+        public BooleanPrototype PrototypeObject { get; private set; }
 
         public static BooleanConstructor CreateBooleanConstructor(Engine engine)
         {
@@ -41,20 +43,23 @@ namespace Jint.Native.Boolean
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.7.2.1
+        /// https://tc39.es/ecma262/#sec-boolean-constructor-boolean-value
         /// </summary>
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
-            return Construct(TypeConverter.ToBoolean(arguments.At(0)));
-        }
+            var b = TypeConverter.ToBoolean(arguments.At(0)) 
+                ? JsBoolean.True
+                : JsBoolean.False;
 
-        public BooleanPrototype PrototypeObject { get; private set; }
+            if (newTarget.IsUndefined())
+            {
+                return Construct(b);
+            }
 
-        public BooleanInstance Construct(bool value)
-        {
-            return Construct(value ? JsBoolean.True : JsBoolean.False);
+            var o = OrdinaryCreateFromConstructor(newTarget, PrototypeObject, static (engine, state) => new BooleanInstance(engine, (JsBoolean) state), b);
+            return Construct(b);
         }
-        
+
         public BooleanInstance Construct(JsBoolean value)
         {
             var instance = new BooleanInstance(Engine)

+ 6 - 0
Jint/Native/Boolean/BooleanInstance.cs

@@ -9,6 +9,12 @@ namespace Jint.Native.Boolean
             : base(engine, ObjectClass.Boolean)
         {
         }
+        
+        public BooleanInstance(Engine engine, JsBoolean value)
+            : base(engine, ObjectClass.Boolean)
+        {
+            PrimitiveValue = value;
+        }
 
         Types IPrimitiveInstance.Type => Types.Boolean;
 

+ 8 - 5
Jint/Native/Error/ErrorConstructor.cs

@@ -45,23 +45,26 @@ namespace Jint.Native.Error
 
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
-            var instance = new ErrorInstance(Engine, _name);
-            instance._prototype = PrototypeObject;
+            var o = OrdinaryCreateFromConstructor(
+                newTarget,
+                PrototypeObject, 
+                static (e, state) => new ErrorInstance(e, (JsString) state),
+                _name);
 
             var jsValue = arguments.At(0);
             if (!jsValue.IsUndefined())
             {
                 var msg = TypeConverter.ToString(jsValue);
                 var msgDesc = new PropertyDescriptor(msg, true, false, true);
-                instance.DefinePropertyOrThrow("message", msgDesc);
+                o.DefinePropertyOrThrow("message", msgDesc);
             }
 
-            return instance;
+            return o;
         }
 
         public ErrorPrototype PrototypeObject { get; private set; }
 
-        protected override ObjectInstance GetPrototypeOf()
+        protected internal override ObjectInstance GetPrototypeOf()
         {
             return _name._value != "Error" ? _engine.Error : _prototype;
         }

+ 0 - 84
Jint/Native/Function/ArrowFunctionInstance.cs

@@ -1,84 +0,0 @@
-using Esprima.Ast;
-using Jint.Runtime;
-using Jint.Runtime.Descriptors;
-using Jint.Runtime.Descriptors.Specialized;
-using Jint.Runtime.Environments;
-using Jint.Runtime.Interpreter;
-
-namespace Jint.Native.Function
-{
-    public sealed class ArrowFunctionInstance : FunctionInstance
-    {
-        private readonly JintFunctionDefinition _function;
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/6.0/#sec-arrow-function-definitions
-        /// </summary>
-        public ArrowFunctionInstance(
-            Engine engine,
-            IFunction functionDeclaration,
-            LexicalEnvironment scope,
-            bool strict)
-            : this(engine, new JintFunctionDefinition(engine, functionDeclaration), scope, strict)
-        {
-        }
-
-        internal ArrowFunctionInstance(
-            Engine engine,
-            JintFunctionDefinition function,
-            LexicalEnvironment scope,
-            bool strict)
-            : base(engine, function, scope, strict ? FunctionThisMode.Strict : FunctionThisMode.Lexical)
-        {
-            _function = function;
-
-            _prototype = Engine.Function.PrototypeObject;
-
-            _length = new LazyPropertyDescriptor(() => JsNumber.Create(function.Initialize(engine, this).Length), PropertyFlag.Configurable);
-        }
-
-        // for example RavenDB wants to inspect this
-        public IFunction FunctionDeclaration => _function.Function;
-
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.1
-        /// </summary>
-        public override JsValue Call(JsValue thisArg, JsValue[] arguments)
-        {
-            var strict = Strict || _engine._isStrict;
-            using (new StrictModeScope(strict, true))
-            {
-                var localEnv = LexicalEnvironment.NewFunctionEnvironment(_engine, this, Undefined);
-                _engine.EnterExecutionContext(localEnv, localEnv);
-
-                try
-                {
-                    _engine.FunctionDeclarationInstantiation(
-                        functionInstance: this,
-                        arguments,
-                        localEnv);
-
-                    var result = _function.Execute();
-
-                    var value = result.GetValueOrDefault().Clone();
-
-                    if (result.Type == CompletionType.Throw)
-                    {
-                        ExceptionHelper.ThrowJavaScriptException(_engine, value, result);
-                    }
-
-                    if (result.Type == CompletionType.Return)
-                    {
-                        return value;
-                    }
-                }
-                finally
-                {
-                    _engine.LeaveExecutionContext();
-                }
-
-                return Undefined;
-            }
-        }
-    }
-}

+ 7 - 1
Jint/Native/Function/BindFunctionInstance.cs

@@ -38,6 +38,12 @@ namespace Jint.Native.Function
             }
 
             var args = CreateArguments(arguments);
+
+            if (ReferenceEquals(this, newTarget))
+            {
+                newTarget = TargetFunction;
+            }
+            
             var value = target.Construct(args, newTarget);
             _engine._jsValueArrayPool.ReturnArray(args);
 
@@ -62,7 +68,7 @@ namespace Jint.Native.Function
             return combined;
         }
 
-        internal override bool IsConstructor => TargetFunction is IConstructor;
+        internal override bool IsConstructor => TargetFunction.IsConstructor;
 
         public override string ToString() => "function () { [native code] }";
     }

+ 204 - 0
Jint/Native/Function/ClassDefinition.cs

@@ -0,0 +1,204 @@
+#nullable enable
+
+using Esprima;
+using Esprima.Ast;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Environments;
+using Jint.Runtime.Interpreter.Expressions;
+
+namespace Jint.Native.Function
+{
+    internal class ClassDefinition
+    {
+        private static readonly MethodDefinition _superConstructor;
+        private static readonly MethodDefinition _emptyConstructor;
+
+        internal readonly string? _className;
+        private readonly Expression? _superClass;
+        private readonly ClassBody _body;
+
+        static ClassDefinition()
+        {
+            // generate missing constructor AST only once
+            static MethodDefinition CreateConstructorMethodDefinition(string source)
+            {
+                var parser = new JavaScriptParser(source);
+                var script = parser.ParseScript();
+                return (MethodDefinition) script.Body[0].ChildNodes[2].ChildNodes[0];
+            }
+
+            _superConstructor = CreateConstructorMethodDefinition("class temp { constructor(...args) { super(...args); } }");
+            _emptyConstructor = CreateConstructorMethodDefinition("class temp { constructor() {} }");
+        }
+
+        public ClassDefinition(
+            Identifier? className,
+            Expression? superClass,
+            ClassBody body)
+        {
+            _className = className?.Name;
+            _superClass = superClass;
+            _body = body;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
+        /// </summary>
+        public ScriptFunctionInstance BuildConstructor(
+            Engine engine,
+            LexicalEnvironment env)
+        {
+            // A class definition is always strict mode code.
+            using var _ = (new StrictModeScope(true, true));
+            
+            var classScope = LexicalEnvironment.NewDeclarativeEnvironment(engine, env);
+
+            if (_className is not null)
+            {
+                classScope._record.CreateImmutableBinding(_className, true);
+            }
+
+            ObjectInstance? protoParent = null;
+            ObjectInstance? constructorParent = null;
+            if (_superClass is null)
+            {
+                protoParent = engine.Object.PrototypeObject;
+                constructorParent = engine.Function.PrototypeObject;
+            }
+            else
+            {
+                engine.UpdateLexicalEnvironment(classScope);
+                var superclass = JintExpression.Build(engine, _superClass).GetValue();
+                engine.UpdateLexicalEnvironment(env);
+
+                if (superclass.IsNull())
+                {
+                    protoParent = null;
+                    constructorParent = engine.Function.PrototypeObject;
+                }
+                else if (!superclass.IsConstructor)
+                {
+                    ExceptionHelper.ThrowTypeError(engine, "super class is not a constructor");
+                }
+                else
+                {
+                    var temp = superclass.Get("prototype");
+                    if (temp is ObjectInstance protoParentObject)
+                    {
+                        protoParent = protoParentObject;
+                    }
+                    else if (temp._type == InternalTypes.Null)
+                    {
+                        // OK
+                    }
+                    else
+                    {
+                        ExceptionHelper.ThrowTypeError(engine);
+                        return null!;
+                    }
+
+                    constructorParent = (ObjectInstance) superclass;
+                }
+            }
+
+            var proto = new ObjectInstance(engine)
+            {
+                _prototype = protoParent
+            };
+
+            MethodDefinition? constructor = null;
+            var classBody = _body.Body;
+            for (var i = 0; i < classBody.Count; ++i)
+            {
+                if (classBody[i].Kind == PropertyKind.Constructor)
+                {
+                    constructor = (MethodDefinition) classBody[i];
+                    break;
+                }
+            }
+
+            constructor ??= _superClass != null
+                ? _superConstructor
+                : _emptyConstructor;
+
+            engine.UpdateLexicalEnvironment(classScope);
+
+            ScriptFunctionInstance F;
+            try
+            {
+                var constructorInfo = constructor.DefineMethod(proto, constructorParent);
+                F = constructorInfo.Closure;
+                if (_className is not null)
+                {
+                    F.SetFunctionName(_className);
+                }
+
+                F.MakeConstructor(false, proto);
+                F._constructorKind = _superClass is null ? ConstructorKind.Base : ConstructorKind.Derived;
+                F.MakeClassConstructor();
+                proto.CreateMethodProperty(CommonProperties.Constructor, F);
+
+                foreach (var classProperty in _body.Body)
+                {
+                    if (classProperty is not MethodDefinition m || m.Kind == PropertyKind.Constructor)
+                    {
+                        continue;
+                    }
+
+                    var target = !m.Static ? proto : F;
+                    PropertyDefinitionEvaluation(engine, target, m);
+                }
+            }
+            finally
+            {
+                engine.UpdateLexicalEnvironment(env);
+            }
+
+            if (_className is not null)
+            {
+                classScope._record.InitializeBinding(_className, F);
+            }
+
+            return F;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-method-definitions-runtime-semantics-propertydefinitionevaluation
+        /// </summary>
+        private static void PropertyDefinitionEvaluation(
+            Engine engine,
+            ObjectInstance obj,
+            MethodDefinition method)
+        {
+            if (method.Kind != PropertyKind.Get && method.Kind != PropertyKind.Set)
+            {
+                var methodDef = method.DefineMethod(obj);
+                methodDef.Closure.SetFunctionName(methodDef.Key);
+                var desc = new PropertyDescriptor(methodDef.Closure, PropertyFlag.NonEnumerable);
+                obj.DefinePropertyOrThrow(methodDef.Key, desc);
+            }
+            else
+            {
+                var propKey = TypeConverter.ToPropertyKey(method.GetKey(engine));
+                var function = method.Value as IFunction ?? ExceptionHelper.ThrowSyntaxError<IFunction>(obj.Engine);
+
+                var closure = new ScriptFunctionInstance(
+                    obj.Engine,
+                    function,
+                    obj.Engine.ExecutionContext.LexicalEnvironment,
+                    true);
+                closure.SetFunctionName(propKey, method.Kind == PropertyKind.Get ? "get" : "set");
+                closure.MakeMethod(obj);
+
+                var propDesc = new GetSetPropertyDescriptor(
+                    method.Kind == PropertyKind.Get ? closure : null,
+                    method.Kind == PropertyKind.Set ? closure : null,
+                    PropertyFlag.Configurable);
+
+                obj.DefinePropertyOrThrow(propKey, propDesc);
+            }
+        }
+    }
+}

+ 8 - 0
Jint/Native/Function/ConstructorKind.cs

@@ -0,0 +1,8 @@
+namespace Jint.Native.Function
+{
+    internal enum ConstructorKind
+    {
+        Base,
+        Derived
+    }
+}

+ 35 - 2
Jint/Native/Function/EvalFunctionInstance.cs

@@ -21,19 +21,39 @@ namespace Jint.Native.Function
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
-            return Call(thisObject, arguments, false);
+            return PerformEval(arguments, false);
         }
 
         /// <summary>
         /// https://tc39.es/ecma262/#sec-performeval
         /// </summary>
-        public JsValue Call(JsValue thisObject, JsValue[] arguments, bool direct)
+        public JsValue PerformEval(JsValue[] arguments, bool direct)
         {
             if (!(arguments.At(0) is JsString x))
             {
                 return arguments.At(0);
             }
 
+            var inFunction = false;
+            var inMethod = false;
+            var inDerivedConstructor = false;
+
+            if (direct)
+            {
+                var thisEnvRec = _engine.GetThisEnvironment();
+                if (thisEnvRec is FunctionEnvironmentRecord functionEnvironmentRecord)
+                {
+                    var F = functionEnvironmentRecord._functionObject;
+                    inFunction = true;
+                    inMethod = thisEnvRec.HasSuperBinding();
+                    
+                    if (F._constructorKind == ConstructorKind.Derived)
+                    {
+                        inDerivedConstructor = true;
+                    }
+                }
+            }
+
             var parser = new JavaScriptParser(x.ToString(), ParserOptions);
             Script script;
             try
@@ -53,6 +73,19 @@ namespace Jint.Native.Function
                 return Undefined;
             }
 
+            if (!inFunction)
+            {
+                // if body Contains NewTarget, throw a SyntaxError exception.
+            } 
+            if (!inMethod)
+            {
+                // if body Contains SuperProperty, throw a SyntaxError exception.
+            } 
+            if (!inDerivedConstructor)
+            {
+                // if body Contains SuperCall, throw a SyntaxError exception.
+            } 
+
             var strictEval = script.Strict || _engine._isStrict;
             var ctx = _engine.ExecutionContext;
 

+ 4 - 39
Jint/Native/Function/FunctionConstructor.cs

@@ -113,6 +113,8 @@ namespace Jint.Native.Function
                 function,
                 _engine.GlobalEnvironment,
                 function.Strict);
+            
+            functionObject.MakeConstructor();
 
             // the function is not actually a named function
             functionObject.SetFunctionName(_functionNameAnonymous, force: true);
@@ -132,47 +134,10 @@ namespace Jint.Native.Function
                 functionDeclaration,
                 env, 
                 functionDeclaration.Strict || _engine._isStrict);
+            
+            functionObject.MakeConstructor();
 
             return functionObject;
         }
-
-        public object Apply(JsValue thisObject, JsValue[] arguments)
-        {
-            if (arguments.Length != 2)
-            {
-                ExceptionHelper.ThrowArgumentException("Apply has to be called with two arguments.");
-            }
-
-            var func = thisObject.TryCast<ICallable>();
-            var thisArg = arguments[0];
-            var argArray = arguments[1];
-
-            if (func is null)
-            {
-                return ExceptionHelper.ThrowTypeError<object>(Engine);
-            }
-
-            if (argArray.IsNullOrUndefined())
-            {
-                return func.Call(thisArg, Arguments.Empty);
-            }
-
-            var argArrayObj = argArray.TryCast<ObjectInstance>();
-            if (ReferenceEquals(argArrayObj, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
-            var len = argArrayObj.Get(CommonProperties.Length, argArrayObj);
-            var n = TypeConverter.ToUint32(len);
-            var argList = new JsValue[n];
-            for (var index = 0; index < n; index++)
-            {
-                var indexName = TypeConverter.ToString(index);
-                var nextArg = argArrayObj.Get(JsString.Create(indexName), argArrayObj);
-                argList[index] = nextArg;
-            }
-            return func.Call(thisArg, argList);
-        }
     }
 }

+ 105 - 16
Jint/Native/Function/FunctionInstance.cs

@@ -1,5 +1,7 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
 using System.Runtime.CompilerServices;
+using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -10,21 +12,16 @@ namespace Jint.Native.Function
 {
     public abstract class FunctionInstance : ObjectInstance, ICallable
     {
-        internal enum FunctionThisMode
-        {
-            Lexical,
-            Strict,
-            Global
-        }
-
-        protected internal PropertyDescriptor _prototypeDescriptor;
+        protected PropertyDescriptor _prototypeDescriptor;
 
         protected internal PropertyDescriptor _length;
-        internal PropertyDescriptor _nameDescriptor;
+        private PropertyDescriptor _nameDescriptor;
 
         protected internal LexicalEnvironment _environment;
         internal readonly JintFunctionDefinition _functionDefinition;
         internal readonly FunctionThisMode _thisMode;
+        internal JsValue _homeObject = Undefined;
+        internal ConstructorKind _constructorKind = ConstructorKind.Base;
 
         internal FunctionInstance(
             Engine engine,
@@ -44,7 +41,7 @@ namespace Jint.Native.Function
             ObjectClass objectClass = ObjectClass.Function)
             : base(engine, objectClass)
         {
-            if (!(name is null))
+            if (name is not null)
             {
                 _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable);
             }
@@ -57,6 +54,9 @@ namespace Jint.Native.Function
             : this(engine, name, FunctionThisMode.Global, ObjectClass.Function)
         {
         }
+        
+        // for example RavenDB wants to inspect this
+        public IFunction FunctionDeclaration => _functionDefinition.Function;
 
         /// <summary>
         /// Executed when a function object is used as a function
@@ -68,6 +68,8 @@ namespace Jint.Native.Function
 
         public bool Strict => _thisMode == FunctionThisMode.Strict;
 
+        internal override bool IsConstructor => this is IConstructor;
+
         public virtual bool HasInstance(JsValue v)
         {
             if (!(v is ObjectInstance o))
@@ -234,15 +236,23 @@ namespace Jint.Native.Function
             _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable);
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor
+        /// </summary>
+        /// <remarks>
+        /// Uses separate builder to get correct type with state support to prevent allocations.
+        /// </remarks>
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal ObjectInstance OrdinaryCreateFromConstructor(JsValue constructor, ObjectInstance intrinsicDefaultProto)
+        internal T OrdinaryCreateFromConstructor<T>(
+            JsValue constructor,
+            ObjectInstance intrinsicDefaultProto,
+            Func<Engine, JsValue, T> objectCreator,
+            JsValue state = null) where T : ObjectInstance
         {
             var proto = GetPrototypeFromConstructor(constructor, intrinsicDefaultProto);
 
-            var obj = new ObjectInstance(_engine)
-            {
-                _prototype = proto
-            };
+            var obj = objectCreator(_engine, state);
+            obj._prototype = proto;
             return obj;
         }
 
@@ -255,6 +265,85 @@ namespace Jint.Native.Function
             //    Set proto to realm's intrinsic object named intrinsicDefaultProto.
             return proto ?? intrinsicDefaultProto;
         }
+        
+        internal void MakeMethod(ObjectInstance homeObject)
+        {
+            _homeObject = homeObject;
+        }
+        
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-ordinarycallbindthis
+        /// </summary>
+        protected void OrdinaryCallBindThis(ExecutionContext calleeContext, JsValue thisArgument)
+        {
+            var thisMode = _thisMode;
+            if (thisMode == FunctionThisMode.Lexical)
+            {
+                return;
+            }
+
+            // Let calleeRealm be F.[[Realm]].
+
+            var localEnv = (FunctionEnvironmentRecord) calleeContext.LexicalEnvironment._record;
+            
+            JsValue thisValue;
+            if (_thisMode == FunctionThisMode.Strict)
+            {
+                thisValue = thisArgument;
+            }
+            else
+            {
+                if (thisArgument.IsNullOrUndefined())
+                {
+                    // Let globalEnv be calleeRealm.[[GlobalEnv]].
+                    var globalEnv = _engine.GlobalEnvironment;
+                    var globalEnvRec = (GlobalEnvironmentRecord) globalEnv._record;
+                    thisValue = globalEnvRec.GlobalThisValue;
+                }
+                else
+                {
+                    thisValue = TypeConverter.ToObject(_engine, thisArgument);
+                }
+            }
+
+            localEnv.BindThisValue(thisValue);
+        }
+
+        protected Completion OrdinaryCallEvaluateBody(
+            JsValue[] arguments,
+            ExecutionContext calleeContext)
+        {
+            var argumentsInstance = _engine.FunctionDeclarationInstantiation(
+                functionInstance: this,
+                arguments,
+                calleeContext.LexicalEnvironment);
+
+            var result = _functionDefinition.Execute();
+            var value = result.GetValueOrDefault().Clone();
+
+            argumentsInstance?.FunctionWasCalled();
+
+            return new Completion(result.Type, value, result.Identifier, result.Location);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-prepareforordinarycall
+        /// </summary>
+        protected ExecutionContext PrepareForOrdinaryCall(JsValue newTarget)
+        {
+            // ** PrepareForOrdinaryCall **
+            // var callerContext = _engine.ExecutionContext;
+            // Let calleeRealm be F.[[Realm]].
+            // Set the Realm of calleeContext to calleeRealm.
+            // Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]].
+            var calleeContext = LexicalEnvironment.NewFunctionEnvironment(_engine, this, newTarget);
+            // If callerContext is not already suspended, suspend callerContext.
+            // Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
+            // NOTE: Any exception objects produced after this point are associated with calleeRealm.
+            // Return calleeContext.
+
+            return _engine.EnterExecutionContext(calleeContext, calleeContext);
+        }
 
         public override string ToString()
         {

+ 18 - 8
Jint/Native/Function/FunctionPrototype.cs

@@ -65,19 +65,13 @@ namespace Jint.Native.Function
 
         private JsValue Bind(JsValue thisObj, JsValue[] arguments)
         {
-            if (!(thisObj is ICallable))
+            if (thisObj is not ICallable)
             {
                 ExceptionHelper.ThrowTypeError(Engine, "Bind must be called on a function");
             }
 
             var thisArg = arguments.At(0);
-            var f = new BindFunctionInstance(Engine)
-            {
-                TargetFunction = thisObj,
-                BoundThis = thisObj is ArrowFunctionInstance ? Undefined : thisArg,
-                BoundArgs = arguments.Skip(1),
-                _prototype = Engine.Function.PrototypeObject,
-            };
+            var f = BoundFunctionCreate((ObjectInstance) thisObj, thisArg, arguments.Skip(1));
 
             JsNumber l;
             var targetHasLength = thisObj.HasOwnProperty(CommonProperties.Length);
@@ -114,6 +108,22 @@ namespace Jint.Native.Function
             return f;
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-boundfunctioncreate
+        /// </summary>
+        private FunctionInstance BoundFunctionCreate(ObjectInstance targetFunction, JsValue boundThis, JsValue[] boundArgs)
+        {
+            var proto = targetFunction.GetPrototypeOf();
+            var obj = new BindFunctionInstance(Engine)
+            {
+                _prototype = proto,
+                TargetFunction = targetFunction,
+                BoundThis = boundThis,
+                BoundArgs = boundArgs
+            };
+            return obj;
+        }
+
         private JsValue ToString(JsValue thisObj, JsValue[] arguments)
         {
             if (thisObj.IsObject() && thisObj.IsCallable)

+ 9 - 0
Jint/Native/Function/FunctionThisMode.cs

@@ -0,0 +1,9 @@
+namespace Jint.Native.Function
+{
+    internal enum FunctionThisMode
+    {
+        Lexical,
+        Strict,
+        Global
+    }
+}

+ 91 - 69
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -11,7 +11,7 @@ namespace Jint.Native.Function
 {
     public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
     {
-        private readonly JintFunctionDefinition _function;
+        private bool _isClassConstructor;
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
@@ -20,8 +20,9 @@ namespace Jint.Native.Function
             Engine engine,
             IFunction functionDeclaration,
             LexicalEnvironment scope,
-            bool strict)
-            : this(engine, new JintFunctionDefinition(engine, functionDeclaration), scope, strict ? FunctionThisMode.Strict : FunctionThisMode.Global)
+            bool strict,
+            ObjectInstance proto = null)
+            : this(engine, new JintFunctionDefinition(engine, functionDeclaration), scope, strict ? FunctionThisMode.Strict : FunctionThisMode.Global, proto)
         {
         }
 
@@ -29,74 +30,34 @@ namespace Jint.Native.Function
             Engine engine,
             JintFunctionDefinition function,
             LexicalEnvironment scope,
-            FunctionThisMode thisMode)
+            FunctionThisMode thisMode,
+            ObjectInstance proto = null)
             : base(engine, function, scope, thisMode)
         {
-            _function = function;
-
-            _prototype = _engine.Function.PrototypeObject;
-
+            _prototype = proto ?? _engine.Function.PrototypeObject;
             _length = new LazyPropertyDescriptor(() => JsNumber.Create(function.Initialize(engine, this).Length), PropertyFlag.Configurable);
 
-            var proto = new ObjectInstanceWithConstructor(engine, this)
-            {
-                _prototype = _engine.Object.PrototypeObject
-            };
-
-            _prototypeDescriptor = new PropertyDescriptor(proto, PropertyFlag.OnlyWritable);
-
-            if (!function.Strict && !engine._isStrict)
+            if (!function.Strict && !engine._isStrict && function.Function is not ArrowFunctionExpression)
             {
                 DefineOwnProperty(CommonProperties.Arguments, engine._callerCalleeArgumentsThrowerConfigurable);
                 DefineOwnProperty(CommonProperties.Caller, new PropertyDescriptor(Undefined, PropertyFlag.Configurable));
             }
         }
 
-        // for example RavenDB wants to inspect this
-        public IFunction FunctionDeclaration => _function.Function;
-
         /// <summary>
         /// https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist
         /// </summary>
         public override JsValue Call(JsValue thisArgument, JsValue[] arguments)
         {
-            // ** PrepareForOrdinaryCall **
-            // var callerContext = _engine.ExecutionContext;
-            // Let calleeRealm be F.[[Realm]].
-            // Set the Realm of calleeContext to calleeRealm.
-            // Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]].
-            var localEnv = LexicalEnvironment.NewFunctionEnvironment(_engine, this, Undefined);
-            // If callerContext is not already suspended, suspend callerContext.
-            // Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
-            // NOTE: Any exception objects produced after this point are associated with calleeRealm.
-            // Return calleeContext.
-
-            _engine.EnterExecutionContext(localEnv, localEnv);
-
-            // ** OrdinaryCallBindThis **
-            
-            JsValue thisValue;
-            if (_thisMode == FunctionThisMode.Strict)
+            if (_isClassConstructor)
             {
-                thisValue = thisArgument;
-            }
-            else
-            {
-                if (thisArgument.IsNullOrUndefined())
-                {
-                    var globalEnv = _engine.GlobalEnvironment;
-                    var globalEnvRec = (GlobalEnvironmentRecord) globalEnv._record;
-                    thisValue = globalEnvRec.GlobalThisValue;
-                }
-                else
-                {
-                    thisValue = TypeConverter.ToObject(_engine, thisArgument);
-                }
+                ExceptionHelper.ThrowTypeError(_engine, $"Class constructor {_functionDefinition.Name} cannot be invoked without 'new'");
             }
 
-            var envRec = (FunctionEnvironmentRecord) localEnv._record;
-            envRec.BindThisValue(thisValue);
-            
+            var calleeContext = PrepareForOrdinaryCall(Undefined);
+
+            OrdinaryCallBindThis(calleeContext, thisArgument);
+
             // actual call
 
             var strict = _thisMode == FunctionThisMode.Strict || _engine._isStrict;
@@ -104,23 +65,16 @@ namespace Jint.Native.Function
             {
                 try
                 {
-                    var argumentsInstance = _engine.FunctionDeclarationInstantiation(
-                        functionInstance: this,
-                        arguments,
-                        localEnv);
-
-                    var result = _function.Execute();
-                    var value = result.GetValueOrDefault().Clone();
-                    argumentsInstance?.FunctionWasCalled();
+                    var result = OrdinaryCallEvaluateBody(arguments, calleeContext);
 
                     if (result.Type == CompletionType.Throw)
                     {
-                        ExceptionHelper.ThrowJavaScriptException(_engine, value, result);
+                        ExceptionHelper.ThrowJavaScriptException(_engine, result.Value, result);
                     }
 
                     if (result.Type == CompletionType.Return)
                     {
-                        return value;
+                        return result.Value;
                     }
                 }
                 finally
@@ -132,22 +86,90 @@ namespace Jint.Native.Function
             }
         }
 
+        internal override bool IsConstructor =>
+            (_homeObject.IsUndefined() || _isClassConstructor) 
+            && _functionDefinition?.Function is not ArrowFunctionExpression;
+
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2
+        /// https://tc39.es/ecma262/#sec-ecmascript-function-objects-construct-argumentslist-newtarget
         /// </summary>
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
-            var thisArgument = OrdinaryCreateFromConstructor(TypeConverter.ToObject(_engine, newTarget), _engine.Object.PrototypeObject);
+            var kind = _constructorKind;
 
-            var result = Call(thisArgument, arguments).TryCast<ObjectInstance>();
-            if (!ReferenceEquals(result, null))
+            var thisArgument = Undefined;
+            
+            if (kind == ConstructorKind.Base)
+            {
+                thisArgument = OrdinaryCreateFromConstructor(newTarget, _engine.Object.PrototypeObject, static (engine, _) => new ObjectInstance(engine));
+            }
+
+            var calleeContext = PrepareForOrdinaryCall(newTarget);
+
+            if (kind == ConstructorKind.Base)
             {
-                return result;
+                OrdinaryCallBindThis(calleeContext, thisArgument);
             }
 
-            return thisArgument;
+            var constructorEnv = (FunctionEnvironmentRecord) calleeContext.LexicalEnvironment._record;
+            
+            var strict = _thisMode == FunctionThisMode.Strict || _engine._isStrict;
+            using (new StrictModeScope(strict, force: true))
+            {
+                try
+                {
+                    var result = OrdinaryCallEvaluateBody(arguments, calleeContext);
+
+                    if (result.Type == CompletionType.Return)
+                    {
+                        if (result.Value is ObjectInstance oi)
+                        {
+                            return oi;
+                        }
+
+                        if (kind == ConstructorKind.Base)
+                        {
+                            return (ObjectInstance) thisArgument!;
+                        }
+
+                        if (!result.Value.IsUndefined())
+                        {
+                            ExceptionHelper.ThrowTypeError(_engine);
+                        }
+                    }
+                    else if (result.Type == CompletionType.Throw)
+                    {
+                        ExceptionHelper.ThrowJavaScriptException(_engine, result.Value, result);
+                    }
+                }
+                finally
+                {
+                    _engine.LeaveExecutionContext();
+                }
+            }
+
+            return (ObjectInstance) constructorEnv.GetThisBinding();
         }
+        
+        internal void MakeConstructor(bool writableProperty = true, ObjectInstance prototype = null)
+        {
+            _constructorKind = ConstructorKind.Base;
+            if (prototype is null)
+            {
+                prototype = new ObjectInstanceWithConstructor(_engine, this)
+                {
+                    _prototype = _engine.Object.PrototypeObject
+                };
+            }
 
+            _prototypeDescriptor = new PropertyDescriptor(prototype, writableProperty, enumerable: false, configurable: false);
+        }        
+
+        internal void MakeClassConstructor()
+        {
+            _isClassConstructor = true;
+        }
+        
         private class ObjectInstanceWithConstructor : ObjectInstance
         {
             private PropertyDescriptor _constructor;

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

@@ -329,15 +329,18 @@ namespace Jint.Native.Iterator
 
             public void Close(CompletionType completion)
             {
-                if (!_target.TryGetValue(CommonProperties.Return, out var func))
+                if (!_target.TryGetValue(CommonProperties.Return, out var func)
+                    || func.IsNullOrUndefined())
                 {
                     return;
                 }
 
+                var callable = func as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(_target.Engine, func + " is not a function");
+
                 var innerResult = Undefined;
                 try
                 {
-                    innerResult = ((ICallable) func).Call(_target, Arguments.Empty);
+                    innerResult = callable.Call(_target, Arguments.Empty);
                 }
                 catch
                 {

+ 1 - 1
Jint/Native/JsNumber.cs

@@ -166,7 +166,7 @@ namespace Jint.Native
 
         public override string ToString()
         {
-            return EsprimaExtensions.DoubleToString(_value);
+            return TypeConverter.ToString(_value);
         }
 
         public override bool Equals(JsValue obj)

+ 1 - 1
Jint/Native/JsValue.cs

@@ -271,7 +271,7 @@ namespace Jint.Native
                 : (Types) (_type & ~InternalTypes.InternalFlags);
         }
 
-        internal virtual bool IsConstructor => this is IConstructor;
+        internal virtual bool IsConstructor => false;
 
         /// <summary>
         /// Creates a valid <see cref="JsValue"/> instance from any <see cref="Object"/> instance

+ 7 - 3
Jint/Native/Map/MapConstructor.cs

@@ -62,13 +62,17 @@ namespace Jint.Native.Map
             return Construct(arguments, thisObject);
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-map-iterable
+        /// </summary>
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
-            var map = new MapInstance(Engine)
+            if (newTarget.IsUndefined())
             {
-                _prototype = PrototypeObject
-            };
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
 
+            var map = OrdinaryCreateFromConstructor(newTarget, PrototypeObject, static (engine, _) => new MapInstance(engine));
             if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())
             {
                 var adder = map.Get("set");

+ 12 - 9
Jint/Native/Number/NumberConstructor.cs

@@ -131,22 +131,25 @@ namespace Jint.Native.Number
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.7.2.1
+        /// https://tc39.es/ecma262/#sec-number-constructor-number-value
         /// </summary>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
-            return Construct(arguments.Length > 0 ? TypeConverter.ToNumber(arguments[0]) : 0);
-        }
+            var value = arguments.Length > 0 
+                ? JsNumber.Create(TypeConverter.ToNumber(arguments[0]))
+                : JsNumber.PositiveZero;
 
-        public NumberPrototype PrototypeObject { get; private set; }
+            if (newTarget.IsUndefined())
+            {
+                return Construct(value);
+            }
 
-        public NumberInstance Construct(double value)
-        {
-            return Construct(JsNumber.Create(value));
+            var o = OrdinaryCreateFromConstructor(newTarget, PrototypeObject, static (engine, state) => new NumberInstance(engine, (JsNumber) state), value);
+            return o;
         }
 
+        public NumberPrototype PrototypeObject { get; private set; }
+
         public NumberInstance Construct(JsNumber value)
         {
             var instance = new NumberInstance(Engine)

+ 6 - 0
Jint/Native/Number/NumberInstance.cs

@@ -14,6 +14,12 @@ namespace Jint.Native.Number
         {
         }
 
+        public NumberInstance(Engine engine, JsNumber value)
+            : base(engine, ObjectClass.Number)
+        {
+            NumberData = value;
+        }
+
         Types IPrimitiveInstance.Type => Types.Number;
 
         JsValue IPrimitiveInstance.PrimitiveValue => NumberData;

+ 6 - 1
Jint/Native/Object/ObjectConstructor.cs

@@ -41,7 +41,7 @@ namespace Jint.Native.Object
                 ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Entries, 1, lengthFlags), propertyFlags),
                 ["fromEntries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "fromEntries", FromEntries, 1, lengthFlags), propertyFlags),
                 ["getPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getPrototypeOf", GetPrototypeOf, 1), propertyFlags),
-                ["getOwnPropertyDescriptor"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyDescriptor", GetOwnPropertyDescriptor, 2), propertyFlags),
+                ["getOwnPropertyDescriptor"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyDescriptor", GetOwnPropertyDescriptor, 2, lengthFlags), propertyFlags),
                 ["getOwnPropertyDescriptors"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyDescriptors", GetOwnPropertyDescriptors, 1, lengthFlags), propertyFlags),
                 ["getOwnPropertyNames"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyNames", GetOwnPropertyNames, 1), propertyFlags),
                 ["getOwnPropertySymbols"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertySymbols", GetOwnPropertySymbols, 1, lengthFlags), propertyFlags),
@@ -153,6 +153,11 @@ namespace Jint.Native.Object
 
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
+            if (!ReferenceEquals(this, newTarget) && !newTarget.IsUndefined())
+            {
+                return OrdinaryCreateFromConstructor(newTarget, _engine.Object.PrototypeObject, (engine, state) => new ObjectInstance(engine));
+            }
+            
             if (arguments.Length > 0)
             {
                 var value = arguments[0];

+ 15 - 6
Jint/Native/Object/ObjectInstance.cs

@@ -342,7 +342,7 @@ namespace Jint.Native.Object
             }
 
             var functionInstance = (FunctionInstance) getter;
-            return functionInstance._engine.Call(functionInstance, thisObject, Arguments.Empty, location: null);
+            return functionInstance._engine.Call(functionInstance, thisObject, Arguments.Empty, expression: null);
         }
 
         /// <summary>
@@ -501,7 +501,7 @@ namespace Jint.Native.Object
             }
 
             var functionInstance = (FunctionInstance) setter;
-            _engine.Call(functionInstance, receiver, new[] { value }, location: null);
+            _engine.Call(functionInstance, receiver, new[] { value }, expression: null);
 
             return true;
         }
@@ -622,7 +622,7 @@ namespace Jint.Native.Object
         {
             if (!DefineOwnProperty(property, desc))
             {
-                ExceptionHelper.ThrowTypeError(_engine);
+                ExceptionHelper.ThrowTypeError(_engine, "Cannot redefine property: " + property);
             }
 
             return true;
@@ -1103,7 +1103,7 @@ namespace Jint.Native.Object
             return JsBoolean.True;
         }
 
-        protected virtual ObjectInstance GetPrototypeOf()
+        protected internal virtual ObjectInstance GetPrototypeOf()
         {
             return _prototype;
         }
@@ -1158,6 +1158,15 @@ namespace Jint.Native.Object
             return true;
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-createmethodproperty
+        /// </summary>
+        internal virtual bool CreateMethodProperty(JsValue p, JsValue v)
+        {
+            var newDesc = new PropertyDescriptor(v, PropertyFlag.NonEnumerable);
+            return DefineOwnProperty(p, newDesc);
+        }
+        
         /// <summary>
         /// https://tc39.es/ecma262/#sec-createdatapropertyorthrow
         /// </summary>
@@ -1199,13 +1208,13 @@ namespace Jint.Native.Object
 
         internal void CopyDataProperties(
             ObjectInstance target,
-            HashSet<JsValue> processedProperties)
+            HashSet<JsValue> excludedItems)
         {
             var keys = GetOwnPropertyKeys();
             for (var i = 0; i < keys.Count; i++)
             {
                 var key = keys[i];
-                if (processedProperties == null || !processedProperties.Contains(key))
+                if (excludedItems == null || !excludedItems.Contains(key))
                 {
                     var desc = GetOwnProperty(key);
                     if (desc.Enumerable)

+ 1 - 4
Jint/Native/Object/ObjectPrototype.cs

@@ -96,11 +96,8 @@ namespace Jint.Native.Object
         }
 
         /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.4.2
+        /// https://tc39.es/ecma262/#sec-object.prototype.tostring
         /// </summary>
-        /// <param name="thisObject"></param>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
         public JsValue ToObjectString(JsValue thisObject, JsValue[] arguments)
         {
             if (thisObject.IsUndefined())

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

@@ -54,7 +54,7 @@ namespace Jint.Native.Proxy
             SetProperties(properties);
         }
 
-        protected override ObjectInstance GetPrototypeOf()
+        protected internal override ObjectInstance GetPrototypeOf()
         {
             return _engine.Function.Prototype;
         }

+ 11 - 14
Jint/Native/Proxy/ProxyInstance.cs

@@ -46,7 +46,12 @@ namespace Jint.Native.Proxy
                 return result;
             }
 
-            return ((ICallable) _target).Call(thisObject, arguments);
+            if (!(_target is ICallable callable))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, _target + " is not a function");
+            }
+
+            return callable.Call(thisObject, arguments);
         }
 
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
@@ -77,7 +82,9 @@ namespace Jint.Native.Proxy
         }
 
         internal override bool IsConstructor => 
-            _handler.TryGetValue(TrapConstruct, out var handlerFunction) && handlerFunction is IConstructor;
+            _handler != null
+            && _handler.TryGetValue(TrapConstruct, out var handlerFunction) 
+            && handlerFunction is IConstructor;
 
         public override JsValue Get(JsValue property, JsValue receiver)
         {
@@ -137,17 +144,7 @@ namespace Jint.Native.Proxy
                 
             }
 
-            if (extensibleTarget && targetNonconfigurableKeys.Count == 0)
-            {
-                return trapResult;
-            }
-            
-            var uncheckedResultKeys = new HashSet<JsValue>();
-            foreach (var jsValue in trapResult)
-            {
-                uncheckedResultKeys.Add(jsValue);
-            }
-
+            var uncheckedResultKeys = new HashSet<JsValue>(trapResult);
             for (var i = 0; i < targetNonconfigurableKeys.Count; i++)
             {
                 var key = targetNonconfigurableKeys[i];
@@ -392,7 +389,7 @@ namespace Jint.Native.Proxy
             }
         }
 
-        protected override ObjectInstance GetPrototypeOf()
+        protected internal override ObjectInstance GetPrototypeOf()
         {
             if (!TryCallHandler(TrapGetProtoTypeOf, new [] { _target }, out var handlerProto ))
             {

+ 4 - 31
Jint/Native/RegExp/RegExpConstructor.cs

@@ -97,7 +97,7 @@ namespace Jint.Native.RegExp
                 f = flags;
             }
 
-            var r = RegExpAlloc();
+            var r = RegExpAlloc(newTarget);
             return RegExpInitialize(r, p, f);
         }
 
@@ -137,40 +137,13 @@ namespace Jint.Native.RegExp
             return r;
         }
 
-        private RegExpInstance RegExpAlloc()
+        private RegExpInstance RegExpAlloc(JsValue newTarget)
         {
-            var r = new RegExpInstance(Engine)
-            {
-                _prototype = PrototypeObject
-            };
-            return r;
-        }
-
-        public RegExpInstance Construct(string regExp, Engine engine)
-        {
-            var r = new RegExpInstance(Engine);
-            r._prototype = PrototypeObject;
-
-            var scanner = new Scanner(regExp, new ParserOptions { AdaptRegexp = true });
-            var body = (string)scanner.ScanRegExpBody().Value;
-            var flags = (string)scanner.ScanRegExpFlags().Value;
-            r.Value = scanner.TestRegExp(body, flags);
-
-            var timeout = engine.Options._RegexTimeoutInterval;
-            if (timeout.Ticks > 0)
-            {
-                r.Value = new Regex(r.Value.ToString(), r.Value.Options);
-            }
-
-            r.Flags = flags;
-            r.Source = string.IsNullOrEmpty(body) ? "(?:)" : body;
-
-            RegExpInitialize(r);
-            
+            var r = OrdinaryCreateFromConstructor(newTarget, PrototypeObject, static(engine, value) => new RegExpInstance(engine));
             return r;
         }
 
-        public RegExpInstance Construct(Regex regExp, string flags, Engine engine)
+        public RegExpInstance Construct(Regex regExp, string flags)
         {
             var r = new RegExpInstance(Engine);
             r._prototype = PrototypeObject;

+ 8 - 3
Jint/Native/Set/SetConstructor.cs

@@ -62,12 +62,17 @@ namespace Jint.Native.Set
             return Construct(arguments, thisObject);
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-set-iterable
+        /// </summary>
         public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
-            var set = new SetInstance(Engine)
+            if (newTarget.IsUndefined())
             {
-                _prototype = PrototypeObject
-            };
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            var set = OrdinaryCreateFromConstructor(newTarget, PrototypeObject, static (engine, _) => new SetInstance(engine));
             if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())
             {
                 var adderValue = set.Get("add");

+ 1 - 0
Jint/Native/Symbol/GlobalSymbolRegistry.cs

@@ -4,6 +4,7 @@ namespace Jint.Native.Symbol
 {
     public class GlobalSymbolRegistry
     {
+        public static readonly JsSymbol AsyncIterator = new JsSymbol("Symbol.asyncIterator");
         public static readonly JsSymbol HasInstance = new JsSymbol("Symbol.hasInstance");
         public static readonly JsSymbol IsConcatSpreadable = new JsSymbol("Symbol.isConcatSpreadable");
         public static readonly JsSymbol Iterator = new JsSymbol("Symbol.iterator");

+ 2 - 1
Jint/Native/Symbol/SymbolConstructor.cs

@@ -58,7 +58,8 @@ namespace Jint.Native.Symbol
                 ["split"] = new PropertyDescriptor(GlobalSymbolRegistry.Split, propertyFlags),
                 ["toPrimitive"] = new PropertyDescriptor(GlobalSymbolRegistry.ToPrimitive, propertyFlags),
                 ["toStringTag"] = new PropertyDescriptor(GlobalSymbolRegistry.ToStringTag, propertyFlags),
-                ["unscopables"] = new PropertyDescriptor(GlobalSymbolRegistry.Unscopables, propertyFlags)
+                ["unscopables"] = new PropertyDescriptor(GlobalSymbolRegistry.Unscopables, propertyFlags),
+                ["asyncIterator"] = new PropertyDescriptor(GlobalSymbolRegistry.AsyncIterator, propertyFlags)
             };
             SetProperties(properties);
         }

+ 3 - 3
Jint/Pooling/ReferencePool.cs

@@ -18,12 +18,12 @@ namespace Jint.Pooling
 
         private static Reference Factory()
         {
-            return new Reference(JsValue.Undefined, JsString.Empty, false);
+            return new Reference(JsValue.Undefined, JsString.Empty, false, null);
         }
 
-        public Reference Rent(JsValue baseValue, JsValue name, bool strict)
+        public Reference Rent(JsValue baseValue, JsValue name, bool strict, JsValue thisValue)
         {
-            return _pool.Allocate().Reassign(baseValue, name, strict);
+            return _pool.Allocate().Reassign(baseValue, name, strict, thisValue);
         }
 
         public void Return(Reference reference)

+ 25 - 5
Jint/Runtime/CallStack/CallStackElement.cs

@@ -1,5 +1,9 @@
-using Esprima;
+#nullable enable
+
+using Esprima;
+using Esprima.Ast;
 using Jint.Native.Function;
+using Jint.Runtime.Interpreter.Expressions;
 
 namespace Jint.Runtime.CallStack
 {
@@ -7,18 +11,34 @@ namespace Jint.Runtime.CallStack
     {
         public CallStackElement(
             FunctionInstance function,
-            Location? location)
+            JintExpression expression)
         {
             Function = function;
-            Location = location;
+            Expression = expression;
         }
 
         public readonly FunctionInstance Function;
-        public readonly Location? Location;
+        public readonly JintExpression? Expression;
+
+        public Location Location =>
+            Expression?._expression.Location ?? ((Node?) Function._functionDefinition?.Function)?.Location ?? default;
+
+        public NodeList<Expression>? Arguments =>
+            Function._functionDefinition?.Function.Params;
 
         public override string ToString()
         {
-            return TypeConverter.ToString(Function?.Get(CommonProperties.Name));
+            var name = TypeConverter.ToString(Function.Get(CommonProperties.Name));
+
+            if (string.IsNullOrWhiteSpace(name))
+            {
+                if (Expression is not null)
+                {
+                    name = JintExpression.ToString(Expression._expression);
+                }
+            }
+            
+            return name ?? "(anonymous)";
         }
     }
 }

+ 21 - 20
Jint/Runtime/CallStack/JintCallStack.cs

@@ -77,34 +77,37 @@ namespace Jint.Runtime.CallStack
                 StringBuilder sb,
                 string shortDescription,
                 Location loc,
-                in NodeList<Expression> arguments)
+                in NodeList<Expression>? arguments)
             {
                 sb
-                    .Append("    at ")
-                    .Append(shortDescription);
+                    .Append("   at");
 
-                if (arguments.Count > 0)
+                if (!string.IsNullOrWhiteSpace(shortDescription))
                 {
-                    sb.Append(" (");
+                    sb
+                        .Append(" ")
+                        .Append(shortDescription);
                 }
 
-                for (var index = 0; index < arguments.Count; index++)
+                if (arguments is not null)
                 {
-                    if (index != 0)
+                    // it's a function
+                    sb.Append(" (");
+                    for (var index = 0; index < arguments.Value.Count; index++)
                     {
-                        sb.Append(", ");
-                    }
+                        if (index != 0)
+                        {
+                            sb.Append(", ");
+                        }
 
-                    var arg = arguments[index];
-                    sb.Append(GetPropertyKey(arg));
-                }
-
-                if (arguments.Count > 0)
-                {
-                    sb.Append(") ");
+                        var arg = arguments.Value[index];
+                        sb.Append(GetPropertyKey(arg));
+                    }
+                    sb.Append(")");
                 }
 
                 sb
+                    .Append(" ")
                     .Append(loc.Source)
                     .Append(":")
                     .Append(loc.Start.Line)
@@ -119,9 +122,8 @@ namespace Jint.Runtime.CallStack
             var index = _stack._size - 1;
             var element = index >= 0 ? _stack[index] : (CallStackElement?) null;
             var shortDescription = element?.ToString() ?? "";
-            var arguments = element?.Function._functionDefinition?.Function.Params ?? new NodeList<Expression>();
 
-            AppendLocation(sb.Builder, shortDescription, location, arguments);
+            AppendLocation(sb.Builder, shortDescription, location, element?.Arguments);
 
             location = element?.Location ?? default;
             index--;
@@ -130,9 +132,8 @@ namespace Jint.Runtime.CallStack
             {
                 element = index >= 0 ? _stack[index] : null;
                 shortDescription = element?.ToString() ?? "";
-                arguments = element?.Function._functionDefinition?.Function.Params ?? new NodeList<Expression>();
 
-                AppendLocation(sb.Builder, shortDescription, location, arguments);
+                AppendLocation(sb.Builder, shortDescription, location, element?.Arguments);
 
                 location = element?.Location ?? default;
                 index--;

+ 2 - 0
Jint/Runtime/Environments/EnvironmentRecord.cs

@@ -104,6 +104,8 @@ namespace Jint.Runtime.Environments
 
         public abstract JsValue GetThisBinding();
 
+        public JsValue NewTarget { get; protected set; }
+
         /// <summary>
         /// Helper to cache JsString/Key when environments use different lookups.
         /// </summary>

+ 7 - 9
Jint/Runtime/Environments/FunctionEnvironmentRecord.cs

@@ -24,9 +24,7 @@ namespace Jint.Runtime.Environments
 
         private JsValue _thisValue;
         private ThisBindingStatus _thisBindingStatus;
-        private readonly FunctionInstance _functionObject;
-        private readonly JsValue _homeObject = Undefined;
-        private readonly JsValue _newTarget;
+        internal readonly FunctionInstance _functionObject;
 
         public FunctionEnvironmentRecord(
             Engine engine, 
@@ -34,8 +32,8 @@ namespace Jint.Runtime.Environments
             JsValue newTarget) : base(engine)
         {
             _functionObject = functionObject;
-            _newTarget = newTarget;
-            if (functionObject is ArrowFunctionInstance)
+            NewTarget = newTarget;
+            if (functionObject._functionDefinition.Function is ArrowFunctionExpression)
             {
                 _thisBindingStatus = ThisBindingStatus.Lexical;
             }
@@ -49,7 +47,7 @@ namespace Jint.Runtime.Environments
         public override bool HasThisBinding() => _thisBindingStatus != ThisBindingStatus.Lexical;
 
         public override bool HasSuperBinding() => 
-            _thisBindingStatus != ThisBindingStatus.Lexical && !_homeObject.IsUndefined();
+            _thisBindingStatus != ThisBindingStatus.Lexical && !_functionObject._homeObject.IsUndefined();
 
         public override JsValue WithBaseObject()
         {
@@ -78,12 +76,12 @@ namespace Jint.Runtime.Environments
 
         public JsValue GetSuperBase()
         {
-            return _homeObject.IsUndefined() 
+            var home = _functionObject._homeObject;
+            return home.IsUndefined() 
                 ? Undefined
-                : ((ObjectInstance) _homeObject).Prototype;
+                : ((ObjectInstance) home).GetPrototypeOf();
         }
 
-
         // optimization to have logic near record internal structures.
 
         internal void InitializeParameters(

+ 24 - 0
Jint/Runtime/ExceptionHelper.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Diagnostics.CodeAnalysis;
 using System.Reflection;
 using Jint.Native;
 using Jint.Runtime.CallStack;
@@ -14,6 +15,7 @@ namespace Jint.Runtime
             return default;
         }
 
+        [DoesNotReturn]
         public static void ThrowSyntaxError(Engine engine, string message = null)
         {
             throw new JavaScriptException(engine.SyntaxError, message);
@@ -25,21 +27,25 @@ namespace Jint.Runtime
             return default;
         }
 
+        [DoesNotReturn]
         public static void ThrowArgumentException(string message = null)
         {
             ThrowArgumentException(message, null);
         }
 
+        [DoesNotReturn]
         public static void ThrowArgumentException(string message, string paramName)
         {
             throw new ArgumentException(message, paramName);
         }
 
+        [DoesNotReturn]
         public static void ThrowReferenceError(Engine engine, Reference reference)
         {
             ThrowReferenceError(engine, reference?.GetReferencedName()?.ToString());
         }
 
+        [DoesNotReturn]
         public static void ThrowReferenceError(Engine engine, string name)
         {
             var message = name != null ? name + " is not defined" : null;
@@ -62,6 +68,7 @@ namespace Jint.Runtime
             return default;
         }
 
+        [DoesNotReturn]
         public static void ThrowTypeError(Engine engine, string message = null, Exception exception = null)
         {
             throw new JavaScriptException(engine.TypeError, message, exception);
@@ -72,16 +79,19 @@ namespace Jint.Runtime
             throw new JavaScriptException(engine.RangeError, message);
         }
 
+        [DoesNotReturn]
         public static void ThrowRangeError(Engine engine, string message = null)
         {
             throw new JavaScriptException(engine.RangeError, message);
         }
 
+        [DoesNotReturn]
         public static void ThrowUriError(Engine engine)
         {
             throw new JavaScriptException(engine.UriError);
         }
 
+        [DoesNotReturn]
         public static void ThrowNotImplementedException(string message = null)
         {
             throw new NotImplementedException(message);
@@ -102,21 +112,25 @@ namespace Jint.Runtime
             throw new ArgumentOutOfRangeException(paramName, message);
         }
 
+        [DoesNotReturn]
         public static void ThrowArgumentOutOfRangeException(string paramName, string message)
         {
             throw new ArgumentOutOfRangeException(paramName, message);
         }
 
+        [DoesNotReturn]
         public static void ThrowTimeoutException()
         {
             throw new TimeoutException();
         }
 
+        [DoesNotReturn]
         public static void ThrowStatementsCountOverflowException()
         {
             throw new StatementsCountOverflowException();
         }
 
+        [DoesNotReturn]
         public static void ThrowArgumentOutOfRangeException()
         {
             throw new ArgumentOutOfRangeException();
@@ -127,26 +141,31 @@ namespace Jint.Runtime
             throw new NotSupportedException(message);
         }
 
+        [DoesNotReturn]
         public static void ThrowNotSupportedException(string message = null)
         {
             throw new NotSupportedException(message);
         }
 
+        [DoesNotReturn]
         public static void ThrowInvalidOperationException(string message = null)
         {
             throw new InvalidOperationException(message);
         }
 
+        [DoesNotReturn]
         public static void ThrowJavaScriptException(Engine engine, JsValue value, in Completion result)
         {
             throw new JavaScriptException(value).SetCallstack(engine, result.Location);
         }
 
+        [DoesNotReturn]
         public static void ThrowRecursionDepthOverflowException(JintCallStack currentStack, string currentExpressionReference)
         {
             throw new RecursionDepthOverflowException(currentStack, currentExpressionReference);
         }
 
+        [DoesNotReturn]
         public static void ThrowArgumentNullException(string paramName)
         {
             throw new ArgumentNullException(paramName);
@@ -157,6 +176,7 @@ namespace Jint.Runtime
             throw new ArgumentNullException(paramName);
         }
 
+        [DoesNotReturn]
         public static void ThrowMeaningfulException(Engine engine, TargetInvocationException exception)
         {
             var meaningfulException = exception.InnerException ?? exception;
@@ -168,21 +188,25 @@ namespace Jint.Runtime
             throw meaningfulException;
         }
 
+        [DoesNotReturn]
         public static void ThrowError(Engine engine, string message)
         {
             throw new JavaScriptException(engine.Error, message);
         }
 
+        [DoesNotReturn]
         public static void ThrowPlatformNotSupportedException(string message)
         {
             throw new PlatformNotSupportedException(message);
         }
 
+        [DoesNotReturn]
         public static void ThrowMemoryLimitExceededException(string message)
         {
             throw new MemoryLimitExceededException(message);
         }
 
+        [DoesNotReturn]
         public static void ThrowExecutionCanceledException()
         {
             throw new ExecutionCanceledException();

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

@@ -291,7 +291,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         sourceKey = identifier.Name;
                     }
 
-                    processedProperties?.Add(sourceKey.ToString());
+                    processedProperties?.Add(sourceKey);
                     if (p.Value is AssignmentPattern assignmentPattern)
                     {
                         source.TryGetValue(sourceKey, out var value);

+ 6 - 6
Jint/Runtime/Interpreter/Expressions/JintArrowFunctionExpression.cs

@@ -1,6 +1,5 @@
 using Esprima.Ast;
 using Jint.Native.Function;
-using Jint.Runtime.Environments;
 
 namespace Jint.Runtime.Interpreter.Expressions
 {
@@ -16,14 +15,15 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         protected override object EvaluateInternal()
         {
-            var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
+            var scope = _engine.ExecutionContext.LexicalEnvironment;
 
-            var closure = new ArrowFunctionInstance(
+            var closure = new ScriptFunctionInstance(
                 _engine,
                 _function,
-                funcEnv,
-                _function.Strict);
-
+                scope,
+                FunctionThisMode.Lexical,
+                proto: _engine.Function.PrototypeObject);
+            
             return closure;
         }
     }

+ 78 - 27
Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs

@@ -1,6 +1,7 @@
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Native.Function;
+using Jint.Native.Object;
 using Jint.Runtime.Environments;
 using Jint.Runtime.References;
 
@@ -11,24 +12,24 @@ namespace Jint.Runtime.Interpreter.Expressions
         private CachedArgumentsHolder _cachedArguments;
         private bool _cached;
 
-        private readonly JintExpression _calleeExpression;
+        private JintExpression _calleeExpression;
         private bool _hasSpreads;
 
         public JintCallExpression(Engine engine, CallExpression expression) : base(engine, expression)
         {
             _initialized = false;
-            _calleeExpression = Build(engine, expression.Callee);
         }
 
         protected override void Initialize()
         {
             var expression = (CallExpression) _expression;
+            _calleeExpression = Build(_engine, expression.Callee);
             var cachedArgumentsHolder = new CachedArgumentsHolder
             {
                 JintArguments = new JintExpression[expression.Arguments.Count]
             };
 
-            bool CanSpread(Node e)
+            static bool CanSpread(Node e)
             {
                 return e?.Type == Nodes.SpreadElement
                     || e is AssignmentExpression ae && ae.Right?.Type == Nodes.SpreadElement;
@@ -67,33 +68,55 @@ namespace Jint.Runtime.Interpreter.Expressions
         }
 
         protected override object EvaluateInternal()
+        {
+            return _calleeExpression is JintSuperExpression 
+                ? SuperCall()
+                : Call();
+        }
+
+        private object SuperCall()
+        {
+            var thisEnvironment = (FunctionEnvironmentRecord) _engine.GetThisEnvironment();
+            var newTarget = GetNewTarget(thisEnvironment);
+            var func = GetSuperConstructor(thisEnvironment);
+            if (!func.IsConstructor)
+            {
+                ExceptionHelper.ThrowTypeError(_engine, "Not a constructor");
+            }
+
+            var argList = ArgumentListEvaluation();
+            var result = ((IConstructor) func).Construct(argList, newTarget);
+            var thisER = (FunctionEnvironmentRecord) _engine.GetThisEnvironment();
+            return thisER.BindThisValue(result);
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-getsuperconstructor
+        /// </summary>
+        private ObjectInstance GetSuperConstructor(FunctionEnvironmentRecord thisEnvironment)
+        {
+            var envRec = thisEnvironment;
+            var activeFunction = envRec._functionObject;
+            var superConstructor = activeFunction.GetPrototypeOf();
+            return superConstructor;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-getnewtarget
+        /// </summary>
+        private JsValue GetNewTarget(FunctionEnvironmentRecord thisEnvironment)
+        {
+            return thisEnvironment.NewTarget;
+        }
+
+        private object Call()
         {
             var callee = _calleeExpression.Evaluate();
             var expression = (CallExpression) _expression;
 
             // todo: implement as in http://www.ecma-international.org/ecma-262/5.1/#sec-11.2.4
 
-            var cachedArguments = _cachedArguments;
-            var arguments = System.Array.Empty<JsValue>();
-            if (_cached)
-            {
-                arguments = cachedArguments.CachedArguments;
-            }
-            else
-            {
-                if (cachedArguments.JintArguments.Length > 0)
-                {
-                    if (_hasSpreads)
-                    {
-                        arguments = BuildArgumentsWithSpreads(cachedArguments.JintArguments);
-                    }
-                    else
-                    {
-                        arguments = _engine._jsValueArrayPool.RentArray(cachedArguments.JintArguments.Length);
-                        BuildArguments(cachedArguments.JintArguments, arguments);
-                    }
-                }
-            }
+            var arguments = ArgumentListEvaluation();
 
             var func = _engine.GetValue(callee, false);
             var r = callee as Reference;
@@ -107,7 +130,8 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
                 if (!_engine._referenceResolver.TryGetCallable(_engine, callee, out func))
                 {
-                    ExceptionHelper.ThrowTypeError(_engine, r == null ? "" : $"Property '{r.GetReferencedName()}' of object is not a function");
+                    ExceptionHelper.ThrowTypeError(_engine,
+                        r == null ? "" : $"Property '{r.GetReferencedName()}' of object is not a function");
                 }
             }
 
@@ -134,13 +158,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                 // is it a direct call to eval ? http://www.ecma-international.org/ecma-262/5.1/#sec-15.1.2.1.1
                 if (r.GetReferencedName() == CommonProperties.Eval && callable is EvalFunctionInstance instance)
                 {
-                    var value = instance.Call(thisObject, arguments, true);
+                    var value = instance.PerformEval(arguments, true);
                     _engine._referencePool.Return(r);
                     return value;
                 }
             }
 
-            var result = _engine.Call(callable, thisObject, arguments, expression.Location);
+            var result = _engine.Call(callable, thisObject, arguments, _calleeExpression);
 
             if (!_cached && arguments.Length > 0)
             {
@@ -151,6 +175,33 @@ namespace Jint.Runtime.Interpreter.Expressions
             return result;
         }
 
+        private JsValue[] ArgumentListEvaluation()
+        {
+            var cachedArguments = _cachedArguments;
+            var arguments = System.Array.Empty<JsValue>();
+            if (_cached)
+            {
+                arguments = cachedArguments.CachedArguments;
+            }
+            else
+            {
+                if (cachedArguments.JintArguments.Length > 0)
+                {
+                    if (_hasSpreads)
+                    {
+                        arguments = BuildArgumentsWithSpreads(cachedArguments.JintArguments);
+                    }
+                    else
+                    {
+                        arguments = _engine._jsValueArrayPool.RentArray(cachedArguments.JintArguments.Length);
+                        BuildArguments(cachedArguments.JintArguments, arguments);
+                    }
+                }
+            }
+
+            return arguments;
+        }
+
         private class CachedArgumentsHolder
         {
             internal JintExpression[] JintArguments;

+ 21 - 0
Jint/Runtime/Interpreter/Expressions/JintClassExpression.cs

@@ -0,0 +1,21 @@
+using Esprima.Ast;
+using Jint.Native.Function;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal sealed class JintClassExpression : JintExpression
+    {
+        public JintClassExpression(Engine engine, ClassExpression expression) : base(engine, expression)
+        {
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var env = _engine.ExecutionContext.LexicalEnvironment;
+            var expression = (ClassExpression) _expression;
+            var classDefinition = new ClassDefinition(expression.Id, expression.SuperClass, expression.Body);
+            var closure = classDefinition.BuildConstructor(_engine, env);
+            return closure;
+        }
+    }
+}

+ 42 - 2
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -1,9 +1,12 @@
+#nullable enable
+
 using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Native.Array;
 using Jint.Native.Iterator;
 using Jint.Native.Number;
+using Jint.Native.Symbol;
 
 namespace Jint.Runtime.Interpreter.Expressions
 {
@@ -52,6 +55,40 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         protected abstract object EvaluateInternal();
 
+        /// <summary>
+        /// If we'd get Esprima source, we would just refer to it, but this makes error messages easier to decipher.
+        /// </summary>
+        internal string SourceText => ToString(_expression) ?? "*unknown*";
+
+        internal static string? ToString(Expression expression)
+        {
+            while (true)
+            {
+                if (expression is Literal literal)
+                {
+                    return EsprimaExtensions.LiteralKeyToString(literal);
+                }
+
+                if (expression is Identifier identifier)
+                {
+                    return identifier.Name;
+                }
+
+                if (expression is MemberExpression memberExpression)
+                {
+                    return ToString(memberExpression.Object) + "." + ToString(memberExpression.Property);
+                }
+
+                if (expression is CallExpression callExpression)
+                {
+                    expression = callExpression.Callee;
+                    continue;
+                }
+
+                return null;
+            }
+        }
+
         protected internal static JintExpression Build(Engine engine, Expression expression)
         {
             return expression.Type switch
@@ -82,6 +119,8 @@ namespace Jint.Runtime.Interpreter.Expressions
                 Nodes.SpreadElement => new JintSpreadExpression(engine, (SpreadElement) expression),
                 Nodes.TemplateLiteral => new JintTemplateLiteralExpression(engine, (TemplateLiteral) expression),
                 Nodes.TaggedTemplateExpression => new JintTaggedTemplateExpression(engine, (TaggedTemplateExpression) expression),
+                Nodes.ClassExpression => new JintClassExpression(engine, (ClassExpression) expression),
+                Nodes.Super => new JintSuperExpression(engine, (Super) expression),
                 _ => ExceptionHelper.ThrowArgumentOutOfRangeException<JintExpression>(nameof(expression), $"unsupported expression type '{expression.Type}'")
             };
         }
@@ -321,8 +360,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                 if (jintExpression is JintSpreadExpression jse)
                 {
                     jse.GetValueAndCheckIterator(out var objectInstance, out var iterator);
-                    // optimize for array
-                    if (objectInstance is ArrayInstance ai)
+                    // optimize for array unless someone has touched the iterator
+                    if (objectInstance is ArrayInstance ai 
+                        && ReferenceEquals(ai.Get(GlobalSymbolRegistry.Iterator), _engine.Array.PrototypeObject._originalIteratorFunction))
                     {
                         var length = ai.GetLength();
                         for (uint j = 0; j < length; ++j)

+ 4 - 2
Jint/Runtime/Interpreter/Expressions/JintFunctionExpression.cs

@@ -19,8 +19,8 @@ namespace Jint.Runtime.Interpreter.Expressions
             var funcEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, _engine.ExecutionContext.LexicalEnvironment);
 
             var functionThisMode = _function.Strict || _engine._isStrict
-                ? FunctionInstance.FunctionThisMode.Strict 
-                : FunctionInstance.FunctionThisMode.Global;
+                ? FunctionThisMode.Strict 
+                : FunctionThisMode.Global;
 
             var closure = new ScriptFunctionInstance(
                 _engine,
@@ -28,6 +28,8 @@ namespace Jint.Runtime.Interpreter.Expressions
                 funcEnv,
                 functionThisMode);
 
+            closure.MakeConstructor();
+
             if (_function.Name != null)
             {
                 var envRec = (DeclarativeEnvironmentRecord) funcEnv._record;

+ 2 - 2
Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs

@@ -29,7 +29,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 ? temp
                 : JsValue.Undefined;
 
-            return _engine._referencePool.Rent(identifierEnvironment, _expressionName.StringValue, strict);
+            return _engine._referencePool.Rent(identifierEnvironment, _expressionName.StringValue, strict, thisValue: null);
         }
 
         public override JsValue GetValue()
@@ -51,7 +51,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 out _,
                 out var value)
                 ? value ?? ExceptionHelper.ThrowReferenceError<JsValue>(_engine, _expressionName.Key.Name + " has not been initialized")
-                : _engine.GetValue(_engine._referencePool.Rent(JsValue.Undefined, _expressionName.StringValue, strict), true);
+                : _engine.GetValue(_engine._referencePool.Rent(JsValue.Undefined, _expressionName.StringValue, strict, thisValue: null), true);
         }
     }
 }

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

@@ -66,7 +66,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             var expression = (Literal) _expression;
             if (expression.TokenType == TokenType.RegularExpression)
             {
-                return _engine.RegExp.Construct((System.Text.RegularExpressions.Regex) expression.Value, expression.Regex.Flags, _engine);
+                return _engine.RegExp.Construct((System.Text.RegularExpressions.Regex) expression.Value, expression.Regex.Flags);
             }
 
             return JsValue.FromObject(_engine, expression.Value);

+ 13 - 12
Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs

@@ -11,11 +11,7 @@ namespace Jint.Runtime.Interpreter.Expressions
     internal sealed class JintMemberExpression : JintExpression
     {
         private MemberExpression _memberExpression;
-
         private JintExpression _objectExpression;
-        private JintIdentifierExpression _objectIdentifierExpression;
-        private JintThisExpression _objectThisExpression;
-
         private JintExpression _propertyExpression;
         private JsValue _determinedProperty;
 
@@ -28,8 +24,6 @@ namespace Jint.Runtime.Interpreter.Expressions
         {
             _memberExpression = (MemberExpression) _expression;
             _objectExpression = Build(_engine, _memberExpression.Object);
-            _objectIdentifierExpression = _objectExpression as JintIdentifierExpression;
-            _objectThisExpression = _objectExpression as JintThisExpression;
 
             if (!_memberExpression.Computed)
             {
@@ -48,25 +42,32 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         protected override object EvaluateInternal()
         {
+            JsValue actualThis = null;
             string baseReferenceName = null;
             JsValue baseValue = null;
             var isStrictModeCode = StrictModeScope.IsStrictModeCode;
 
-            if (_objectIdentifierExpression != null)
+            if (_objectExpression is JintIdentifierExpression identifierExpression)
             {
-                baseReferenceName = _objectIdentifierExpression._expressionName.Key.Name;
+                baseReferenceName = identifierExpression._expressionName.Key.Name;
                 var strict = isStrictModeCode;
                 var env = _engine.ExecutionContext.LexicalEnvironment;
                 LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
                     env,
-                    _objectIdentifierExpression._expressionName,
+                    identifierExpression._expressionName,
                     strict,
                     out _,
                     out baseValue);
             }
-            else if (_objectThisExpression != null)
+            else if (_objectExpression is JintThisExpression thisExpression)
+            {
+                baseValue = thisExpression.GetValue();
+            }
+            else if (_objectExpression is JintSuperExpression)
             {
-                baseValue = _objectThisExpression.GetValue();
+                var env = (FunctionEnvironmentRecord) _engine.GetThisEnvironment();
+                actualThis = env.GetThisBinding();
+                baseValue = env.GetSuperBase();
             }
 
             if (baseValue is null)
@@ -96,7 +97,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 ? property
                 : TypeConverter.ToPropertyKey(property);
 
-            return _engine._referencePool.Rent(baseValue, propertyKey, isStrictModeCode);
+            return _engine._referencePool.Rent(baseValue, propertyKey, isStrictModeCode, thisValue: actualThis);
         }
     }
 }

+ 6 - 5
Jint/Runtime/Interpreter/Expressions/JintNewExpression.cs

@@ -36,6 +36,9 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         protected override object EvaluateInternal()
         {
+            // todo: optimize by defining a common abstract class or interface
+            var jsValue = _calleeExpression.GetValue();
+
             JsValue[] arguments;
             if (_jintArguments.Length == 0)
             {
@@ -51,15 +54,13 @@ namespace Jint.Runtime.Interpreter.Expressions
                 BuildArguments(_jintArguments, arguments);
             }
 
-            // todo: optimize by defining a common abstract class or interface
-            var jsValue = _calleeExpression.GetValue();
-            if (!(jsValue is IConstructor callee))
+            if (!jsValue.IsConstructor)
             {
-                return ExceptionHelper.ThrowTypeError<object>(_engine, "The object can't be used as constructor.");
+                ExceptionHelper.ThrowTypeError(_engine,  _calleeExpression.SourceText + " is not a constructor");
             }
 
             // construct the new instance using the Function's constructor method
-            var instance = callee.Construct(arguments, jsValue);
+            var instance = _engine.Construct((IConstructor) jsValue, arguments, jsValue, _calleeExpression);
 
             _engine._jsValueArrayPool.ReturnArray(arguments);
 

+ 38 - 30
Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs

@@ -1,3 +1,5 @@
+#nullable enable
+
 using Esprima.Ast;
 using Jint.Collections;
 using Jint.Native;
@@ -8,12 +10,12 @@ using Jint.Runtime.Descriptors;
 namespace Jint.Runtime.Interpreter.Expressions
 {
     /// <summary>
-    /// http://www.ecma-international.org/ecma-262/#sec-object-initializer
+    /// https://tc39.es/ecma262/#sec-object-initializer
     /// </summary>
     internal sealed class JintObjectExpression : JintExpression
     {
         private JintExpression[] _valueExpressions = System.Array.Empty<JintExpression>();
-        private ObjectProperty[] _properties = System.Array.Empty<ObjectProperty>();
+        private ObjectProperty?[] _properties = System.Array.Empty<ObjectProperty>();
 
         // check if we can do a shortcut when all are object properties
         // and don't require duplicate checking
@@ -21,17 +23,17 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         private class ObjectProperty
         {
-            internal readonly string _key;
-            private JsString _keyJsString;
+            internal readonly string? _key;
+            private JsString? _keyJsString;
             internal readonly Property _value;
 
-            public ObjectProperty(string key, Property property)
+            public ObjectProperty(string? key, Property property)
             {
                 _key = key;
                 _value = property;
             }
 
-            public JsString KeyJsString => _keyJsString ??= _key != null ? JsString.Create(_key) : null;
+            public JsString? KeyJsString => _keyJsString ??= _key != null ? JsString.Create(_key) : null;
         }
 
         public JintObjectExpression(Engine engine, ObjectExpression expression) : base(engine, expression)
@@ -54,7 +56,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
             for (var i = 0; i < _properties.Length; i++)
             {
-                string propName = null;
+                string? propName = null;
                 var property = expression.Properties[i];
                 if (property is Property p)
                 {
@@ -120,12 +122,15 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var objectProperty = _properties[i];
                 var valueExpression = _valueExpressions[i];
                 var propValue = valueExpression.GetValue().Clone();
-                properties[objectProperty._key] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
+                properties[objectProperty!._key] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
             }
             obj.SetProperties(properties);
             return obj;
         }
 
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-object-initializer-runtime-semantics-propertydefinitionevaluation
+        /// </summary>
         private object BuildObjectNormal()
         {
             var obj = _engine.Object.Construct(_properties.Length);
@@ -146,45 +151,48 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
                 
                 var property = objectProperty._value;
-                var propName = objectProperty.KeyJsString ?? property.GetKey(_engine);
-
-                PropertyDescriptor propDesc;
 
+                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);
+                    continue;
+                }
+                
+                var propName = objectProperty.KeyJsString ?? property.GetKey(_engine);
                 if (property.Kind == PropertyKind.Init || property.Kind == PropertyKind.Data)
                 {
                     var expr = _valueExpressions[i];
-                    var propValue = expr.GetValue().Clone();
+                    JsValue propValue = expr.GetValue().Clone();
                     if (expr._expression.IsFunctionWithName())
                     {
-                        var functionInstance = (FunctionInstance) propValue;
-                        functionInstance.SetFunctionName(propName);
+                        var closure = (FunctionInstance) propValue;
+                        closure.SetFunctionName(propName);
                     }
-                    propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
+
+                    var propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
+                    obj.DefinePropertyOrThrow(propName, propDesc);
                 }
                 else if (property.Kind == PropertyKind.Get || property.Kind == PropertyKind.Set)
                 {
                     var function = property.Value as IFunction ?? ExceptionHelper.ThrowSyntaxError<IFunction>(_engine);
 
-                    var functionInstance = new ScriptFunctionInstance(
+                    var closure = new ScriptFunctionInstance(
                         _engine,
                         function,
                         _engine.ExecutionContext.LexicalEnvironment,
-                        isStrictModeCode
-                    );
-                    functionInstance.SetFunctionName(propName);
-                    functionInstance._prototypeDescriptor = null;
-
-                    propDesc = new GetSetPropertyDescriptor(
-                        get: property.Kind == PropertyKind.Get ? functionInstance : null,
-                        set: property.Kind == PropertyKind.Set ? functionInstance : null,
+                        isStrictModeCode);
+                    closure.SetFunctionName(propName, property.Kind == PropertyKind.Get ? "get" : "set");
+
+                    var propDesc = new GetSetPropertyDescriptor(
+                        get: property.Kind == PropertyKind.Get ? closure : null,
+                        set: property.Kind == PropertyKind.Set ? closure : null,
                         PropertyFlag.Enumerable | PropertyFlag.Configurable);
+                    
+                    obj.DefinePropertyOrThrow(propName, propDesc);
                 }
-                else
-                {
-                    return ExceptionHelper.ThrowArgumentOutOfRangeException<object>();
-                }
-
-                obj.DefineOwnProperty(propName, propDesc);
             }
 
             return obj;

+ 20 - 0
Jint/Runtime/Interpreter/Expressions/JintSuperExpression.cs

@@ -0,0 +1,20 @@
+using Esprima.Ast;
+using Jint.Runtime.Environments;
+
+namespace Jint.Runtime.Interpreter.Expressions
+{
+    internal class JintSuperExpression : JintExpression
+    {
+        public JintSuperExpression(Engine engine, Super expression) : base(engine, expression)
+        {
+        }
+
+        protected override object EvaluateInternal()
+        {
+            var envRec = (FunctionEnvironmentRecord) _engine.GetThisEnvironment();
+            var activeFunction = envRec._functionObject;
+            var superConstructor = activeFunction.GetPrototypeOf();
+            return superConstructor;
+        }
+    }
+}

+ 5 - 0
Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs

@@ -79,6 +79,11 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                     if (r.IsPropertyReference())
                     {
+                        if (r.IsSuperReference())
+                        {
+                            ExceptionHelper.ThrowReferenceError(_engine, r);
+                        }
+                        
                         var o = TypeConverter.ToObject(_engine, r.GetBase());
                         var deleteStatus  = o.Delete(r.GetReferencedName());
                         if (!deleteStatus && r.IsStrictReference())

+ 1 - 1
Jint/Runtime/Interpreter/JintFunctionDefinition.cs

@@ -128,7 +128,7 @@ namespace Jint.Runtime.Interpreter
             const string ParameterNameArguments = "arguments";
 
             state.ArgumentsObjectNeeded = true;
-            if (functionInstance._thisMode == FunctionInstance.FunctionThisMode.Lexical)
+            if (functionInstance._thisMode == FunctionThisMode.Lexical)
             {
                 state.ArgumentsObjectNeeded = false;
             }

+ 32 - 0
Jint/Runtime/Interpreter/Statements/JintClassDeclarationStatement.cs

@@ -0,0 +1,32 @@
+#nullable enable
+
+using Esprima.Ast;
+using Jint.Native.Function;
+
+namespace Jint.Runtime.Interpreter.Statements
+{
+    internal sealed class JintClassDeclarationStatement : JintStatement<ClassDeclaration>
+    {
+        private readonly ClassDefinition _classDefinition;
+
+        public JintClassDeclarationStatement(Engine engine, ClassDeclaration classDeclaration) : base(engine, classDeclaration)
+        {
+            _classDefinition = new ClassDefinition(className: classDeclaration.Id, classDeclaration.SuperClass, classDeclaration.Body);
+        }
+
+        protected override Completion ExecuteInternal()
+        {
+            var env = _engine.ExecutionContext.LexicalEnvironment;
+            var F = _classDefinition.BuildConstructor(_engine, env);
+
+            var classBinding = _classDefinition._className;
+            if (classBinding != null)
+            {
+                env._record.CreateMutableBinding(classBinding);
+                env._record.InitializeBinding(classBinding, F);
+            }
+
+            return new Completion(CompletionType.Normal, null, null, Location);
+        }
+    }
+}

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

@@ -255,11 +255,13 @@ namespace Jint.Runtime.Interpreter.Statements
 
                     if (result.Type == CompletionType.Break && (result.Identifier == null || result.Identifier == _statement?.LabelSet?.Name))
                     {
+                        completionType = CompletionType.Normal;
                         return new Completion(CompletionType.Normal, v, null, Location);
                     }
 
                     if (result.Type != CompletionType.Continue || (result.Identifier != null && result.Identifier != _statement?.LabelSet?.Name))
                     {
+                        completionType = result.Type;
                         if (result.Type != CompletionType.Normal)
                         {
                             return result;
@@ -276,7 +278,18 @@ namespace Jint.Runtime.Interpreter.Statements
             {
                 if (close)
                 {
-                    iteratorRecord.Close(completionType);
+                    try
+                    {
+                        iteratorRecord.Close(completionType);
+                    }
+                    catch
+                    {
+                        // if we already have and exception, use it
+                        if (completionType != CompletionType.Throw)
+                        {
+                            throw;
+                        }
+                    }
                 }
                 _engine.UpdateLexicalEnvironment(oldEnv);
             }

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

@@ -83,6 +83,7 @@ namespace Jint.Runtime.Interpreter.Statements
                 Nodes.WithStatement => new JintWithStatement(engine, (WithStatement) statement),
                 Nodes.DebuggerStatement => new JintDebuggerStatement(engine, (DebuggerStatement) statement),
                 Nodes.Program => new JintScript(engine, statement as Script ?? ExceptionHelper.ThrowArgumentException<Script>("modules not supported")),
+                Nodes.ClassDeclaration => new JintClassDeclarationStatement(engine, (ClassDeclaration) statement),
                 _ => ExceptionHelper.ThrowArgumentOutOfRangeException<JintStatement>(nameof(statement.Type), $"unsupported statement type '{statement.Type}'")
             };
         }

+ 7 - 5
Jint/Runtime/References/Reference.cs

@@ -14,11 +14,13 @@ namespace Jint.Runtime.References
         private JsValue _baseValue;
         private JsValue _property;
         internal bool _strict;
+        private JsValue _thisValue;
 
-        public Reference(JsValue baseValue, JsValue property, bool strict)
+        public Reference(JsValue baseValue, JsValue property, bool strict, JsValue thisValue = null)
         {
             _baseValue = baseValue;
             _property = property;
+            _thisValue = thisValue;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -44,8 +46,7 @@ namespace Jint.Runtime.References
 
         public bool IsSuperReference()
         {
-            // TODO super not implemented
-            return false;
+            return _thisValue is not null;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -59,17 +60,18 @@ namespace Jint.Runtime.References
         {
             if (IsSuperReference())
             {
-                return ExceptionHelper.ThrowNotImplementedException<JsValue>();
+                return _thisValue;
             }
 
             return GetBase();
         }
 
-        internal Reference Reassign(JsValue baseValue, JsValue name, bool strict)
+        internal Reference Reassign(JsValue baseValue, JsValue name, bool strict, JsValue thisValue)
         {
             _baseValue = baseValue;
             _property = name;
             _strict = strict;
+            _thisValue = thisValue;
 
             return this;
         }

+ 18 - 11
Jint/Runtime/TypeConverter.cs

@@ -420,7 +420,7 @@ namespace Jint.Runtime
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static string  ToString(double d)
+        internal static string ToString(double d)
         {
             if (d > long.MinValue && d < long.MaxValue  && Math.Abs(d % 1) <= DoubleIsIntegerTolerance)
             {
@@ -428,11 +428,9 @@ namespace Jint.Runtime
                 return ToString((long) d);
             }
 
-            using (var stringBuilder = StringBuilderPool.Rent())
-            {
-                // we can create smaller array as we know the format to be short
-                return NumberPrototype.NumberToString(d, new DtoaBuilder(17), stringBuilder.Builder);
-            }
+            using var stringBuilder = StringBuilderPool.Rent();
+            // we can create smaller array as we know the format to be short
+            return NumberPrototype.NumberToString(d, new DtoaBuilder(17), stringBuilder.Builder);
         }
 
         /// <summary>
@@ -495,14 +493,23 @@ namespace Jint.Runtime
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static ObjectInstance ToObject(Engine engine, JsValue value)
+        {
+            if (value is ObjectInstance oi)
+            {
+                return oi;
+            }
+
+            return ToObjectNonObject(engine, value);
+        }
+
+        private static ObjectInstance ToObjectNonObject(Engine engine, JsValue value)
         {
             var type = value._type & ~InternalTypes.InternalFlags;
             return type switch
             {
-                InternalTypes.Object => (ObjectInstance) value,
-                InternalTypes.Boolean => engine.Boolean.Construct(((JsBoolean) value)._value),
-                InternalTypes.Number => engine.Number.Construct(((JsNumber) value)._value),
-                InternalTypes.Integer => engine.Number.Construct(((JsNumber) value)._value),
+                InternalTypes.Boolean => engine.Boolean.Construct((JsBoolean) value),
+                InternalTypes.Number => engine.Number.Construct((JsNumber) value),
+                InternalTypes.Integer => engine.Number.Construct((JsNumber) value),
                 InternalTypes.String => engine.String.Construct(value.ToString()),
                 InternalTypes.Symbol => engine.Symbol.Construct((JsSymbol) value),
                 InternalTypes.Null => ExceptionHelper.ThrowTypeError<ObjectInstance>(engine, "Cannot convert undefined or null to object"),
@@ -510,7 +517,7 @@ namespace Jint.Runtime
                 _ => ExceptionHelper.ThrowTypeError<ObjectInstance>(engine, "Cannot convert given item to object")
             };
         }
-        
+
         [MethodImpl(MethodImplOptions.NoInlining)]
         internal static void CheckObjectCoercible(
             Engine engine,

+ 1 - 2
README.md

@@ -240,7 +240,7 @@ for (var i = 0; i < 10; i++)
 
 ES6 features which are being implemented:
 - [x] [arrows](https://github.com/lukehoban/es6features/blob/master/README.md#arrows)
-- [ ] [classes](https://github.com/lukehoban/es6features/blob/master/README.md#classes)
+- [x] [classes](https://github.com/lukehoban/es6features/blob/master/README.md#classes)
 - [x] [enhanced object literals](https://github.com/lukehoban/es6features/blob/master/README.md#enhanced-object-literals)
 - [x] [template strings](https://github.com/lukehoban/es6features/blob/master/README.md#template-strings)
 - [x] [destructuring](https://github.com/lukehoban/es6features/blob/master/README.md#destructuring)
@@ -255,7 +255,6 @@ ES6 features which are being implemented:
 - [ ] [weakmap + weakset](https://github.com/lukehoban/es6features/blob/master/README.md#map--set--weakmap--weakset)
 - [x] [proxies](https://github.com/lukehoban/es6features/blob/master/README.md#proxies)
 - [x] [symbols](https://github.com/lukehoban/es6features/blob/master/README.md#symbols)
-- [ ] [subclassable built-ins](https://github.com/lukehoban/es6features/blob/master/README.md#subclassable-built-ins)
 - [ ] [promises](https://github.com/lukehoban/es6features/blob/master/README.md#promises)
 - [x] [math APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)
 - [x] [number APIs](https://github.com/lukehoban/es6features/blob/master/README.md#math--number--string--array--object-apis)