Browse Source

Reflect & Proxy (#681)

Marko Lahma 5 years ago
parent
commit
f89886063e
100 changed files with 2521 additions and 1383 deletions
  1. 1 1
      Jint.Benchmark/Jint.Benchmark.csproj
  2. 1 1
      Jint.Repl/Jint.Repl.csproj
  3. 1 1
      Jint.Tests.CommonScripts/Jint.Tests.CommonScripts.csproj
  4. 1 1
      Jint.Tests.Ecma/Jint.Tests.Ecma.csproj
  5. 1 1
      Jint.Tests.Test262/Jint.Tests.Test262.csproj
  6. 15 0
      Jint.Tests.Test262/ProxyTests.cs
  7. 15 0
      Jint.Tests.Test262/ReflectTests.cs
  8. 38 20
      Jint.Tests.Test262/Test262Test.cs
  9. 8 49
      Jint.Tests.Test262/test/skipped.json
  10. 1 1
      Jint.Tests/Jint.Tests.csproj
  11. 3 3
      Jint.Tests/Runtime/EngineTests.cs
  12. 2 2
      Jint.Tests/Runtime/InteropTests.cs
  13. 1 1
      Jint.Tests/Runtime/NullPropagation.cs
  14. 1 1
      Jint.Tests/Runtime/ObjectInstanceTests.cs
  15. 7 7
      Jint/Collections/StringDictionarySlim.cs
  16. 1 1
      Jint/Directory.Build.props
  17. 36 44
      Jint/Engine.cs
  18. 20 22
      Jint/EsprimaExtensions.cs
  19. 1 1
      Jint/JsValueExtensions.cs
  20. 34 7
      Jint/Key.cs
  21. 25 29
      Jint/Native/Argument/ArgumentsInstance.cs
  22. 116 16
      Jint/Native/Array/ArrayConstructor.cs
  23. 75 69
      Jint/Native/Array/ArrayInstance.cs
  24. 268 0
      Jint/Native/Array/ArrayOperations.cs
  25. 62 282
      Jint/Native/Array/ArrayPrototype.cs
  26. 4 8
      Jint/Native/Boolean/BooleanConstructor.cs
  27. 1 2
      Jint/Native/Boolean/BooleanPrototype.cs
  28. 13 18
      Jint/Native/Date/DateConstructor.cs
  29. 3 4
      Jint/Native/Date/DatePrototype.cs
  30. 12 7
      Jint/Native/Error/ErrorConstructor.cs
  31. 4 5
      Jint/Native/Error/ErrorPrototype.cs
  32. 6 6
      Jint/Native/Function/ArrowFunctionInstance.cs
  33. 10 8
      Jint/Native/Function/BindFunctionInstance.cs
  34. 1 1
      Jint/Native/Function/EvalFunctionInstance.cs
  35. 8 15
      Jint/Native/Function/FunctionConstructor.cs
  36. 36 14
      Jint/Native/Function/FunctionInstance.cs
  37. 20 11
      Jint/Native/Function/FunctionPrototype.cs
  38. 33 20
      Jint/Native/Function/ScriptFunctionInstance.cs
  39. 1 1
      Jint/Native/Function/ThrowTypeError.cs
  40. 4 3
      Jint/Native/Global/GlobalObject.cs
  41. 1 2
      Jint/Native/IConstructor.cs
  42. 12 13
      Jint/Native/Iterator/IteratorConstructor.cs
  43. 7 7
      Jint/Native/Iterator/IteratorInstance.cs
  44. 1 1
      Jint/Native/Iterator/IteratorProtocol.cs
  45. 1 2
      Jint/Native/Iterator/IteratorPrototype.cs
  46. 6 0
      Jint/Native/JsString.cs
  47. 11 5
      Jint/Native/JsSymbol.cs
  48. 10 5
      Jint/Native/JsValue.cs
  49. 7 14
      Jint/Native/Json/JsonInstance.cs
  50. 3 3
      Jint/Native/Json/JsonParser.cs
  51. 7 8
      Jint/Native/Json/JsonSerializer.cs
  52. 6 8
      Jint/Native/Map/MapConstructor.cs
  53. 0 39
      Jint/Native/Map/MapInstance.cs
  54. 3 4
      Jint/Native/Map/MapPrototype.cs
  55. 1 2
      Jint/Native/Math/MathInstance.cs
  56. 5 7
      Jint/Native/Number/NumberConstructor.cs
  57. 1 2
      Jint/Native/Number/NumberPrototype.cs
  58. 112 126
      Jint/Native/Object/ObjectConstructor.cs
  59. 333 168
      Jint/Native/Object/ObjectInstance.cs
  60. 1 2
      Jint/Native/Object/ObjectPrototype.cs
  61. 92 0
      Jint/Native/Proxy/ProxyConstructor.cs
  62. 486 0
      Jint/Native/Proxy/ProxyInstance.cs
  63. 37 0
      Jint/Native/Proxy/ProxyPrototype.cs
  64. 203 0
      Jint/Native/Reflect/ReflectInstance.cs
  65. 12 13
      Jint/Native/RegExp/RegExpConstructor.cs
  66. 6 7
      Jint/Native/RegExp/RegExpPrototype.cs
  67. 6 8
      Jint/Native/Set/SetConstructor.cs
  68. 0 39
      Jint/Native/Set/SetInstance.cs
  69. 3 4
      Jint/Native/Set/SetPrototype.cs
  70. 6 10
      Jint/Native/String/StringConstructor.cs
  71. 2 2
      Jint/Native/String/StringInstance.cs
  72. 7 8
      Jint/Native/String/StringPrototype.cs
  73. 70 14
      Jint/Native/Symbol/GlobalSymbolRegistry.cs
  74. 12 16
      Jint/Native/Symbol/SymbolConstructor.cs
  75. 4 5
      Jint/Native/Symbol/SymbolPrototype.cs
  76. 1 2
      Jint/Pooling/ArgumentsInstancePool.cs
  77. 12 0
      Jint/Runtime/Arguments.cs
  78. 3 5
      Jint/Runtime/Debugger/DebugHandler.cs
  79. 11 11
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  80. 1 1
      Jint/Runtime/Environments/EnvironmentRecord.cs
  81. 10 7
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  82. 2 3
      Jint/Runtime/Interop/ClrFunctionInstance.cs
  83. 12 3
      Jint/Runtime/Interop/DefaultTypeConverter.cs
  84. 2 2
      Jint/Runtime/Interop/DelegateWrapper.cs
  85. 3 3
      Jint/Runtime/Interop/MethodInfoFunctionInstance.cs
  86. 3 13
      Jint/Runtime/Interop/NamespaceReference.cs
  87. 7 16
      Jint/Runtime/Interop/ObjectWrapper.cs
  88. 12 33
      Jint/Runtime/Interop/TypeReference.cs
  89. 7 7
      Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs
  90. 3 2
      Jint/Runtime/Interpreter/Expressions/JintNewExpression.cs
  91. 14 14
      Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
  92. 1 1
      Jint/Runtime/Interpreter/Expressions/JintSpreadExpression.cs
  93. 1 1
      Jint/Runtime/Interpreter/Expressions/JintTaggedTemplateExpression.cs
  94. 14 3
      Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs
  95. 4 4
      Jint/Runtime/Interpreter/JintFunctionDefinition.cs
  96. 2 2
      Jint/Runtime/Interpreter/Statements/JintForInStatement.cs
  97. 1 1
      Jint/Runtime/Interpreter/Statements/JintTryStatement.cs
  98. 3 3
      Jint/Runtime/JavaScriptException.cs
  99. 7 0
      Jint/Runtime/References/Reference.cs
  100. 21 19
      Jint/Runtime/TypeConverter.cs

+ 1 - 1
Jint.Benchmark/Jint.Benchmark.csproj

@@ -22,7 +22,7 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.11.5" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
     <PackageReference Include="Jurassic" Version="3.0.0" />
     <PackageReference Include="Newtonsoft.Json" Version="12.0.2" />
     <PackageReference Include="NiL.JS.NetCore" Version="2.5.1327" />

+ 1 - 1
Jint.Repl/Jint.Repl.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFramework>netcoreapp2.0</TargetFramework>
+    <TargetFramework>netcoreapp3.0</TargetFramework>
     <OutputType>Exe</OutputType>
   </PropertyGroup>
   <ItemGroup>

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

@@ -9,7 +9,7 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />

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

@@ -6,7 +6,7 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />

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

@@ -6,7 +6,7 @@
     <ProjectReference Include="..\Jint\Jint.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />

+ 15 - 0
Jint.Tests.Test262/ProxyTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class ProxyTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\Proxy")]
+        [MemberData(nameof(SourceFiles), "built-ins\\Proxy", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\Proxy", true, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 15 - 0
Jint.Tests.Test262/ReflectTests.cs

@@ -0,0 +1,15 @@
+using Xunit;
+
+namespace Jint.Tests.Test262
+{
+    public class ReflectTests : Test262Test
+    {
+        [Theory(DisplayName = "built-ins\\Reflect")]
+        [MemberData(nameof(SourceFiles), "built-ins\\Reflect", false)]
+        [MemberData(nameof(SourceFiles), "built-ins\\Reflect", true, Skip = "Skipped")]
+        protected void RunTest(SourceFile sourceFile)
+        {
+            RunTestInternal(sourceFile);
+        }
+    }
+}

+ 38 - 20
Jint.Tests.Test262/Test262Test.cs

@@ -1,9 +1,11 @@
 using System;
+using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
 using System.Reflection;
 using System.Text.RegularExpressions;
+using System.Threading.Tasks;
 using Jint.Runtime;
 using Newtonsoft.Json.Linq;
 using Xunit;
@@ -13,7 +15,7 @@ namespace Jint.Tests.Test262
 {
     public abstract class Test262Test
     {
-        private static readonly string[] Sources;
+        private static readonly Dictionary<string, string> Sources;
 
         private static readonly string BasePath;
 
@@ -24,6 +26,11 @@ namespace Jint.Tests.Test262
 
         private static readonly HashSet<string> _strictSkips =
             new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+        
+        private static readonly Dictionary<string, string> extraSourceFiles = new Dictionary<string, string>()
+        {
+            { "built-ins/Array/isArray/descriptor.js", "propertyHelper.js"}
+        };
 
         static Test262Test()
         {
@@ -37,17 +44,18 @@ namespace Jint.Tests.Test262
 
             string[] files =
             {
-                @"harness\sta.js",
-                @"harness\assert.js",
-                @"harness\propertyHelper.js",
-                @"harness\compareArray.js",
-                @"harness\decimalToHexString.js",
+                "sta.js",
+                "assert.js",
+                "propertyHelper.js",
+                "compareArray.js",
+                "decimalToHexString.js",
+                "proxyTrapsHelper.js",
             };
 
-            Sources = new string[files.Length];
+            Sources = new Dictionary<string, string>(files.Length);
             for (var i = 0; i < files.Length; i++)
             {
-                Sources[i] = File.ReadAllText(Path.Combine(BasePath, files[i]));
+                Sources[files[i]] = File.ReadAllText(Path.Combine(BasePath, "harness", files[i]));
             }
 
             var content = File.ReadAllText(Path.Combine(BasePath, "test/skipped.json"));
@@ -67,13 +75,27 @@ namespace Jint.Tests.Test262
         {
             var engine = new Engine(cfg => cfg
                 .LocalTimeZone(_pacificTimeZone)
-                .Strict(strict));
-
-            for (int i = 0; i < Sources.Length; ++i)
+                .Strict(strict)
+            );
+
+            engine.Execute(Sources["sta.js"]);
+            engine.Execute(Sources["assert.js"]);
+            
+            var includes = Regex.Match(code, @"includes: \[(.+?)\]");
+            if (includes.Success)
             {
-                engine.Execute(Sources[i]);
+                var files = includes.Groups[1].Captures[0].Value.Split(',');
+                foreach (var file in files)
+                {
+                    engine.Execute(Sources[file.Trim()]);
+                }
             }
 
+            if (code.IndexOf("propertyHelper.js", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                engine.Execute(Sources["propertyHelper.js"]);
+            }
+            
             string lastError = null;
 
             bool negative = code.IndexOf("negative:", StringComparison.Ordinal) > -1;
@@ -121,12 +143,12 @@ namespace Jint.Tests.Test262
 
         public static IEnumerable<object[]> SourceFiles(string pathPrefix, bool skipped)
         {
-            var results = new List<object[]>();
+            var results = new ConcurrentBag<object[]>();
             var fixturesPath = Path.Combine(BasePath, "test");
             var searchPath = Path.Combine(fixturesPath, pathPrefix);
             var files = Directory.GetFiles(searchPath, "*", SearchOption.AllDirectories);
 
-            foreach (var file in files)
+            Parallel.ForEach(files, file =>
             {
                 var name = file.Substring(fixturesPath.Length + 1).Replace("\\", "/");
                 bool skip = _skipReasons.TryGetValue(name, out var reason);
@@ -175,10 +197,6 @@ namespace Jint.Tests.Test262
                                 skip = true;
                                 reason = "Symbol.species not implemented";
                                 break;
-                            case "Proxy":
-                                skip = true;
-                                reason = "Proxies not implemented";
-                                break;
                             case "object-spread":
                                 skip = true;
                                 reason = "Object spread not implemented";
@@ -246,7 +264,7 @@ namespace Jint.Tests.Test262
                         }
                     }
                 }
-
+                
                 if (code.IndexOf("SpecialCasing.txt") > -1)
                 {
                     skip = true;
@@ -281,7 +299,7 @@ namespace Jint.Tests.Test262
                         sourceFile
                     });
                 }
-            }
+            });
 
             return results;
         }

+ 8 - 49
Jint.Tests.Test262/test/skipped.json

@@ -1,4 +1,8 @@
 [
+  {
+    "source": "built-ins/Proxy/enumerate/removed-does-not-trigger.js",
+    "reason": "for-of not implemented"
+  },
   {
     "source": "built-ins/Array/prototype/concat/create-ctor-non-object.js",
     "reason": "Constructor functions not implemented"
@@ -179,14 +183,6 @@
     "source": "built-ins/Set/prototype/values/does-not-have-setdata-internal-slot-weakset.js",
     "reason": "WeakSet not implemented"
   },
-  {
-    "source": "built-ins/Array/prototype/reverse/length-exceeding-integer-limit-with-proxy.js",
-    "reason": "proxies not implemented"
-  },
-  {
-    "source": "built-ins/Array/prototype/slice/length-exceeding-integer-limit-proxied-array.js",
-    "reason": "proxies not implemented"
-  },
   {
     "source": "built-ins/Array/prototype/splice/create-non-array-invalid-len.js",
     "reason": "requires constructor functions"
@@ -333,14 +329,6 @@
     "source": "language/statements/for-in/head-var-bound-names-dup.js",
     "reason": "destructing not implemented"
   },
-  {
-    "source": "language/rest-parameters/arrow-function.js",
-    "reason": "Github issue #600 - rest parameters hang when not set"
-  },
-  {
-    "source": "language/rest-parameters/object-pattern.js",
-    "reason": "destructing not implemented"
-  },
   {
     "source": "built-ins/Math/pow/int32_min-exponent.js",
     "reason": "const not implemented"
@@ -349,17 +337,9 @@
     "source": "language/expressions/template-literal/tv-line-terminator-sequence.js",
     "reason": "Line feed problems (git, windows, linux)"
   },
-  {
-    "source": "built-ins/Number/prototype/toPrecision/range.js",
-    "reason": "Github issue #599 - dtoa function not precise enough"
-  },
   {
     "source": "built-ins/Number/prototype/toFixed/range.js",
-    "reason": "Github issue #599 - dtoa function not precise enough"
-  },
-  {
-    "source": "built-ins/Number/prototype/toExponential/range.js",
-    "reason": "Github issue #599 - dtoa function not precise enough"
+    "reason": "100 fraction digits is not supported due to .NET format specifier limitation"
   },
   {
     "source": "language/types/number/8.5.1.js",
@@ -584,41 +564,20 @@
 
   {
     "source": "language/expressions/object/method.js",
-    "reason": "setPrototypeOf not implemented"
+    "reason": "super not implemented"
   },
   {
     "source": "language/expressions/object/setter-super-prop.js",
-    "reason": "setPrototypeOf not implemented"
+    "reason": "super not implemented"
   },
   {
     "source": "language/expressions/object/getter-super-prop.js",
-    "reason": "setPrototypeOf not implemented"
+    "reason": "super not implemented"
   },
 
 
   {
     "source": "language/expressions/object/fn-name-arrow.js",
-    "reason": "symbols not identifiable in property name"
-  },
-  {
-    "source": "language/expressions/object/fn-name-cover.js",
-    "reason": "symbols not identifiable in property name"
-  },
-  {
-    "source": "language/expressions/object/fn-name-fn.js",
-    "reason": "symbols not identifiable in property name"
-  },
-  {
-    "source": "language/expressions/object/method-definition/fn-name-fn.js",
-    "reason": "symbols not identifiable in property name"
-  },
-  {
-    "source": "language/expressions/object/method-definition/name-name-prop-symbol.js",
-    "reason": "symbols not identifiable in property name"
-  },
-
-
-  {
     "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)"
   },

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

@@ -12,7 +12,7 @@
     <Reference Include="Microsoft.CSharp" Condition=" '$(TargetFramework)' == 'net452' " />
   </ItemGroup>
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.analyzers" Version="0.10.0" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />

+ 3 - 3
Jint.Tests/Runtime/EngineTests.cs

@@ -558,7 +558,7 @@ namespace Jint.Tests.Runtime
                     str += z;
                 }
 
-                assert(str == 'xystrz');
+                equal('xystrz', str);
             ");
         }
 
@@ -987,7 +987,7 @@ namespace Jint.Tests.Runtime
             ");
 
             var obj = _engine.GetValue("obj").AsObject();
-            var getFoo = obj.Get("getFoo");
+            var getFoo = obj.Get("getFoo", obj);
 
             Assert.Equal("foo is 5, bar is 7", _engine.Invoke(getFoo, obj, new object[] { 7 }).AsString());
         }
@@ -1000,7 +1000,7 @@ namespace Jint.Tests.Runtime
             ");
 
             var obj = _engine.GetValue("obj").AsObject();
-            var foo = obj.Get("foo");
+            var foo = obj.Get("foo", obj);
 
             Assert.Throws<ArgumentException>(() => _engine.Invoke(foo, obj, new object[] { }));
         }

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

@@ -468,8 +468,8 @@ namespace Jint.Tests.Runtime
         [Fact]
         public void PocosCanReturnObjectInstanceDirectly()
         {
-            var x = new ObjectInstance(_engine) { Extensible = true};
-            x.Put("foo", new JsString("bar"), false);
+            var x = new ObjectInstance(_engine);
+            x.Set("foo", new JsString("bar"));
 
             var o = new
             {

+ 1 - 1
Jint.Tests/Runtime/NullPropagation.cs

@@ -100,7 +100,7 @@ function test2(arg) {
             var engine = new Engine(cfg => cfg.SetReferencesResolver(new NullPropagationReferenceResolver()));
 
             var jsObject = engine.Object.Construct(Arguments.Empty);
-            jsObject.Put("NullField", JsValue.Null, true);
+            jsObject.Set("NullField", JsValue.Null);
 
             var script = @"
 this.is_nullfield_not_null = this.NullField !== null;

+ 1 - 1
Jint.Tests/Runtime/ObjectInstanceTests.cs

@@ -16,7 +16,7 @@ namespace Jint.Tests.Runtime
             instance.FastAddProperty("scope", JsValue.Null, true, true, true);
             instance.RemoveOwnProperty("bare");
             var propertyNames = instance.GetOwnProperties().Select(x => x.Key).ToList();
-            Assert.Equal(new [] { "scope" }, propertyNames);
+            Assert.Equal(new Key[] { "scope" }, propertyNames);
         }
     }
 }

+ 7 - 7
Jint/Collections/StringDictionarySlim.cs

@@ -19,7 +19,7 @@ namespace Jint.Collections
     /// </summary>
     [DebuggerTypeProxy(typeof(DictionarySlimDebugView<>))]
     [DebuggerDisplay("Count = {Count}")]
-    internal sealed class StringDictionarySlim<TValue> : IReadOnlyCollection<KeyValuePair<string, TValue>>
+    internal sealed class StringDictionarySlim<TValue> : IReadOnlyCollection<KeyValuePair<Key, TValue>>
     {
         // We want to initialize without allocating arrays. We also want to avoid null checks.
         // Array.Empty would give divide by zero in modulo operation. So we use static one element arrays.
@@ -228,7 +228,7 @@ namespace Jint.Collections
         /// <summary>
         /// Gets an enumerator over the dictionary
         /// </summary>
-        IEnumerator<KeyValuePair<string, TValue>> IEnumerable<KeyValuePair<string, TValue>>.GetEnumerator() =>
+        IEnumerator<KeyValuePair<Key, TValue>> IEnumerable<KeyValuePair<Key, TValue>>.GetEnumerator() =>
             new Enumerator(this);
 
         /// <summary>
@@ -236,12 +236,12 @@ namespace Jint.Collections
         /// </summary>
         IEnumerator IEnumerable.GetEnumerator() => new Enumerator(this);
 
-        public struct Enumerator : IEnumerator<KeyValuePair<string, TValue>>
+        public struct Enumerator : IEnumerator<KeyValuePair<Key, TValue>>
         {
             private readonly StringDictionarySlim<TValue> _dictionary;
             private int _index;
             private int _count;
-            private KeyValuePair<string, TValue> _current;
+            private KeyValuePair<Key, TValue> _current;
 
             internal Enumerator(StringDictionarySlim<TValue> dictionary)
             {
@@ -264,13 +264,13 @@ namespace Jint.Collections
                 while (_dictionary._entries[_index].next < -1)
                     _index++;
 
-                _current = new KeyValuePair<string, TValue>(
+                _current = new KeyValuePair<Key, TValue>(
                     _dictionary._entries[_index].key,
                     _dictionary._entries[_index++].value);
                 return true;
             }
 
-            public KeyValuePair<string, TValue> Current => _current;
+            public KeyValuePair<Key, TValue> Current => _current;
 
             object IEnumerator.Current => _current;
 
@@ -307,7 +307,7 @@ namespace Jint.Collections
             }
 
             [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
-            public KeyValuePair<string, V>[] Items
+            public KeyValuePair<Key, V>[] Items
             {
                 get
                 {

+ 1 - 1
Jint/Directory.Build.props

@@ -20,7 +20,7 @@
   </PropertyGroup>
 
   <ItemGroup Condition="'$(SourceLinkEnabled)' != 'false'">
-    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0-beta2-19367-01" PrivateAssets="All"/>
+    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All"/>
   </ItemGroup>
 
   <!-- Called after so that the <VersionSuffix> is built after the local ones -->

+ 36 - 44
Jint/Engine.cs

@@ -16,6 +16,8 @@ using Jint.Native.Map;
 using Jint.Native.Math;
 using Jint.Native.Number;
 using Jint.Native.Object;
+using Jint.Native.Proxy;
+using Jint.Native.Reflect;
 using Jint.Native.RegExp;
 using Jint.Native.Set;
 using Jint.Native.String;
@@ -30,6 +32,7 @@ using Jint.Runtime.Environments;
 using Jint.Runtime.Interop;
 using Jint.Runtime.Interpreter;
 using Jint.Runtime.References;
+using ExecutionContext = Jint.Runtime.Environments.ExecutionContext;
 
 namespace Jint
 {
@@ -184,6 +187,8 @@ namespace Jint
             Date = DateConstructor.CreateDateConstructor(this);
             Math = MathInstance.CreateMathObject(this);
             Json = JsonInstance.CreateJsonObject(this);
+            Proxy = ProxyConstructor.CreateProxyConstructor(this);
+            Reflect = ReflectInstance.CreateReflectObject(this);
 
             GlobalSymbolRegistry = new GlobalSymbolRegistry();
 
@@ -191,8 +196,8 @@ namespace Jint
             // their configuration is delayed to a later step
 
             // this is implementation dependent, and only to pass some unit tests
-            Global.Prototype = Object.PrototypeObject;
-            Object.Prototype = Function.PrototypeObject;
+            Global._prototype = Object.PrototypeObject;
+            Object._prototype = Function.PrototypeObject;
 
             // create the global environment http://www.ecma-international.org/ecma-262/5.1/#sec-10.2.3
             GlobalEnvironment = LexicalEnvironment.NewObjectEnvironment(this, Global, null, false);
@@ -219,7 +224,7 @@ namespace Jint
             _argumentsInstancePool = new ArgumentsInstancePool(this);
             _jsValueArrayPool = new JsValueArrayPool();
 
-            Eval = new EvalFunctionInstance(this, System.ArrayExt.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
+            Eval = new EvalFunctionInstance(this, ArrayExt.Empty<string>(), LexicalEnvironment.NewDeclarativeEnvironment(this, ExecutionContext.LexicalEnvironment), StrictModeScope.IsStrictModeCode);
             Global._properties[KnownKeys.Eval] = new PropertyDescriptor(Eval, true, false, true);
 
             if (Options._IsClrAllowed)
@@ -249,6 +254,8 @@ namespace Jint
         public DateConstructor Date { get; }
         public MathInstance Math { get; }
         public JsonInstance Json { get; }
+        public ProxyConstructor Proxy { get; }
+        public ReflectInstance Reflect { get; }
         public SymbolConstructor Symbol { get; }
         public EvalFunctionInstance Eval { get; }
 
@@ -334,7 +341,7 @@ namespace Jint
 
         public Engine SetValue(in Key name, JsValue value)
         {
-            Global.Put(name, value, false);
+            Global.Set(name, value, Global);
             return this;
         }
 
@@ -582,18 +589,26 @@ namespace Jint
                     ExceptionHelper.ThrowReferenceError(this, reference);
                 }
 
-                Global.Put(referencedName, value, false);
+                Global.Set(referencedName, value);
             }
             else if (reference.IsPropertyReference())
             {
                 var baseValue = reference._baseValue;
                 if (reference._baseValue._type == InternalTypes.Object || reference._baseValue._type == InternalTypes.None)
                 {
-                    ((ObjectInstance) baseValue).Put(referencedName, value, reference._strict);
+                    var thisValue = GetThisValue(reference);
+                    var succeeded = ((ObjectInstance) thisValue).Set(referencedName, value, thisValue);
+                    if (!succeeded && reference._strict)
+                    {
+                        ExceptionHelper.ThrowTypeError(this);
+                    }
                 }
                 else
                 {
-                    PutPrimitiveBase(baseValue, referencedName, value, reference._strict);
+                    if (!PutPrimitiveBase(baseValue, referencedName, value) && reference._strict)
+                    {
+                        ExceptionHelper.ThrowTypeError(this);
+                    }
                 }
             }
             else
@@ -603,46 +618,23 @@ namespace Jint
             }
         }
 
-        /// <summary>
-        /// Used by PutValue when the reference has a primitive base value
-        /// </summary>
-        public void PutPrimitiveBase(JsValue b, in Key name, JsValue value, bool throwOnError)
+        private static JsValue GetThisValue(Reference reference)
         {
-            var o = TypeConverter.ToObject(this, b);
-            if (!o.CanPut(name))
+            if (reference.IsSuperReference())
             {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(this);
-                }
-                return;
+                return ExceptionHelper.ThrowNotImplementedException<JsValue>();
             }
 
-            var ownDesc = o.GetOwnProperty(name);
-
-            if (ownDesc.IsDataDescriptor())
-            {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(this);
-                }
-                return;
-            }
-
-            var desc = o.GetProperty(name);
+            return reference._baseValue;
+        }
 
-            if (desc.IsAccessorDescriptor())
-            {
-                var setter = (ICallable)desc.Set.AsObject();
-                setter.Call(b, new[] { value });
-            }
-            else
-            {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(this);
-                }
-            }
+        /// <summary>
+        /// Used by PutValue when the reference has a primitive base value
+        /// </summary>
+        public bool PutPrimitiveBase(JsValue b, in Key name, JsValue value)
+        {
+            var o = TypeConverter.ToObject(this, b);
+            return o.Set(name, b, o);
         }
 
         /// <summary>
@@ -813,7 +805,7 @@ namespace Jint
                     for (var j = 0; j < declarationsCount; j++)
                     {
                         var d = declarations[j];
-                        if (d.Id is Esprima.Ast.Identifier id1)
+                        if (d.Id is Identifier id1)
                         {
                             Key name = id1.Name;
                             var varAlreadyDeclared = env.HasBinding(name);
@@ -861,7 +853,7 @@ namespace Jint
                             }
 
                             var descriptor = new PropertyDescriptor(Undefined.Instance, flags);
-                            go.DefineOwnProperty(fn, descriptor, true);
+                            go.DefinePropertyOrThrow(fn, descriptor);
                         }
                         else
                         {

+ 20 - 22
Jint/EsprimaExtensions.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Runtime.CompilerServices;
 using Esprima.Ast;
-using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime.Interpreter.Expressions;
 
@@ -9,44 +8,43 @@ namespace Jint
 {
     public static class EsprimaExtensions
     {
-        public static string GetKey<T>(this T expression, Engine engine) where T : class, Expression
+        public static Key GetKey(this Property property, Engine engine) => GetKey(property.Key, engine, property.Computed);
+
+        internal static Key GetKey<T>(this T expression, Engine engine, bool computed) where T : class, Expression
         {
             if (expression is Literal literal)
             {
                 return literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
             }
 
-            if (expression is Esprima.Ast.Identifier identifier)
+            if (!computed && expression is Identifier identifier)
             {
                 return identifier.Name;
             }
 
-            if (expression is StaticMemberExpression staticMemberExpression)
+            if (!TryGetComputedPropertyKey(expression, engine, out var propertyKey))
             {
-                var obj = staticMemberExpression.Object.GetKey(engine);
-                var property = staticMemberExpression.Property.GetKey(engine);
-
-                if (obj == "Symbol")
-                {
-                    if (property == "iterator")
-                    {
-                        return GlobalSymbolRegistry.Iterator._value;
-                    }
-                    if (property == "toPrimitive")
-                    {
-                        return GlobalSymbolRegistry.ToPrimitive._value;
-                    }
-                }
+                ExceptionHelper.ThrowArgumentException("Unable to extract correct key, node type: " + expression.Type);
             }
 
-            if (expression.Type == Nodes.CallExpression
+            return propertyKey;
+        }
+
+        private static bool TryGetComputedPropertyKey<T>(T expression, Engine engine, out Key propertyKey)
+            where T : class, Expression
+        {
+            if (expression.Type == Nodes.Identifier
+                || expression.Type == Nodes.CallExpression
                 || expression.Type == Nodes.BinaryExpression
-                || expression.Type == Nodes.UpdateExpression)
+                || expression.Type == Nodes.UpdateExpression
+                || expression is StaticMemberExpression)
             {
-                return Convert.ToString(JintExpression.Build(engine, expression).GetValue());
+                propertyKey = JintExpression.Build(engine, expression).GetValue().ToPropertyKey();
+                return true;
             }
 
-            return ExceptionHelper.ThrowArgumentException<string>("Unable to extract correct key, node type: " + expression.Type);
+            propertyKey = "";
+            return false;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 1 - 1
Jint/JsValueExtensions.cs

@@ -59,7 +59,7 @@ namespace Jint
                 ThrowWrongTypeException(value, "symbol");
             }
 
-            return ((JsSymbol) value)._value;
+            return ((JsSymbol) value).ToPropertyKey();
         }
 
         private static void ThrowWrongTypeException(JsValue value, string expectedType)

+ 34 - 7
Jint/Key.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Diagnostics;
 using System.Runtime.CompilerServices;
+using Jint.Native;
 using Jint.Runtime;
 
 namespace Jint
@@ -23,24 +24,44 @@ namespace Jint
             }
         }
 
-        public Key(string name)
+        public Key(string name) : this(name, symbolIdentity: 0)
+        {
+        }
+
+        internal Key(string name, int symbolIdentity)
         {
             if (name == null)
             {
                 ExceptionHelper.ThrowArgumentException("name cannot be null");
             }
             Name = name;
-            HashCode = name.GetHashCode();
+            HashCode = name.GetHashCode() * 397 ^ symbolIdentity.GetHashCode();
+            _symbolIdentity = symbolIdentity;
         }
 
         public readonly string Name;
         internal readonly int HashCode;
+        internal readonly int _symbolIdentity;
+
+        public Types Type => _symbolIdentity == 0 ? Types.String : Types.Symbol;
+
+        public bool IsSymbol => _symbolIdentity != 0;
 
         public static implicit operator Key(string name)
         {
             return new Key(name);
         }
 
+        public static implicit operator Key(JsSymbol name)
+        {
+            return name.ToPropertyKey();
+        }
+
+        public static implicit operator JsValue(in Key key)
+        {
+            return !key.IsSymbol ? (JsValue) JsString.Create(key.Name) : new JsSymbol(key.Name, key._symbolIdentity);
+        }
+
         public static implicit operator Key(int value)
         {
             var keys = indexKeys;
@@ -53,6 +74,12 @@ namespace Jint
             return value < keys.Length ? keys[value] : BuildKey(value);
         }
 
+        public static implicit operator Key(ulong value)
+        {
+            var keys = indexKeys;
+            return value < (ulong) keys.Length ? keys[value] : new Key(value.ToString());
+        }
+
         [MethodImpl(MethodImplOptions.NoInlining)]
         private static Key BuildKey(long value)
         {
@@ -63,27 +90,27 @@ namespace Jint
 
         public static bool operator ==(in Key a, Key b)
         {
-            return a.HashCode == b.HashCode && a.Name == b.Name;
+            return a.HashCode == b.HashCode && a.Name == b.Name && a._symbolIdentity == b._symbolIdentity;
         }
 
         public static bool operator !=(in Key a, Key b)
         {
-            return a.HashCode != b.HashCode || a.Name != b.Name;
+            return a.HashCode != b.HashCode || a.Name != b.Name || a._symbolIdentity != b._symbolIdentity;
         }
 
         public static bool operator ==(in Key a, string b)
         {
-            return a.Name == b;
+            return a.Name == b && a._symbolIdentity == 0;
         }
 
         public static bool operator !=(in Key a, string b)
         {
-            return a.Name != b;
+            return a.Name != b || a._symbolIdentity > 0;
         }
 
         public bool Equals(Key other)
         {
-            return HashCode == other.HashCode && Name == other.Name;
+            return Name == other.Name && _symbolIdentity == other._symbolIdentity;
         }
 
         public override bool Equals(object obj)

+ 25 - 29
Jint/Native/Argument/ArgumentsInstance.cs

@@ -92,8 +92,8 @@ namespace Jint.Native.Argument
             // step 14
             else
             {
-                DefineOwnProperty(KnownKeys.Caller, _engine._getSetThrower, false);
-                DefineOwnProperty(KnownKeys.Callee, _engine._getSetThrower, false);
+                DefineOwnProperty(KnownKeys.Caller, _engine._getSetThrower);
+                DefineOwnProperty(KnownKeys.Callee, _engine._getSetThrower);
             }
         }
 
@@ -125,18 +125,13 @@ namespace Jint.Native.Argument
         /// Implementation from ObjectInstance official specs as the one
         /// in ObjectInstance is optimized for the general case and wouldn't work
         /// for arrays
-        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
+        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
         {
             EnsureInitialized();
 
             if (!CanPut(propertyName))
             {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(Engine);
-                }
-
-                return;
+                return false;
             }
 
             var ownDesc = GetOwnProperty(propertyName);
@@ -144,8 +139,7 @@ namespace Jint.Native.Argument
             if (ownDesc.IsDataDescriptor())
             {
                 var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
-                DefineOwnProperty(propertyName, valueDesc, throwOnError);
-                return;
+                return DefineOwnProperty(propertyName, valueDesc);
             }
 
             // property is an accessor or inherited
@@ -153,22 +147,27 @@ namespace Jint.Native.Argument
 
             if (desc.IsAccessorDescriptor())
             {
-                var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(this, new[] {value});
+                if (!(desc.Set is ICallable setter))
+                {
+                    return false;
+                }
+                setter.Call(receiver, new[] {value});
             }
             else
             {
                 var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
-                DefineOwnProperty(propertyName, newDesc, throwOnError);
+                return DefineOwnProperty(propertyName, newDesc);
             }
+
+            return true;
         }
 
-        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
         {
             if (_func is ScriptFunctionInstance scriptFunctionInstance && scriptFunctionInstance._function._hasRestParameter)
             {
                 // immutable
-                return false;
+                return true;
             }
 
             EnsureInitialized();
@@ -177,32 +176,29 @@ namespace Jint.Native.Argument
             {
                 var map = ParameterMap;
                 var isMapped = map.GetOwnProperty(propertyName);
-                var allowed = base.DefineOwnProperty(propertyName, desc, false);
+                var allowed = base.DefineOwnProperty(propertyName, desc);
                 if (!allowed)
                 {
-                    if (throwOnError)
-                    {
-                        ExceptionHelper.ThrowTypeError(Engine);
-                    }
+                    return false;
                 }
 
                 if (isMapped != PropertyDescriptor.Undefined)
                 {
                     if (desc.IsAccessorDescriptor())
                     {
-                        map.Delete(propertyName, false);
+                        map.Delete(propertyName);
                     }
                     else
                     {
                         var descValue = desc.Value;
                         if (!ReferenceEquals(descValue, null) && !descValue.IsUndefined())
                         {
-                            map.Put(propertyName, descValue, throwOnError);
+                            map.Set(propertyName, descValue, false);
                         }
 
                         if (desc.WritableSet && !desc.Writable)
                         {
-                            map.Delete(propertyName, false);
+                            map.Delete(propertyName);
                         }
                     }
                 }
@@ -210,10 +206,10 @@ namespace Jint.Native.Argument
                 return true;
             }
 
-            return base.DefineOwnProperty(propertyName, desc, throwOnError);
+            return base.DefineOwnProperty(propertyName, desc);
         }
 
-        public override bool Delete(in Key propertyName, bool throwOnError)
+        public override bool Delete(in Key propertyName)
         {
             EnsureInitialized();
 
@@ -221,16 +217,16 @@ namespace Jint.Native.Argument
             {
                 var map = ParameterMap;
                 var isMapped = map.GetOwnProperty(propertyName);
-                var result = base.Delete(propertyName, throwOnError);
+                var result = base.Delete(propertyName);
                 if (result && isMapped != PropertyDescriptor.Undefined)
                 {
-                    map.Delete(propertyName, false);
+                    map.Delete(propertyName);
                 }
 
                 return result;
             }
 
-            return base.Delete(propertyName, throwOnError);
+            return base.Delete(propertyName);
         }
 
         internal void PersistArguments()

+ 116 - 16
Jint/Native/Array/ArrayConstructor.cs

@@ -1,9 +1,11 @@
 using System.Collections;
+using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using Jint.Collections;
 using Jint.Native.Function;
 using Jint.Native.Iterator;
 using Jint.Native.Object;
+using Jint.Native.Proxy;
 using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -26,8 +28,7 @@ namespace Jint.Native.Array
         {
             var obj = new ArrayConstructor(engine)
             {
-                Extensible = true,
-                Prototype = engine.Function.PrototypeObject
+                _prototype = engine.Function.PrototypeObject
             };
 
             // The value of the [[Prototype]] internal property of the Array constructor is the Function prototype object
@@ -36,7 +37,7 @@ namespace Jint.Native.Array
             obj._length = new PropertyDescriptor(1, PropertyFlag.Configurable);
 
             // The initial value of Array.prototype is the Array prototype object
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -45,7 +46,7 @@ namespace Jint.Native.Array
         {
             _properties = new StringDictionarySlim<PropertyDescriptor>(5)
             {
-                [GlobalSymbolRegistry.Species._value] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined,PropertyFlag.Configurable),
+                [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(Engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined,PropertyFlag.Configurable),
                 ["from"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "from", From, 1, PropertyFlag.Configurable), PropertyFlag.NonEnumerable)),
                 ["isArray"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "isArray", IsArray, 1), PropertyFlag.NonEnumerable)),
                 ["of"] = new PropertyDescriptor(new PropertyDescriptor(new ClrFunctionInstance(Engine, "of", Of, 0, PropertyFlag.Configurable), PropertyFlag.NonEnumerable))
@@ -104,7 +105,7 @@ namespace Jint.Native.Array
             ICallable callable, 
             JsValue thisArg)
         {
-            var operations = ArrayPrototype.ArrayOperations.For(objectInstance);
+            var operations = ArrayOperations.For(objectInstance);
 
             var length = operations.GetLength();
 
@@ -191,7 +192,38 @@ namespace Jint.Native.Array
 
         private JsValue Of(JsValue thisObj, JsValue[] arguments)
         {
-            return _engine.Array.Construct(arguments);
+            var len = arguments.Length;
+            ObjectInstance a;
+            if (!ReferenceEquals(thisObj, this)
+                && thisObj.IsConstructor)
+            {
+                a = ((IConstructor) thisObj).Construct(arguments, thisObj);
+
+                for (uint k = 0; k < arguments.Length; k++)
+                {
+                    var kValue = arguments[k];
+                    var key = TypeConverter.ToString(k);
+                    a.CreateDataPropertyOrThrow(key, kValue);
+                }
+
+                a.Set(KnownKeys.Length, len, true);
+            }
+            else
+            {
+                // faster for real arrays
+                ArrayInstance ai;
+                a = ai = _engine.Array.Construct(len);
+
+                for (uint k = 0; k < arguments.Length; k++)
+                {
+                    var kValue = arguments[k];
+                    ai.SetIndexValue(k, kValue, updateLength: false);
+                }
+
+                ai.SetLength((uint) arguments.Length);
+            }
+
+            return a;
         }
 
         private static JsValue Species(JsValue thisObject, JsValue[] arguments)
@@ -201,22 +233,44 @@ namespace Jint.Native.Array
 
         private static JsValue IsArray(JsValue thisObj, JsValue[] arguments)
         {
-            if (arguments.Length == 0)
+            var o = arguments.At(0);
+
+            return IsArray(o);
+        }
+
+        private static JsValue IsArray(JsValue o)
+        {
+            if (!o.IsObject())
             {
-                return false;
+                return JsBoolean.False;
             }
 
-            var o = arguments.At(0);
+            if (o.AsObject().Class == "Array")
+            {
+                return JsBoolean.True;
+            }
+
+            if (o.AsObject().Class == "Proxy")
+            {
+                var proxyInstance = (ProxyInstance) o;
+                proxyInstance.AssertNotRevoked("isArray");
+                return IsArray(proxyInstance._target);
+            }
 
-            return o.IsObject() && o.AsObject().Class == "Array";
+            return JsBoolean.False;
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
-            return Construct(arguments);
+            return Construct(arguments, thisObject);
         }
 
         public ObjectInstance Construct(JsValue[] arguments)
+        {
+            return Construct(arguments, this);
+        }
+
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             // check if we can figure out good size
             var capacity = arguments.Length > 0 ? (uint) arguments.Length : 0;
@@ -248,8 +302,7 @@ namespace Jint.Native.Array
         public ArrayInstance Construct(JsValue[] arguments, uint capacity)
         {
             var instance = new ArrayInstance(Engine, capacity);
-            instance.Prototype = PrototypeObject;
-            instance.Extensible = true;
+            instance._prototype = PrototypeObject;
 
             if (arguments.Length == 1 && arguments.At(0).IsNumber())
             {
@@ -287,7 +340,7 @@ namespace Jint.Native.Array
 
         private ArrayInstance ConstructArrayFromIEnumerable(IEnumerable enumerable)
         {
-            var jsArray = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
+            var jsArray = (ArrayInstance) Construct(Arguments.Empty);
             var tempArray = _engine._jsValueArrayPool.RentArray(1);
             foreach (var item in enumerable)
             {
@@ -305,11 +358,58 @@ namespace Jint.Native.Array
         {
             var instance = new ArrayInstance(Engine, length)
             {
-                Prototype = PrototypeObject,
-                Extensible = true,
+                _prototype = PrototypeObject,
                 _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable)
             };
             return instance;
         }
+
+        public ArrayInstance ArraySpeciesCreate(ObjectInstance originalArray, uint length)
+        {
+            var isArray = originalArray.IsArray();
+            if (!isArray)
+            {
+                return ConstructFast(length);
+            }
+
+            var c = originalArray.Get("constructor");
+            // If IsConstructor(C) is true, then
+            // Let thisRealm be the current Realm Record.
+            // Let realmC be ? GetFunctionRealm(C).
+            // If thisRealm and realmC are not the same Realm Record, then
+            // If SameValue(C, realmC.[[Intrinsics]].[[%Array%]]) is true, set C to undefined.
+
+            if (c is ObjectInstance oi)
+            {
+                c = oi.Get(GlobalSymbolRegistry.Species) ?? Undefined;
+            }
+
+
+            if (c.IsUndefined())
+            {
+                return ConstructFast(length);
+            }
+
+            if (!c.IsConstructor)
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            return Construct(length);
+        }
+
+        internal JsValue CreateArrayFromList(List<JsValue> values)
+        {
+            var jsArray = ConstructFast((uint) values.Count);
+            var index = 0;
+            for (; index < values.Count; index++)
+            {
+                var item = values[index];
+                jsArray.SetIndexValue((uint) index, item, false);
+            }
+
+            jsArray.SetLength((uint) index);
+            return jsArray;
+        }
     }
 }

+ 75 - 69
Jint/Native/Array/ArrayInstance.cs

@@ -11,6 +11,7 @@ namespace Jint.Native.Array
         internal PropertyDescriptor _length;
 
         private const int MaxDenseArrayLength = 1024 * 10;
+        private const ulong MaxArrayLength = 4294967295;
 
         // we have dense and sparse, we usually can start with dense and fall back to sparse when necessary
         internal PropertyDescriptor[] _dense;
@@ -55,23 +56,16 @@ namespace Jint.Native.Array
             _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
 
-        internal override bool IsConcatSpreadable => !TryGetIsConcatSpreadable(out var isConcatSpreadable) || isConcatSpreadable;
-
         internal override bool IsArrayLike => true;
 
         /// Implementation from ObjectInstance official specs as the one
         /// in ObjectInstance is optimized for the general case and wouldn't work
         /// for arrays
-        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
+        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
         {
             if (!CanPut(propertyName))
             {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(Engine);
-                }
-
-                return;
+                return false;
             }
 
             var ownDesc = GetOwnProperty(propertyName);
@@ -79,8 +73,7 @@ namespace Jint.Native.Array
             if (ownDesc.IsDataDescriptor())
             {
                 var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
-                DefineOwnProperty(propertyName, valueDesc, throwOnError);
-                return;
+                return DefineOwnProperty(propertyName, valueDesc);
             }
 
             // property is an accessor or inherited
@@ -89,16 +82,18 @@ namespace Jint.Native.Array
             if (desc.IsAccessorDescriptor())
             {
                 var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(this, new[] {value});
+                setter.Call(receiver, new[] {value});
             }
             else
             {
                 var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
-                DefineOwnProperty(propertyName, newDesc, throwOnError);
+                return DefineOwnProperty(propertyName, newDesc);
             }
+
+            return true;
         }
 
-        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
         {
             var oldLenDesc = _length;
             var oldLen = (uint) TypeConverter.ToNumber(oldLenDesc.Value);
@@ -108,7 +103,7 @@ namespace Jint.Native.Array
                 var value = desc.Value;
                 if (ReferenceEquals(value, null))
                 {
-                    return base.DefineOwnProperty("length", desc, throwOnError);
+                    return base.DefineOwnProperty("length", desc);
                 }
 
                 var newLenDesc = new PropertyDescriptor(desc);
@@ -121,16 +116,11 @@ namespace Jint.Native.Array
                 newLenDesc.Value = newLen;
                 if (newLen >= oldLen)
                 {
-                    return base.DefineOwnProperty("length", newLenDesc, throwOnError);
+                    return base.DefineOwnProperty("length", newLenDesc);
                 }
 
                 if (!oldLenDesc.Writable)
                 {
-                    if (throwOnError)
-                    {
-                        ExceptionHelper.ThrowTypeError(_engine);
-                    }
-
                     return false;
                 }
 
@@ -145,7 +135,7 @@ namespace Jint.Native.Array
                     newLenDesc.Writable = true;
                 }
 
-                var succeeded = base.DefineOwnProperty("length", newLenDesc, throwOnError);
+                var succeeded = base.DefineOwnProperty("length", newLenDesc);
                 if (!succeeded)
                 {
                     return false;
@@ -175,13 +165,7 @@ namespace Jint.Native.Array
                                         newLenDesc.Writable = false;
                                     }
 
-                                    base.DefineOwnProperty("length", newLenDesc, false);
-
-                                    if (throwOnError)
-                                    {
-                                        ExceptionHelper.ThrowTypeError(_engine);
-                                    }
-
+                                    base.DefineOwnProperty("length", newLenDesc);
                                     return false;
                                 }
                             }
@@ -200,7 +184,7 @@ namespace Jint.Native.Array
                             // is it the index of the array
                             if (keyIndex >= newLen && keyIndex < oldLen)
                             {
-                                var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex), false);
+                                var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex));
                                 if (!deleteSucceeded)
                                 {
                                     newLenDesc.Value = JsNumber.Create(keyIndex + 1);
@@ -209,13 +193,7 @@ namespace Jint.Native.Array
                                         newLenDesc.Writable = false;
                                     }
 
-                                    base.DefineOwnProperty("length", newLenDesc, false);
-
-                                    if (throwOnError)
-                                    {
-                                        ExceptionHelper.ThrowTypeError(_engine);
-                                    }
-
+                                    base.DefineOwnProperty("length", newLenDesc);
                                     return false;
                                 }
                             }
@@ -228,7 +206,7 @@ namespace Jint.Native.Array
                     {
                         // algorithm as per the spec
                         oldLen--;
-                        var deleteSucceeded = Delete(TypeConverter.ToString(oldLen), false);
+                        var deleteSucceeded = Delete(TypeConverter.ToString(oldLen));
                         if (!deleteSucceeded)
                         {
                             newLenDesc.Value = oldLen + 1;
@@ -237,13 +215,7 @@ namespace Jint.Native.Array
                                 newLenDesc.Writable = false;
                             }
 
-                            base.DefineOwnProperty("length", newLenDesc, false);
-
-                            if (throwOnError)
-                            {
-                                ExceptionHelper.ThrowTypeError(_engine);
-                            }
-
+                            base.DefineOwnProperty("length", newLenDesc);
                             return false;
                         }
                     }
@@ -251,7 +223,7 @@ namespace Jint.Native.Array
 
                 if (!newWritable)
                 {
-                    DefineOwnProperty("length", new PropertyDescriptor(value: null, PropertyFlag.WritableSet), false);
+                    base.DefineOwnProperty("length", new PropertyDescriptor(value: null, PropertyFlag.WritableSet));
                 }
 
                 return true;
@@ -260,35 +232,25 @@ namespace Jint.Native.Array
             {
                 if (index >= oldLen && !oldLenDesc.Writable)
                 {
-                    if (throwOnError)
-                    {
-                        ExceptionHelper.ThrowTypeError(_engine);
-                    }
-
                     return false;
                 }
 
-                var succeeded = base.DefineOwnProperty(propertyName, desc, false);
+                var succeeded = base.DefineOwnProperty(propertyName, desc);
                 if (!succeeded)
                 {
-                    if (throwOnError)
-                    {
-                        ExceptionHelper.ThrowTypeError(_engine);
-                    }
-
                     return false;
                 }
 
                 if (index >= oldLen)
                 {
                     oldLenDesc.Value = index + 1;
-                    base.DefineOwnProperty("length", oldLenDesc, false);
+                    base.DefineOwnProperty("length", oldLenDesc);
                 }
 
                 return true;
             }
 
-            return base.DefineOwnProperty(propertyName, desc, throwOnError);
+            return base.DefineOwnProperty(propertyName, desc);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -318,14 +280,41 @@ namespace Jint.Native.Array
 
             return base.TryGetProperty(propertyName, out descriptor);
         }
-
-        public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
+        
+        internal override List<JsValue> GetOwnPropertyKeys(Types types)
         {
+            var properties = new List<JsValue>(_dense?.Length ?? 0 + 1);
+            if (_dense != null)
+            {
+                var length = System.Math.Min(_dense.Length, GetLength());
+                for (var i = 0; i < length; i++)
+                {
+                    if (_dense[i] != null)
+                    {
+                        properties.Add(TypeConverter.ToString(i));
+                    }
+                }
+            }
+            else
+            {
+                foreach (var entry in _sparse)
+                {
+                    properties.Add(TypeConverter.ToString(entry.Key));
+                }
+            }
+
             if (_length != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Length, _length);
+                properties.Add(KnownKeys.Length);
             }
 
+            properties.AddRange(base.GetOwnPropertyKeys(types));
+
+            return properties;
+        }
+
+        public override IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
+        {
             if (_dense != null)
             {
                 var length = System.Math.Min(_dense.Length, GetLength());
@@ -333,7 +322,7 @@ namespace Jint.Native.Array
                 {
                     if (_dense[i] != null)
                     {
-                        yield return new KeyValuePair<string, PropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
+                        yield return new KeyValuePair<Key, PropertyDescriptor>(TypeConverter.ToString(i), _dense[i]);
                     }
                 }
             }
@@ -341,10 +330,15 @@ namespace Jint.Native.Array
             {
                 foreach (var entry in _sparse)
                 {
-                    yield return new KeyValuePair<string, PropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
+                    yield return new KeyValuePair<Key, PropertyDescriptor>(TypeConverter.ToString(entry.Key), entry.Value);
                 }
             }
 
+            if (_length != null)
+            {
+                yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Length, _length);
+            }
+
             foreach (var entry in base.GetOwnProperties())
             {
                 yield return entry;
@@ -460,7 +454,7 @@ namespace Jint.Native.Array
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private static uint ParseArrayIndex(string p)
+        internal static uint ParseArrayIndex(string p)
         {
             if (p.Length == 0)
             {
@@ -560,8 +554,11 @@ namespace Jint.Native.Array
         {
             value = Undefined;
 
-            TryGetDescriptor(index, out var desc);
-            desc = desc ?? GetProperty(index) ?? PropertyDescriptor.Undefined;
+            if (!TryGetDescriptor(index, out var desc))
+            {
+                desc = GetProperty(index);
+            }
+
             return desc.TryGetValue(this, out value);
         }
 
@@ -709,7 +706,10 @@ namespace Jint.Native.Array
             }
             else
             {
-                Put(KnownKeys.Length, newLength, true);
+                if (!Set(KnownKeys.Length, newLength, this))
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
             }
 
             return (uint) n;
@@ -734,7 +734,7 @@ namespace Jint.Native.Array
             }
             else
             {
-                DefineOwnProperty((uint) n, desc, true);
+                DefinePropertyOrThrow((uint) n, desc);
             }
         }
 
@@ -882,5 +882,11 @@ namespace Jint.Native.Array
                 }
             }
         }
+        
+        public override string ToString()
+        {
+            // debugger can make things hard when evaluates computed values
+            return "(" + (_length?._value.AsNumber() ?? 0) + ")[]";
+        }
     }
 }

+ 268 - 0
Jint/Native/Array/ArrayOperations.cs

@@ -0,0 +1,268 @@
+using Jint.Native.Number;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.Array
+{
+    internal abstract class ArrayOperations
+    {
+        protected internal const ulong MaxArrayLength = 4294967295;
+        protected internal const ulong MaxArrayLikeLength = NumberConstructor.MaxSafeInteger;
+
+        public static ArrayOperations For(ObjectInstance instance)
+        {
+            if (instance is ArrayInstance arrayInstance)
+            {
+                return new ArrayInstanceOperations(arrayInstance);
+            }
+
+            return new ObjectInstanceOperations(instance);
+        }
+
+        public static ArrayOperations For(Engine engine, JsValue thisObj)
+        {
+            var instance = TypeConverter.ToObject(engine, thisObj);
+            return For(instance);
+        }
+
+        public abstract ObjectInstance Target { get; }
+
+        public abstract ulong GetSmallestIndex(ulong length);
+
+        public abstract uint GetLength();
+
+        public abstract ulong GetLongLength();
+
+        public abstract void SetLength(ulong length);
+
+        public abstract void EnsureCapacity(ulong capacity);
+
+        public abstract JsValue Get(ulong index);
+
+        public virtual JsValue[] GetAll(Types elementTypes)
+        {
+            var n = (int) GetLength();
+            var jsValues = new JsValue[n];
+            for (uint i = 0; i < (uint) jsValues.Length; i++)
+            {
+                var jsValue = Get(i);
+                if ((jsValue.Type & elementTypes) == 0)
+                {
+                    ExceptionHelper.ThrowTypeErrorNoEngine<object>("invalid type");
+                }
+
+                jsValues[i] = jsValue;
+            }
+
+            return jsValues;
+        }
+
+        public abstract bool TryGetValue(ulong index, out JsValue value);
+
+        public bool HasProperty(ulong index) => Target.HasProperty(index);
+
+        public abstract void Set(ulong index, JsValue value, bool throwOnError);
+
+        public abstract void DeletePropertyOrThrow(ulong index);
+
+        private sealed class ObjectInstanceOperations : ArrayOperations<ObjectInstance>
+        {
+            public ObjectInstanceOperations(ObjectInstance target) : base(target)
+            {
+            }
+
+            private double GetIntegerLength()
+            {
+                var descValue = _target.Get("length", _target);
+                if (!ReferenceEquals(descValue, null))
+                {
+                    return TypeConverter.ToInteger(descValue);
+                }
+
+                return 0;
+            }
+
+            public override ulong GetSmallestIndex(ulong length)
+            {
+                // there are some evil tests that iterate a lot with unshift..
+                if (_target._properties == null)
+                {
+                    return 0;
+                }
+
+                var min = length;
+                foreach (var entry in _target._properties)
+                {
+                    if (ulong.TryParse(entry.Key, out var index))
+                    {
+                        min = System.Math.Min(index, min);
+                    }
+                }
+
+                if (_target.Prototype?._properties != null)
+                {
+                    foreach (var entry in _target.Prototype._properties)
+                    {
+                        if (ulong.TryParse(entry.Key, out var index))
+                        {
+                            min = System.Math.Min(index, min);
+                        }
+                    }
+                }
+
+                return min;
+            }
+
+            public override uint GetLength()
+            {
+                var integerLength = GetIntegerLength();
+                return (uint) (integerLength >= 0 ? integerLength : 0);
+            }
+
+            public override ulong GetLongLength()
+            {
+                var integerLength = GetIntegerLength();
+                if (integerLength <= 0)
+                {
+                    return 0;
+                }
+
+                return (ulong) System.Math.Min(integerLength, MaxArrayLikeLength);
+            }
+
+            public override void SetLength(ulong length)
+            {
+                _target.Set("length", length, true);
+            }
+
+            public override void EnsureCapacity(ulong capacity)
+            {
+            }
+
+            public override JsValue Get(ulong index)
+            {
+                return _target.Get(TypeConverter.ToString(index), _target);
+            }
+
+            public override bool TryGetValue(ulong index, out JsValue value)
+            {
+                var propertyName = TypeConverter.ToString(index);
+                var property = _target.GetProperty(propertyName);
+                var kPresent = property != PropertyDescriptor.Undefined;
+                value = kPresent ? _target.UnwrapJsValue(property) : JsValue.Undefined;
+                return kPresent;
+            }
+
+            public override void Set(ulong index, JsValue value, bool throwOnError)
+            {
+                _target.Set(TypeConverter.ToString(index), value, throwOnError);
+            }
+
+            public override void DeletePropertyOrThrow(ulong index)
+            {
+                _target.DeletePropertyOrThrow(TypeConverter.ToString(index));
+            }
+        }
+
+        private sealed class ArrayInstanceOperations : ArrayOperations<ArrayInstance>
+        {
+            public ArrayInstanceOperations(ArrayInstance target) : base(target)
+            {
+            }
+
+            public override ulong GetSmallestIndex(ulong length)
+            {
+                return _target.GetSmallestIndex();
+            }
+
+            public override uint GetLength()
+            {
+                return (uint) ((JsNumber) _target._length._value)._value;
+            }
+
+            public override ulong GetLongLength()
+            {
+                return (ulong) ((JsNumber) _target._length._value)._value;
+            }
+
+            public override void SetLength(ulong length)
+            {
+                _target.Set("length", length, true);
+            }
+
+            public override void EnsureCapacity(ulong capacity)
+            {
+                _target.EnsureCapacity((uint) capacity);
+            }
+
+            public override bool TryGetValue(ulong index, out JsValue value)
+            {
+                // array max size is uint
+                return _target.TryGetValue((uint) index, out value);
+            }
+
+            public override JsValue Get(ulong index)
+            {
+                return _target.Get((uint) index);
+            }
+
+            public override JsValue[] GetAll(Types elementTypes)
+            {
+                var n = _target.Length;
+
+                if (_target._dense == null || _target._dense.Length < n)
+                {
+                    return base.GetAll(elementTypes);
+                }
+
+                // optimized
+                var jsValues = new JsValue[n];
+                for (uint i = 0; i < (uint) jsValues.Length; i++)
+                {
+                    var prop = _target._dense[i] ?? PropertyDescriptor.Undefined;
+                    if (prop == PropertyDescriptor.Undefined)
+                    {
+                        prop = _target.Prototype?.GetProperty(i) ?? PropertyDescriptor.Undefined;
+                    }
+
+                    var jsValue = _target.UnwrapJsValue(prop);
+                    if ((jsValue.Type & elementTypes) == 0)
+                    {
+                        ExceptionHelper.ThrowTypeErrorNoEngine<object>("invalid type");
+                    }
+
+                    jsValues[i] = jsValue;
+                }
+
+                return jsValues;
+            }
+
+            public override void DeletePropertyOrThrow(ulong index)
+            {
+                _target.DeleteAt((uint) index);
+            }
+
+            public override void Set(ulong index, JsValue value, bool throwOnError)
+            {
+                _target.SetIndexValue((uint) index, value, throwOnError);
+            }
+        }
+    }
+
+    /// <summary>
+    ///     Adapter to use optimized array operations when possible.
+    ///     Gaps the difference between ArgumentsInstance and ArrayInstance.
+    /// </summary>
+    internal abstract class ArrayOperations<T> : ArrayOperations where T : ObjectInstance
+    {
+        protected readonly T _target;
+
+        protected ArrayOperations(T target)
+        {
+            _target = target;
+        }
+
+        public override ObjectInstance Target => _target;
+    }
+}

+ 62 - 282
Jint/Native/Array/ArrayPrototype.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Collections.Generic;
 using Jint.Collections;
-using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.Symbol;
 using Jint.Pooling;
@@ -29,13 +28,10 @@ namespace Jint.Native.Array
         {
             var obj = new ArrayPrototype(engine)
             {
-                Extensible = true,
-                Prototype = engine.Object.PrototypeObject,
+                _prototype = engine.Object.PrototypeObject,
                 _length = new PropertyDescriptor(JsNumber.PositiveZero, PropertyFlag.Writable),
                 _arrayConstructor = arrayConstructor,
             };
-
-
             return obj;
         }
 
@@ -73,7 +69,7 @@ namespace Jint.Native.Array
                 ["findIndex"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "findIndex", FindIndex, 1, PropertyFlag.Configurable), true, false, true),
                 ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Keys, 0, PropertyFlag.Configurable), true, false, true),
                 ["values"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "values", Values, 0, PropertyFlag.Configurable), true, false, true),
-                [GlobalSymbolRegistry.Iterator._value] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Values, 1), true, false, true)
+                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Values, 1), true, false, true)
             };
         }
         
@@ -146,7 +142,7 @@ namespace Jint.Native.Array
 
             for (var i = actualStart; i < actualEnd; ++i)
             {
-                operations.Put(i, value, false);
+                operations.Set(i, value, false);
             }
 
             return thisObj;
@@ -198,13 +194,15 @@ namespace Jint.Native.Array
 
             while (count > 0)
             {
-                if (operations.TryGetValue((ulong) from, out var value))
+                var fromPresent = operations.HasProperty((ulong) from);
+                if (fromPresent)
                 {
-                    operations.Put((ulong) to, value, false);
+                    var fromValue = operations.Get((ulong) from);
+                    operations.Set((ulong) to, fromValue, throwOnError: true);
                 }
                 else
                 {
-                    operations.DeleteAt((ulong) to);
+                    operations.DeletePropertyOrThrow((ulong) to);
                 }
                 from = (uint) (from + direction);
                 to = (uint) (to + direction);
@@ -262,9 +260,11 @@ namespace Jint.Native.Array
             var i = (uint) k;
             for (;; i--)
             {
-                if (o.TryGetValue(i, out var value))
+                var kPresent = o.HasProperty(i);
+                if (kPresent)
                 {
-                    var same = JintBinaryExpression.StrictlyEqual(value, searchElement);
+                    var elementK = o.Get(i);
+                    var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
                     if (same)
                     {
                         return i;
@@ -321,6 +321,7 @@ namespace Jint.Native.Array
             }
 
             var args = new JsValue[4];
+            args[3] = o.Target;
             while (k < len)
             {
                 var i = (uint) k;
@@ -329,7 +330,6 @@ namespace Jint.Native.Array
                     args[0] = accumulator;
                     args[1] = kvalue;
                     args[2] = i;
-                    args[3] = o.Target;
                     accumulator = callable.Call(Undefined, args);
                 }
 
@@ -349,7 +349,7 @@ namespace Jint.Native.Array
 
             var callable = GetCallable(callbackfn);
 
-            var a = Engine.Array.ConstructFast(0);
+            var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), 0);
 
             uint to = 0;
             var args = _engine._jsValueArrayPool.RentArray(3);
@@ -394,7 +394,7 @@ namespace Jint.Native.Array
             var thisArg = arguments.At(1);
             var callable = GetCallable(callbackfn);
 
-            var a = Engine.Array.ConstructFast((uint) len);
+            var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), (uint) len);
             var args = _engine._jsValueArrayPool.RentArray(3);
             args[2] = o.Target;
             for (uint k = 0; k < len; k++)
@@ -449,7 +449,7 @@ namespace Jint.Native.Array
 
             var searchElement = arguments.At(0);
             var fromIndex = arguments.At(1, 0);
-            
+
             var n = TypeConverter.ToNumber(fromIndex);
             n = n > ArrayOperations.MaxArrayLikeLength
                 ? ArrayOperations.MaxArrayLikeLength
@@ -460,14 +460,14 @@ namespace Jint.Native.Array
                     ? n
                     : len - System.Math.Abs(n), 0);
 
-            bool SameValueZero(JsValue x, JsValue y) {
+            static bool SameValueZero(JsValue x, JsValue y) {
                 return x == y 
                              || (x is JsNumber xNum && y is JsNumber yNum && double.IsNaN(xNum._value) && double.IsNaN(yNum._value));
             }
 
             while (k < len)
             {
-                o.TryGetValue(k, out var value);
+                var value = o.Get(k);
                 if (SameValueZero(value, searchElement))
                 {
                     return true;
@@ -566,8 +566,10 @@ namespace Jint.Native.Array
             var searchElement = arguments.At(0);
             for (; k < len; k++)
             {
-                if (o.TryGetValue(k, out var elementK))
+                var kPresent = o.HasProperty(k);
+                if (kPresent)
                 {
+                    var elementK = o.Get(k);
                     var same = JintBinaryExpression.StrictlyEqual(elementK, searchElement);
                     if (same)
                     {
@@ -645,10 +647,10 @@ namespace Jint.Native.Array
 
             if (actualDeleteCount > ArrayOperations.MaxArrayLength)
             {
-                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Invalid array length");
+                return ExceptionHelper.ThrowRangeError<JsValue>(_engine, "Invalid array length");
             }
             
-            var a = Engine.Array.ConstructFast((uint) actualDeleteCount);
+            var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), (uint) actualDeleteCount);
             for (uint k = 0; k < actualDeleteCount; k++)
             {
                 if (o.TryGetValue(actualStart + k, out var fromValue))
@@ -668,17 +670,17 @@ namespace Jint.Native.Array
                     var to = k + (ulong) items.Length;
                     if (o.TryGetValue(from, out var fromValue))
                     {
-                        o.Put(to, fromValue, true);
+                        o.Set(to, fromValue, true);
                     }
                     else
                     {
-                        o.DeleteAt(to);
+                        o.DeletePropertyOrThrow(to);
                     }
                 }
 
                 for (var k = len; k > len - actualDeleteCount + (ulong) items.Length; k--)
                 {
-                    o.DeleteAt(k - 1);
+                    o.DeletePropertyOrThrow(k - 1);
                 }
             }
             else if ((ulong) items.Length > actualDeleteCount)
@@ -689,11 +691,11 @@ namespace Jint.Native.Array
                     var to =  k + (ulong) items.Length - 1;
                     if (o.TryGetValue(from, out var fromValue))
                     {
-                        o.Put(to, fromValue, true);
+                        o.Set(to, fromValue, true);
                     }
                     else
                     {
-                        o.DeleteAt(to);
+                        o.DeletePropertyOrThrow(to);
                     }
                 }
             }
@@ -701,7 +703,7 @@ namespace Jint.Native.Array
             for (uint k = 0; k < items.Length; k++)
             {
                 var e = items[k];
-                o.Put(k + actualStart, e, true);
+                o.Set(k + actualStart, e, true);
             }
 
             o.SetLength(length);
@@ -727,17 +729,17 @@ namespace Jint.Native.Array
                 var to = k + argCount - 1;
                 if (o.TryGetValue(from, out var fromValue))
                 {
-                    o.Put(to, fromValue, true);
+                    o.Set(to, fromValue, true);
                 }
                 else
                 {
-                    o.DeleteAt(to);
+                    o.DeletePropertyOrThrow(to);
                 }
             }
 
             for (uint j = 0; j < argCount; j++)
             {
-                o.Put(j, arguments[j], true);
+                o.Set(j, arguments[j], true);
             }
 
             o.SetLength(len + argCount);
@@ -846,11 +848,11 @@ namespace Jint.Native.Array
             {
                 if (!ReferenceEquals(array[i], null))
                 {
-                    obj.Put(i, array[i], false);
+                    obj.Set(i, array[i], false);
                 }
                 else
                 {
-                    obj.DeleteAt(i);
+                    obj.DeletePropertyOrThrow(i);
                 }
             }
 
@@ -900,7 +902,7 @@ namespace Jint.Native.Array
             }
 
             var length = (uint) System.Math.Max(0, (long) final - (long) k);
-            var a = Engine.Array.Construct(length);
+            var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), length);
             if (thisObj is ArrayInstance ai)
             {
                 a.CopyValues(ai, (uint) k, 0, length);
@@ -916,7 +918,7 @@ namespace Jint.Native.Array
                     }
                 }
             }
-            a.DefineOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.None), false);
+            a.DefineOwnProperty("length", new PropertyDescriptor(length, PropertyFlag.None));
 
             return a;
         }
@@ -937,15 +939,15 @@ namespace Jint.Native.Array
                 var to = k - 1;
                 if (o.TryGetValue(k, out var fromVal))
                 {
-                    o.Put(to, fromVal, true);
+                    o.Set(to, fromVal, true);
                 }
                 else
                 {
-                    o.DeleteAt(to);
+                    o.DeletePropertyOrThrow(to);
                 }
             }
 
-            o.DeleteAt(len - 1);
+            o.DeletePropertyOrThrow(len - 1);
             o.SetLength(len - 1);
 
             return first;
@@ -960,24 +962,29 @@ namespace Jint.Native.Array
             while (lower != middle)
             {
                 var upper = len - lower - 1;
-                var lowerExists = o.TryGetValue(lower, out var lowerValue);
-                var upperExists = o.TryGetValue(upper, out var upperValue);
+
+                var lowerExists = o.HasProperty(lower);
+                var lowerValue = lowerExists ? o.Get(lower) : null;
+
+                var upperExists = o.HasProperty(upper);
+                var upperValue = upperExists ? o.Get(upper) : null;
+                
                 if (lowerExists && upperExists)
                 {
-                    o.Put(lower, upperValue, true);
-                    o.Put(upper, lowerValue, true);
+                    o.Set(lower, upperValue, true);
+                    o.Set(upper, lowerValue, true);
                 }
 
                 if (!lowerExists && upperExists)
                 {
-                    o.Put(lower, upperValue, true);
-                    o.DeleteAt(upper);
+                    o.Set(lower, upperValue, true);
+                    o.DeletePropertyOrThrow(upper);
                 }
 
                 if (lowerExists && !upperExists)
                 {
-                    o.DeleteAt(lower);
-                    o.Put(upper, lowerValue, true);
+                    o.DeletePropertyOrThrow(lower);
+                    o.Set(upper, lowerValue, true);
                 }
 
                 lower++;
@@ -1048,7 +1055,7 @@ namespace Jint.Native.Array
             else
             {
                 var elementObj = TypeConverter.ToObject(Engine, firstElement);
-                var func = elementObj.Get("toLocaleString") as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(_engine);
+                var func = elementObj.Get("toLocaleString", elementObj) as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(_engine);
 
                 r = func.Call(elementObj, Arguments.Empty);
             }
@@ -1063,7 +1070,7 @@ namespace Jint.Native.Array
                 else
                 {
                     var elementObj = TypeConverter.ToObject(Engine, nextElement);
-                    var func = elementObj.Get("toLocaleString") as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(_engine);
+                    var func = elementObj.Get("toLocaleString", elementObj) as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(_engine);
                     r = func.Call(elementObj, Arguments.Empty);
                 }
 
@@ -1094,14 +1101,13 @@ namespace Jint.Native.Array
                 {
                     var isConcatSpreadable = objectInstance.IsConcatSpreadable;
                     hasObjectSpreadables |= isConcatSpreadable;
-                    var operations = ArrayOperations.For(objectInstance);
-                    increment = isConcatSpreadable ? operations.GetLength() : 1; 
+                    increment = isConcatSpreadable ? ArrayOperations.For(objectInstance).GetLength() : 1; 
                 }
                 capacity += increment;
             }
 
             uint n = 0;
-            var a = Engine.Array.ConstructFast(capacity);
+            var a = Engine.Array.ArraySpeciesCreate(TypeConverter.ToObject(_engine, thisObj), capacity);
             for (var i = 0; i < items.Count; i++)
             {
                 var e = items[i];
@@ -1133,7 +1139,7 @@ namespace Jint.Native.Array
 
             // this is not in the specs, but is necessary in case the last element of the last
             // array doesn't exist, and thus the length would not be incremented
-            a.DefineOwnProperty("length", new PropertyDescriptor(n, PropertyFlag.None), false);
+            a.DefineOwnProperty("length", new PropertyDescriptor(n, PropertyFlag.None));
 
             return a;
         }
@@ -1143,9 +1149,9 @@ namespace Jint.Native.Array
             var array = TypeConverter.ToObject(Engine, thisObj);
 
             ICallable func;
-            func = array.Get("join").TryCast<ICallable>(x =>
+            func = array.Get("join", array).TryCast<ICallable>(x =>
             {
-                func = Engine.Object.PrototypeObject.Get("toString").TryCast<ICallable>(y => ExceptionHelper.ThrowArgumentException());
+                func = Engine.Object.PrototypeObject.Get("toString", array).TryCast<ICallable>(y => ExceptionHelper.ThrowArgumentException());
             });
 
             if (array.IsArrayLike == false || func == null)
@@ -1226,9 +1232,9 @@ namespace Jint.Native.Array
             }
 
             // cast to double as we need to prevent an overflow
-            for (var i = 0; i < arguments.Length; i++)
+            foreach (var a in arguments)
             {
-                o.Put(n, arguments[i], true);
+                o.Set(n, a, true);
                 n++;
             }
 
@@ -1248,235 +1254,9 @@ namespace Jint.Native.Array
 
             len = len - 1;
             JsValue element = o.Get(len);
-            o.DeleteAt(len);
+            o.DeletePropertyOrThrow(len);
             o.SetLength(len);
             return element;
         }
-
-        /// <summary>
-        /// Adapter to use optimized array operations when possible.
-        /// Gaps the difference between ArgumentsInstance and ArrayInstance.
-        /// </summary>
-        internal abstract class ArrayOperations
-        {
-            protected internal const ulong MaxArrayLength = 4294967295;
-            protected internal const ulong MaxArrayLikeLength = NumberConstructor.MaxSafeInteger;
-
-            public abstract ObjectInstance Target { get; }
-
-            public abstract ulong GetSmallestIndex(ulong length);
-
-            public abstract uint GetLength();
-
-            public abstract ulong GetLongLength();
-
-            public abstract void SetLength(ulong length);
-
-            public abstract void EnsureCapacity(ulong capacity);
-
-            public abstract JsValue Get(ulong index);
-
-            public virtual JsValue[] GetAll()
-            {
-                var n = (int) GetLength();
-                var jsValues = new JsValue[n];
-                for (uint i = 0; i < (uint) jsValues.Length; i++)
-                {
-                    jsValues[i] = Get(i);
-                }
-
-                return jsValues;
-            }
-
-            public abstract bool TryGetValue(ulong index, out JsValue value);
-
-            public abstract void Put(ulong index, JsValue value, bool throwOnError);
-
-            public abstract void DeleteAt(ulong index);
-
-            public static ArrayOperations For(Engine engine, JsValue thisObj)
-            {
-                var instance = TypeConverter.ToObject(engine, thisObj);
-                return For(instance);
-            }
-
-            public static ArrayOperations For(ObjectInstance instance)
-            {
-                if (instance is ArrayInstance arrayInstance)
-                {
-                    return new ArrayInstanceOperations(arrayInstance);
-                }
-
-                return new ObjectInstanceOperations(instance);
-            }
-
-            internal sealed class ObjectInstanceOperations : ArrayOperations
-            {
-                private readonly ObjectInstance _instance;
-
-                public ObjectInstanceOperations(ObjectInstance instance)
-                {
-                    _instance = instance;
-                }
-
-                public override ObjectInstance Target => _instance;
-
-                internal double GetIntegerLength()
-                {
-                    var desc = _instance.GetProperty("length");
-                    var descValue = desc.Value;
-                    if (desc.IsDataDescriptor() && !ReferenceEquals(descValue, null))
-                    {
-                        return TypeConverter.ToInteger(descValue);
-                    }
-
-                    var getter = desc.Get ?? Undefined;
-                    if (getter.IsUndefined())
-                    {
-                        return 0;
-                    }
-
-                    // if getter is not undefined it must be ICallable
-                    var callable = (ICallable) getter;
-                    var value = callable.Call(_instance, Arguments.Empty);
-                    return TypeConverter.ToInteger(value);
-                }
-
-                public override ulong GetSmallestIndex(ulong length)
-                {
-                    // there are some evil tests that iterate a lot with unshift..
-                    if (_instance._properties == null)
-                    {
-                        return 0;
-                    }
-
-                    ulong min = length;
-                    foreach (var entry in _instance._properties)
-                    {
-                        if (ulong.TryParse(entry.Key, out var index))
-                        {
-                            min = System.Math.Min(index, min);
-                        }
-                    }
-
-                    if (_instance.Prototype?._properties != null)
-                    {
-                        foreach (var entry  in _instance.Prototype._properties)
-                        {
-                            if (ulong.TryParse(entry.Key, out var index))
-                            {
-                                min = System.Math.Min(index, min);
-                            }
-                        }
-                    }
-
-                    return min;
-                }
-
-                public override uint GetLength()
-                {
-                    var integerLength = GetIntegerLength();
-                    return (uint) (integerLength >= 0 ? integerLength : 0);
-                }
-
-                public override ulong GetLongLength()
-                {
-                    var integerLength = GetIntegerLength();
-                    if (integerLength <= 0)
-                    {
-                        return 0;
-                    }
-                    return (ulong) System.Math.Min(integerLength, MaxArrayLikeLength);
-                }
-
-                public override void SetLength(ulong length) => _instance.Put("length", length, true);
-
-                public override void EnsureCapacity(ulong capacity)
-                {
-                }
-
-                public override JsValue Get(ulong index) => _instance.Get(TypeConverter.ToString(index));
-
-                public override bool TryGetValue(ulong index, out JsValue value)
-                {
-                    var property = TypeConverter.ToString(index);
-                    var kPresent = _instance.HasProperty(property);
-                    value = kPresent ? _instance.Get(property) : Undefined;
-                    return kPresent;
-                }
-
-                public override void Put(ulong index, JsValue value, bool throwOnError) => _instance.Put(TypeConverter.ToString(index), value, throwOnError);
-
-                public override void DeleteAt(ulong index) => _instance.Delete(TypeConverter.ToString(index), true);
-            }
-
-            private sealed class ArrayInstanceOperations : ArrayOperations
-            {
-                private readonly ArrayInstance _array;
-
-                public ArrayInstanceOperations(ArrayInstance array)
-                {
-                    _array = array;
-                }
-
-                public override ObjectInstance Target => _array;
-
-                public override ulong GetSmallestIndex(ulong length) => _array.GetSmallestIndex();
-
-                public override uint GetLength()
-                {
-                    return (uint) ((JsNumber) _array._length._value)._value;
-                }
-
-                public override ulong GetLongLength()
-                {
-                    return (ulong) ((JsNumber) _array._length._value)._value;
-                }
-
-                public override void SetLength(ulong length) => _array.Put("length", length, true);
-
-                public override void EnsureCapacity(ulong capacity)
-                {
-                    _array.EnsureCapacity((uint) capacity);
-                }
-
-                public override bool TryGetValue(ulong index, out JsValue value)
-                {
-                    // array max size is uint
-                    return _array.TryGetValue((uint) index, out value);
-                }
-
-                public override JsValue Get(ulong index) => _array.Get((uint) index);
-
-                public override JsValue[] GetAll()
-                {
-                    var n = _array.Length;
-
-                    if (_array._dense == null || _array._dense.Length < n)
-                    {
-                        return base.GetAll();
-                    }
-
-                    // optimized
-                    var jsValues = new JsValue[n];
-                    for (uint i = 0; i < (uint) jsValues.Length; i++)
-                    {
-                        var prop = _array._dense[i] ?? PropertyDescriptor.Undefined;
-                        if (prop == PropertyDescriptor.Undefined)
-                        {
-                            prop = _array.Prototype?.GetProperty(i) ?? PropertyDescriptor.Undefined;
-                        }
-
-                        jsValues[i] = _array.UnwrapJsValue(prop);
-                    }
-
-                    return jsValues;
-                }
-
-                public override void DeleteAt(ulong index) => _array.DeleteAt((uint) index);
-
-                public override void Put(ulong index, JsValue value, bool throwOnError) => _array.SetIndexValue((uint) index, value, throwOnError);
-            }
-        }
     }
 }

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

@@ -17,16 +17,15 @@ namespace Jint.Native.Boolean
         public static BooleanConstructor CreateBooleanConstructor(Engine engine)
         {
             var obj = new BooleanConstructor(engine);
-            obj.Extensible = true;
 
             // The value of the [[Prototype]] internal property of the Boolean constructor is the Function prototype object
-            obj.Prototype = engine.Function.PrototypeObject;
+            obj._prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = BooleanPrototype.CreatePrototypeObject(engine, obj);
 
             obj._length = PropertyDescriptor.AllForbiddenDescriptor.NumberOne;
 
             // The initial value of Boolean.prototype is the Boolean prototype object
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -44,9 +43,7 @@ namespace Jint.Native.Boolean
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.7.2.1
         /// </summary>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             return Construct(TypeConverter.ToBoolean(arguments.At(0)));
         }
@@ -62,9 +59,8 @@ namespace Jint.Native.Boolean
         {
             var instance = new BooleanInstance(Engine)
             {
-                Prototype = PrototypeObject,
+                _prototype = PrototypeObject,
                 PrimitiveValue = value,
-                Extensible = true
             };
 
             return instance;

+ 1 - 2
Jint/Native/Boolean/BooleanPrototype.cs

@@ -20,9 +20,8 @@ namespace Jint.Native.Boolean
         {
             var obj = new BooleanPrototype(engine)
             {
-                Prototype = engine.Object.PrototypeObject,
+                _prototype = engine.Object.PrototypeObject,
                 PrimitiveValue = false,
-                Extensible = true,
                 _booleanConstructor = booleanConstructor
             };
 

+ 13 - 18
Jint/Native/Date/DateConstructor.cs

@@ -62,8 +62,7 @@ namespace Jint.Native.Date
         {
             var obj = new DateConstructor(engine)
             {
-                Extensible = true,
-                Prototype = engine.Function.PrototypeObject
+                _prototype = engine.Function.PrototypeObject
             };
 
             // The value of the [[Prototype]] internal property of the Date constructor is the Function prototype object
@@ -72,7 +71,7 @@ namespace Jint.Native.Date
             obj._length = new PropertyDescriptor(7, PropertyFlag.AllForbidden);
 
             // The initial value of Date.prototype is the Date prototype object
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -121,15 +120,13 @@ namespace Jint.Native.Date
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
-            return PrototypeObject.ToString(Construct(Arguments.Empty), Arguments.Empty);
+            return PrototypeObject.ToString(Construct(Arguments.Empty, thisObject), Arguments.Empty);
         }
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.9.3
         /// </summary>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             if (arguments.Length == 0)
             {
@@ -207,11 +204,10 @@ namespace Jint.Native.Date
         public DateInstance Construct(DateTime value)
         {
             var instance = new DateInstance(Engine)
-                {
-                    Prototype = PrototypeObject,
-                    PrimitiveValue = FromDateTime(value),
-                    Extensible = true
-                };
+            {
+                _prototype = PrototypeObject,
+                PrimitiveValue = FromDateTime(value)
+            };
 
             return instance;
         }
@@ -219,11 +215,10 @@ namespace Jint.Native.Date
         public DateInstance Construct(double time)
         {
             var instance = new DateInstance(Engine)
-                {
-                    Prototype = PrototypeObject,
-                    PrimitiveValue = TimeClip(time),
-                    Extensible = true
-                };
+            {
+                _prototype = PrototypeObject,
+                PrimitiveValue = TimeClip(time)
+            };
 
             return instance;
         }
@@ -258,7 +253,7 @@ namespace Jint.Native.Date
                 result = PrototypeObject.Utc(result);
             }
 
-            return System.Math.Floor(result);
+            return System.Math.Round(result);
         }
     }
 }

+ 3 - 4
Jint/Native/Date/DatePrototype.cs

@@ -24,8 +24,7 @@ namespace Jint.Native.Date
         {
             var obj = new DatePrototype(engine)
             {
-                Prototype = engine.Object.PrototypeObject,
-                Extensible = true,
+                _prototype = engine.Object.PrototypeObject,
                 PrimitiveValue = double.NaN,
                 _dateConstructor = dateConstructor
             };
@@ -588,7 +587,7 @@ namespace Jint.Native.Date
                 return Null;
             }
 
-            var toIso = o.Get("toISOString");
+            var toIso = o.Get("toISOString", o);
             if (!toIso.Is<ICallable>())
             {
                 ExceptionHelper.ThrowTypeError(Engine);
@@ -913,7 +912,7 @@ namespace Jint.Native.Date
                 year = isLeapYear ? 2000 : 1999;
             }
 
-            var dateTime = new DateTime((int)year, 1, 1).AddMilliseconds(timeInYear);
+            var dateTime = new DateTime(year, 1, 1).AddMilliseconds(timeInYear);
 
             return Engine.Options._LocalTimeZone.IsDaylightSavingTime(dateTime) ? MsPerHour : 0;
         }

+ 12 - 7
Jint/Native/Error/ErrorConstructor.cs

@@ -18,9 +18,8 @@ namespace Jint.Native.Error
         {
             var obj = new ErrorConstructor(engine)
             {
-                Extensible = true,
                 _name = name,
-                Prototype = engine.Function.PrototypeObject
+                _prototype = engine.Function.PrototypeObject
             };
 
             // The value of the [[Prototype]] internal property of the Error constructor is the Function prototype object (15.11.3)
@@ -29,26 +28,32 @@ namespace Jint.Native.Error
             obj._length = PropertyDescriptor.AllForbiddenDescriptor.NumberOne;
 
             // The initial value of Error.prototype is the Error prototype object
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
-            return Construct(arguments);
+            return Construct(arguments, thisObject);
         }
 
         public ObjectInstance Construct(JsValue[] arguments)
+        {
+            return Construct(arguments, this);
+        }
+
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             var instance = new ErrorInstance(Engine, _name);
-            instance.Prototype = PrototypeObject;
-            instance.Extensible = true;
+            instance._prototype = PrototypeObject;
 
             var jsValue = arguments.At(0);
             if (!jsValue.IsUndefined())
             {
-                instance.Put("message", TypeConverter.ToString(jsValue), false);
+                var msg = TypeConverter.ToString(jsValue);
+                var msgDesc = new PropertyDescriptor(msg, true, false, true);
+                instance.DefinePropertyOrThrow("message", msgDesc);
             }
 
             return instance;

+ 4 - 5
Jint/Native/Error/ErrorPrototype.cs

@@ -21,17 +21,16 @@ namespace Jint.Native.Error
         {
             var obj = new ErrorPrototype(engine, name)
             {
-                Extensible = true,
                 _errorConstructor = errorConstructor,
             };
 
             if (name != "Error")
             {
-                obj.Prototype = engine.Error.PrototypeObject;
+                obj._prototype = engine.Error.PrototypeObject;
             }
             else
             {
-                obj.Prototype = engine.Object.PrototypeObject;
+                obj._prototype = engine.Object.PrototypeObject;
             }
 
             return obj;
@@ -52,9 +51,9 @@ namespace Jint.Native.Error
                 ExceptionHelper.ThrowTypeError(Engine);
             }
 
-            var name = TypeConverter.ToString(o.Get("name"));
+            var name = TypeConverter.ToString(o.Get("name", this));
 
-            var msgProp = o.Get("message");
+            var msgProp = o.Get("message", this);
             string msg;
             if (msgProp.IsUndefined())
             {

+ 6 - 6
Jint/Native/Function/ArrowFunctionInstance.cs

@@ -33,8 +33,8 @@ namespace Jint.Native.Function
         {
             _function = function;
 
-            Extensible = false;
-            Prototype = Engine.Function.PrototypeObject;
+            PreventExtensions();
+            _prototype = Engine.Function.PrototypeObject;
 
             _length = new PropertyDescriptor(JsNumber.Create(function._length), PropertyFlag.Configurable);
             _thisBinding = _engine.ExecutionContext.ThisBinding;
@@ -98,16 +98,16 @@ namespace Jint.Native.Function
             }
         }
 
-        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
+        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
         {
             AssertValidPropertyName(propertyName);
-            base.Put(propertyName, value, throwOnError);
+            return base.Set(propertyName, value, receiver);
         }
 
-        public override JsValue Get(in Key propertyName)
+        public override JsValue Get(in Key propertyName, JsValue receiver)
         {
             AssertValidPropertyName(propertyName);
-            return base.Get(propertyName);
+            return base.Get(propertyName, receiver);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]

+ 10 - 8
Jint/Native/Function/BindFunctionInstance.cs

@@ -19,22 +19,22 @@ namespace Jint.Native.Function
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
-            var f = TargetFunction.TryCast<FunctionInstance>(x =>
+            if (!(TargetFunction is FunctionInstance f))
             {
-                ExceptionHelper.ThrowTypeError(Engine);
-            });
+                return ExceptionHelper.ThrowTypeError<ObjectInstance>(Engine);
+            }
 
             return f.Call(BoundThis, CreateArguments(arguments));
         }
 
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
-            var target = TargetFunction.TryCast<IConstructor>(x =>
+            if (!(TargetFunction is IConstructor target))
             {
-                ExceptionHelper.ThrowTypeError(Engine);
-            });
+                return ExceptionHelper.ThrowTypeError<ObjectInstance>(Engine);
+            }
 
-            return target.Construct(CreateArguments(arguments));
+            return target.Construct(CreateArguments(arguments), newTarget);
         }
 
         public override bool HasInstance(JsValue v)
@@ -51,5 +51,7 @@ namespace Jint.Native.Function
         {
             return Enumerable.Union(BoundArgs, arguments).ToArray();
         }
+
+        internal override bool IsConstructor => TargetFunction is IConstructor;
     }
 }

+ 1 - 1
Jint/Native/Function/EvalFunctionInstance.cs

@@ -14,7 +14,7 @@ namespace Jint.Native.Function
         public EvalFunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict) 
             : base(engine, _functionName, parameters, scope, strict)
         {
-            Prototype = Engine.Function.PrototypeObject;
+            _prototype = Engine.Function.PrototypeObject;
             _length = PropertyDescriptor.AllForbiddenDescriptor.NumberOne;
         }
 

+ 8 - 15
Jint/Native/Function/FunctionConstructor.cs

@@ -24,16 +24,15 @@ namespace Jint.Native.Function
         {
             var obj = new FunctionConstructor(engine)
             {
-                Extensible = true,
                 PrototypeObject = FunctionPrototype.CreatePrototypeObject(engine)
             };
 
             // The initial value of Function.prototype is the standard built-in Function prototype object
 
             // The value of the [[Prototype]] internal property of the Function constructor is the standard built-in Function prototype object
-            obj.Prototype = obj.PrototypeObject;
+            obj._prototype = obj.PrototypeObject;
 
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
             obj._length =  PropertyDescriptor.AllForbiddenDescriptor.NumberOne;
 
             return obj;
@@ -43,10 +42,10 @@ namespace Jint.Native.Function
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
-            return Construct(arguments);
+            return Construct(arguments, thisObject);
         }
 
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             var argCount = arguments.Length;
             string p = "";
@@ -86,10 +85,7 @@ namespace Jint.Native.Function
                 Engine,
                 function,
                 LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment),
-                function.Strict)
-            {
-                Extensible = true
-            };
+                function.Strict);
 
             return functionObject;
 
@@ -106,10 +102,7 @@ namespace Jint.Native.Function
                 Engine,
                 functionDeclaration,
                 LexicalEnvironment.NewDeclarativeEnvironment(Engine, Engine.ExecutionContext.LexicalEnvironment),
-                functionDeclaration.Strict)
-            {
-                Extensible = true
-            };
+                functionDeclaration.Strict);
 
             return functionObject;
         }
@@ -155,13 +148,13 @@ namespace Jint.Native.Function
                 ExceptionHelper.ThrowTypeError(Engine);
             }
 
-            var len = argArrayObj.Get("length");
+            var len = argArrayObj.Get("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(indexName);
+                var nextArg = argArrayObj.Get(indexName, argArrayObj);
                 argList[index] = nextArg;
             }
             return func.Call(thisArg, argList);

+ 36 - 14
Jint/Native/Function/FunctionInstance.cs

@@ -8,7 +8,7 @@ namespace Jint.Native.Function
 {
     public abstract class FunctionInstance : ObjectInstance, ICallable
     {
-        protected internal PropertyDescriptor _prototype;
+        protected internal PropertyDescriptor _prototypeDescriptor;
 
         protected PropertyDescriptor _length;
 
@@ -76,7 +76,7 @@ namespace Jint.Native.Function
                 return false;
             }
 
-            var po = Get(KnownKeys.Prototype);
+            var po = Get(KnownKeys.Prototype, this);
             if (!po.IsObject())
             {
                 ExceptionHelper.ThrowTypeError(_engine, $"Function has non-object prototype '{TypeConverter.ToString(po)}' in instanceof check");
@@ -109,10 +109,11 @@ namespace Jint.Native.Function
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.4
         /// </summary>
         /// <param name="propertyName"></param>
+        /// <param name="receiver"></param>
         /// <returns></returns>
-        public override JsValue Get(in Key propertyName)
+        public override JsValue Get(in Key propertyName, JsValue receiver)
         {
-            var v = base.Get(propertyName);
+            var v = base.Get(propertyName, receiver);
 
             if (propertyName == KnownKeys.Caller
                 && ((v.As<FunctionInstance>()?._strict).GetValueOrDefault()))
@@ -123,19 +124,19 @@ namespace Jint.Native.Function
             return v;
         }
 
-        public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
         {
-            if (_prototype != null)
+            if (_prototypeDescriptor != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Prototype, _prototype);
+                yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Prototype, _prototypeDescriptor);
             }
             if (_length != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Length, _length);
+                yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Length, _length);
             }
             if (!(_name is null))
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Name, GetOwnProperty(KnownKeys.Name));
+                yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Name, GetOwnProperty(KnownKeys.Name));
             }
 
             foreach (var entry in base.GetOwnProperties())
@@ -144,11 +145,32 @@ namespace Jint.Native.Function
             }
         }
 
+        internal override List<JsValue> GetOwnPropertyKeys(Types types)
+        {
+            var keys = new List<JsValue>();
+            if (_prototypeDescriptor != null)
+            {
+                keys.Add(KnownKeys.Prototype);
+            }
+            if (_length != null)
+            {
+                keys.Add(KnownKeys.Length);
+            }
+            if (!(_name is null))
+            {
+                keys.Add(KnownKeys.Name);
+            }
+
+            keys.AddRange(base.GetOwnPropertyKeys(types));
+
+            return keys;
+        }
+
         public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
             if (propertyName == KnownKeys.Prototype)
             {
-                return _prototype ?? PropertyDescriptor.Undefined;
+                return _prototypeDescriptor ?? PropertyDescriptor.Undefined;
             }
             if (propertyName == KnownKeys.Length)
             {
@@ -168,7 +190,7 @@ namespace Jint.Native.Function
         {
             if (propertyName == KnownKeys.Prototype)
             {
-                _prototype = desc;
+                _prototypeDescriptor = desc;
             }
             else if (propertyName == KnownKeys.Length)
             {
@@ -189,7 +211,7 @@ namespace Jint.Native.Function
         {
             if (propertyName == KnownKeys.Prototype)
             {
-                return _prototype != null;
+                return _prototypeDescriptor != null;
             }
             if (propertyName == KnownKeys.Length)
             {
@@ -207,7 +229,7 @@ namespace Jint.Native.Function
         {
             if (propertyName == KnownKeys.Prototype)
             {
-                _prototype = null;
+                _prototypeDescriptor = null;
             }
             if (propertyName == KnownKeys.Length)
             {
@@ -226,7 +248,7 @@ namespace Jint.Native.Function
         {
             if (_name is null)
             {
-                _name = key.Name;
+                _name = key.IsSymbol && key.Name.Length > 0 ? "[" + key.Name + "]" : key.Name;
             }
             else if (throwIfExists)
             {

+ 20 - 11
Jint/Native/Function/FunctionPrototype.cs

@@ -24,9 +24,8 @@ namespace Jint.Native.Function
         {
             var obj = new FunctionPrototype(engine)
             {
-                Extensible = true,
                 // The value of the [[Prototype]] internal property of the Function prototype object is the standard built-in Object prototype object
-                Prototype = engine.Object.PrototypeObject,
+                _prototype = engine.Object.PrototypeObject,
                 _length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero
             };
 
@@ -53,19 +52,20 @@ namespace Jint.Native.Function
                 ExceptionHelper.ThrowTypeError(Engine);
             });
 
+            var func = thisObj as IConstructor;
+            
             var thisArg = arguments.At(0);
             var f = new BindFunctionInstance(Engine)
             {
-                Extensible = true,
                 TargetFunction = thisObj,
                 BoundThis = thisArg,
                 BoundArgs = arguments.Skip(1),
-                Prototype = Engine.Function.PrototypeObject
+                _prototype = Engine.Function.PrototypeObject
             };
 
             if (target is FunctionInstance functionInstance)
             {
-                var l = TypeConverter.ToNumber(functionInstance.Get("length")) - (arguments.Length - 1);
+                var l = TypeConverter.ToNumber(functionInstance.Get("length", functionInstance)) - (arguments.Length - 1);
                 f.SetOwnProperty(KnownKeys.Length, new PropertyDescriptor(System.Math.Max(l, 0), PropertyFlag.AllForbidden));
             }
             else
@@ -73,8 +73,8 @@ namespace Jint.Native.Function
                 f.SetOwnProperty(KnownKeys.Length, PropertyDescriptor.AllForbiddenDescriptor.NumberZero);
             }
 
-            f.DefineOwnProperty(KnownKeys.Caller, _engine._getSetThrower, false);
-            f.DefineOwnProperty(KnownKeys.Arguments, _engine._getSetThrower, false);
+            f.DefineOwnProperty(KnownKeys.Caller, _engine._getSetThrower);
+            f.DefineOwnProperty(KnownKeys.Arguments, _engine._getSetThrower);
 
             return f;
         }
@@ -89,7 +89,7 @@ namespace Jint.Native.Function
             return "function() {{ ... }}";
         }
 
-        private JsValue Apply(JsValue thisObject, JsValue[] arguments)
+        internal JsValue Apply(JsValue thisObject, JsValue[] arguments)
         {
             var func = thisObject as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(Engine);
             var thisArg = arguments.At(0);
@@ -100,15 +100,24 @@ namespace Jint.Native.Function
                 return func.Call(thisArg, Arguments.Empty);
             }
 
-            var argArrayObj = argArray as ObjectInstance ?? ExceptionHelper.ThrowTypeError<ObjectInstance>(Engine);
-            var operations = ArrayPrototype.ArrayOperations.For(argArrayObj);
-            var argList = operations.GetAll();
+            var argList = CreateListFromArrayLike(argArray);
 
             var result = func.Call(thisArg, argList);
 
             return result;
         }
 
+        internal JsValue[] CreateListFromArrayLike(JsValue argArray, Types? elementTypes = null)
+        {
+            var argArrayObj = argArray as ObjectInstance ?? ExceptionHelper.ThrowTypeError<ObjectInstance>(_engine);
+            var operations = ArrayOperations.For(argArrayObj);
+            var allowedTypes = elementTypes ??
+                               Types.Undefined | Types.Null | Types.Boolean | Types.String | Types.Symbol | Types.Number | Types.Object;
+            
+            var argList = operations.GetAll(allowedTypes);
+            return argList;
+        }
+
         private JsValue CallImpl(JsValue thisObject, JsValue[] arguments)
         {
             var func = thisObject as ICallable ?? ExceptionHelper.ThrowTypeError<ICallable>(Engine);

+ 33 - 20
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -1,4 +1,5 @@
 using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Native.Object;
 using Jint.Runtime;
@@ -35,23 +36,21 @@ namespace Jint.Native.Function
         {
             _function = function;
 
-            Extensible = true;
-            Prototype = _engine.Function.PrototypeObject;
+            _prototype = _engine.Function.PrototypeObject;
 
             _length = new PropertyDescriptor(JsNumber.Create(function._length), PropertyFlag.Configurable);
 
             var proto = new ObjectInstanceWithConstructor(engine, this)
             {
-                Extensible = true,
-                Prototype = _engine.Object.PrototypeObject
+                _prototype = _engine.Object.PrototypeObject
             };
 
-            _prototype = new PropertyDescriptor(proto, PropertyFlag.OnlyWritable);
+            _prototypeDescriptor = new PropertyDescriptor(proto, PropertyFlag.OnlyWritable);
 
             if (strict)
             {
-                DefineOwnProperty(KnownKeys.Caller, engine._getSetThrower, false);
-                DefineOwnProperty(KnownKeys.Arguments, engine._getSetThrower, false);
+                DefineOwnProperty(KnownKeys.Caller, engine._getSetThrower);
+                DefineOwnProperty(KnownKeys.Arguments, engine._getSetThrower);
             }
         }
 
@@ -135,27 +134,41 @@ namespace Jint.Native.Function
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2.2
         /// </summary>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
-            var proto = Get(KnownKeys.Prototype).TryCast<ObjectInstance>();
-
-            var obj = new ObjectInstance(_engine)
-            {
-                Extensible = true,
-                Prototype = proto ?? _engine.Object.PrototypeObject
-            };
+            var thisArgument = OrdinaryCreateFromConstructor(TypeConverter.ToObject(_engine, newTarget), _engine.Object.PrototypeObject);
 
-            var result = Call(obj, arguments).TryCast<ObjectInstance>();
+            var result = Call(thisArgument, arguments).TryCast<ObjectInstance>();
             if (!ReferenceEquals(result, null))
             {
                 return result;
             }
 
+            return thisArgument;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private ObjectInstance OrdinaryCreateFromConstructor(ObjectInstance constructor, ObjectInstance intrinsicDefaultProto)
+        {
+            var proto = GetPrototypeFromConstructor(constructor, intrinsicDefaultProto);
+
+            var obj = new ObjectInstance(_engine)
+            {
+                _prototype = proto
+            };
             return obj;
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static ObjectInstance GetPrototypeFromConstructor(ObjectInstance constructor, ObjectInstance intrinsicDefaultProto)
+        {
+            var proto = constructor.Get(KnownKeys.Prototype, constructor) as ObjectInstance;
+            // If Type(proto) is not Object, then
+            //    Let realm be ? GetFunctionRealm(constructor).
+            //    Set proto to realm's intrinsic object named intrinsicDefaultProto.
+            return proto ?? intrinsicDefaultProto;
+        }
+
         private class ObjectInstanceWithConstructor : ObjectInstance
         {
             private PropertyDescriptor _constructor;
@@ -165,11 +178,11 @@ namespace Jint.Native.Function
                 _constructor = new PropertyDescriptor(thisObj, PropertyFlag.NonEnumerable);
             }
 
-            public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
+            public override IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
             {
                 if (_constructor != null)
                 {
-                    yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Constructor, _constructor);
+                    yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Constructor, _constructor);
                 }
 
                 foreach (var entry in base.GetOwnProperties())

+ 1 - 1
Jint/Native/Function/ThrowTypeError.cs

@@ -11,7 +11,7 @@ namespace Jint.Native.Function
             : base(engine, _functionName, System.ArrayExt.Empty<string>(), engine.GlobalEnvironment, false)
         {
             _length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero;
-            Extensible = false;
+            PreventExtensions();
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)

+ 4 - 3
Jint/Native/Global/GlobalObject.cs

@@ -25,8 +25,7 @@ namespace Jint.Native.Global
         {
             var global = new GlobalObject(engine)
             {
-                Prototype = null,
-                Extensible = true,
+                _prototype = null,
                 _properties = new StringDictionarySlim<PropertyDescriptor>(35)
             };
 
@@ -51,8 +50,10 @@ namespace Jint.Native.Global
             _properties["JSON"] = new PropertyDescriptor(Engine.Json, true, false, true);
             _properties["Error"] = new LazyPropertyDescriptor(() => Engine.Error, true, false, true);
             _properties["EvalError"] = new LazyPropertyDescriptor(() => Engine.EvalError, true, false, true);
+            _properties["Proxy"] = new LazyPropertyDescriptor(() => Engine.Proxy, true, false, true);
             _properties["RangeError"] = new LazyPropertyDescriptor(() => Engine.RangeError, true, false, true);
             _properties["ReferenceError"] = new LazyPropertyDescriptor(() => Engine.ReferenceError, true, false, true);
+            _properties["Reflect"] = new LazyPropertyDescriptor(() => Engine.Reflect, true, false, true);
             _properties["SyntaxError"] = new LazyPropertyDescriptor(() => Engine.SyntaxError, true, false, true);
             _properties["TypeError"] = new LazyPropertyDescriptor(() => Engine.TypeError, true, false, true);
             _properties["URIError"] = new LazyPropertyDescriptor(() => Engine.UriError, true, false, true);
@@ -446,7 +447,7 @@ namespace Jint.Native.Global
                         v = (c - 0xD800) * 0x400 + (kChar - 0xDC00) + 0x10000;
                     }
 
-                    byte[] octets = System.ArrayExt.Empty<byte>();
+                    byte[] octets = ArrayExt.Empty<byte>();
 
                     if (v >= 0 && v <= 0x007F)
                     {

+ 1 - 2
Jint/Native/IConstructor.cs

@@ -4,7 +4,6 @@ namespace Jint.Native
 {
     public interface IConstructor
     {
-        JsValue Call(JsValue thisObject, JsValue[] arguments);
-        ObjectInstance Construct(JsValue[] arguments);
+        ObjectInstance Construct(JsValue[] arguments, JsValue newTarget);
     }
 }

+ 12 - 13
Jint/Native/Iterator/IteratorConstructor.cs

@@ -22,16 +22,15 @@ namespace Jint.Native.Iterator
         public static IteratorConstructor CreateIteratorConstructor(Engine engine)
         {
             var obj = new IteratorConstructor(engine);
-            obj.Extensible = true;
 
             // The value of the [[Prototype]] internal property of the Map constructor is the Function prototype object
-            obj.Prototype = engine.Function.PrototypeObject;
+            obj._prototype = engine.Function.PrototypeObject;
             obj.PrototypeObject = IteratorPrototype.CreatePrototypeObject(engine, obj);
 
             obj._length = new PropertyDescriptor(0, PropertyFlag.Configurable);
 
             // The initial value of Map.prototype is the Map prototype object
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -42,7 +41,7 @@ namespace Jint.Native.Iterator
             return Construct(arguments);
         }
 
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             return Construct(Enumerable.Empty<JsValue>());
         }
@@ -51,7 +50,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance(Engine, enumerable)
             {
-                Prototype = PrototypeObject, Extensible = true
+                _prototype = PrototypeObject
             };
 
             return instance;
@@ -61,7 +60,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.ListIterator(Engine, enumerable)
             {
-                Prototype = PrototypeObject, Extensible = true
+                _prototype = PrototypeObject
             };
 
             return instance;
@@ -71,7 +70,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.ArrayLikeIterator(Engine, array)
             {
-                Prototype = PrototypeObject, Extensible = true
+                _prototype = PrototypeObject
             };
 
             return instance;
@@ -81,7 +80,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.MapIterator(Engine, map)
             {
-                Prototype = PrototypeObject, Extensible = true
+                _prototype = PrototypeObject
             };
 
             return instance;
@@ -91,7 +90,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.SetIterator(Engine, set)
             {
-                Prototype = PrototypeObject, Extensible = true
+                _prototype = PrototypeObject
             };
 
             return instance;
@@ -101,7 +100,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.SetEntryIterator(Engine, set)
             {
-                Prototype = PrototypeObject, Extensible = true
+                _prototype = PrototypeObject
             };
 
             return instance;
@@ -111,7 +110,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.ArrayLikeKeyIterator(Engine, array)
             {
-                Prototype = PrototypeObject, Extensible = true
+                _prototype = PrototypeObject
             };
 
             return instance;
@@ -121,7 +120,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.ArrayLikeValueIterator(Engine, array)
             {
-                Prototype = PrototypeObject, Extensible = true
+                _prototype = PrototypeObject
             };
 
             return instance;
@@ -131,7 +130,7 @@ namespace Jint.Native.Iterator
         {
             var instance = new IteratorInstance.StringIterator(Engine, str)
             {
-                Prototype = PrototypeObject, Extensible = true
+                _prototype = PrototypeObject
             };
 
             return instance;

+ 7 - 7
Jint/Native/Iterator/IteratorInstance.cs

@@ -113,7 +113,7 @@ namespace Jint.Native.Iterator
 
         public class ArrayLikeIterator : IteratorInstance
         {
-            private readonly ArrayPrototype.ArrayOperations _array;
+            private readonly ArrayOperations _array;
             private uint? _end;
             private uint _position;
 
@@ -125,7 +125,7 @@ namespace Jint.Native.Iterator
                     return;
                 }
 
-                _array = ArrayPrototype.ArrayOperations.For(objectInstance);
+                _array = ArrayOperations.For(objectInstance);
                 _position = 0;
             }
 
@@ -222,13 +222,13 @@ namespace Jint.Native.Iterator
 
         public class ArrayLikeKeyIterator : IteratorInstance
         {
-            private readonly ArrayPrototype.ArrayOperations _operations;
+            private readonly ArrayOperations _operations;
             private uint _position;
             private bool _closed;
 
             public ArrayLikeKeyIterator(Engine engine, ObjectInstance objectInstance) : base(engine)
             {
-                _operations = ArrayPrototype.ArrayOperations.For(objectInstance);
+                _operations = ArrayOperations.For(objectInstance);
                 _position = 0;
             }
 
@@ -247,13 +247,13 @@ namespace Jint.Native.Iterator
 
         public class ArrayLikeValueIterator : IteratorInstance
         {
-            private readonly ArrayPrototype.ArrayOperations _operations;
+            private readonly ArrayOperations _operations;
             private uint _position;
             private bool _closed;
 
             public ArrayLikeValueIterator(Engine engine, ObjectInstance objectInstance) : base(engine)
             {
-                _operations = ArrayPrototype.ArrayOperations.For(objectInstance);
+                _operations = ArrayOperations.For(objectInstance);
                 _position = 0;
             }
 
@@ -279,7 +279,7 @@ namespace Jint.Native.Iterator
             public ObjectWrapper(ObjectInstance target)
             {
                 _target = target;
-                _callable = (ICallable) target.Get("next");
+                _callable = (ICallable) target.Get("next", target);
             }
 
             public ObjectInstance Next()

+ 1 - 1
Jint/Native/Iterator/IteratorProtocol.cs

@@ -68,7 +68,7 @@ namespace Jint.Native.Iterator
 
         protected abstract void ProcessItem(JsValue[] args, JsValue currentValue);
 
-        protected JsValue ExtractValueFromIteratorInstance(JsValue jsValue)
+        protected static JsValue ExtractValueFromIteratorInstance(JsValue jsValue)
         {
             if (jsValue is ArrayInstance ai)
             {

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

@@ -14,8 +14,7 @@ namespace Jint.Native.Iterator
         {
             var obj = new IteratorPrototype(engine)
             {
-                Extensible = true,
-                Prototype = engine.Object.PrototypeObject
+                _prototype = engine.Object.PrototypeObject
             };
 
             return obj;

+ 6 - 0
Jint/Native/JsString.cs

@@ -19,6 +19,7 @@ namespace Jint.Native
         internal static readonly JsString BooleanString = new JsString("boolean");
         internal static readonly JsString StringString = new JsString("string");
         internal static readonly JsString NumberString = new JsString("number");
+        internal static readonly JsString LengthString = new JsString("length");
 
         internal string _value;
 
@@ -98,6 +99,11 @@ namespace Jint.Native
             return new JsString(value);
         }
 
+        internal override Key ToPropertyKey()
+        {
+            return new Key(_value);
+        }
+
         public override string ToString()
         {
             return _value;

+ 11 - 5
Jint/Native/JsSymbol.cs

@@ -10,15 +10,16 @@ namespace Jint.Native
     public sealed class JsSymbol : JsValue, IEquatable<JsSymbol>
     {
         internal readonly string _value;
+        private readonly Key _key;
 
-        public JsSymbol(string value) : base(Types.Symbol)
+        internal JsSymbol(string value, int identity) : base(Types.Symbol)
         {
             _value = value;
+            _key = new Key(_value, identity);
         }
 
-        internal JsSymbol(JsValue value) : base(Types.Symbol)
+        internal JsSymbol(JsValue value, int identity) : this(value.IsUndefined() ? "" : value.ToString(), identity)
         {
-            _value = value.IsUndefined() ? "" : value.ToString();
         }
 
         public override object ToObject()
@@ -26,6 +27,11 @@ namespace Jint.Native
             return _value;
         }
 
+        internal override Key ToPropertyKey()
+        {
+            return _key;
+        }
+
         public override string ToString()
         {
             return "Symbol(" + _value + ")";
@@ -33,12 +39,12 @@ namespace Jint.Native
 
         public override bool Equals(JsValue obj)
         {
-            return ReferenceEquals(this, obj);
+            return Equals(obj as JsSymbol);
         }
 
         public bool Equals(JsSymbol other)
         {
-            return ReferenceEquals(this, other);
+            return other != null && other._key == _key;
         }
 
         public override int GetHashCode()

+ 10 - 5
Jint/Native/JsValue.cs

@@ -55,8 +55,7 @@ namespace Jint.Native
         }
 
         [Pure]
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool IsArray()
+        public virtual bool IsArray()
         {
             return this is ArrayInstance;
         }
@@ -182,7 +181,7 @@ namespace Jint.Native
         {
             var objectInstance = TypeConverter.ToObject(engine, this);
 
-            if (!objectInstance.TryGetValue(GlobalSymbolRegistry.Iterator._value, out var value)
+            if (!objectInstance.TryGetValue(GlobalSymbolRegistry.Iterator, out var value)
                 || !(value is ICallable callable))
             {
                 iterator = null;
@@ -282,6 +281,8 @@ namespace Jint.Native
             get => _type == InternalTypes.Integer ? Types.Number : (Types) _type;
         }
 
+        internal virtual bool IsConstructor => this is IConstructor;
+
         /// <summary>
         /// Creates a valid <see cref="JsValue"/> instance from any <see cref="Object"/> instance
         /// </summary>
@@ -369,8 +370,7 @@ namespace Jint.Native
             var arrayLength = (uint) array.Length;
 
             var jsArray = new ArrayInstance(e, arrayLength);
-            jsArray.Prototype = e.Array.PrototypeObject;
-            jsArray.Extensible = true;
+            jsArray._prototype = e.Array.PrototypeObject;
 
             for (uint i = 0; i < arrayLength; ++i)
             {
@@ -429,6 +429,11 @@ namespace Jint.Native
             return false;
         }
 
+        internal virtual Key ToPropertyKey()
+        {
+            return new Key(ToString());
+        }
+
         public override string ToString()
         {
             return "None";

+ 7 - 14
Jint/Native/Json/JsonInstance.cs

@@ -13,14 +13,13 @@ namespace Jint.Native.Json
         private JsonInstance(Engine engine)
             : base(engine, objectClass: "JSON")
         {
-            Extensible = true;
         }
 
         public static JsonInstance CreateJsonObject(Engine engine)
         {
             var json = new JsonInstance(engine)
             {
-                Prototype = engine.Object.PrototypeObject
+                _prototype = engine.Object.PrototypeObject
             };
             return json;
         }
@@ -36,7 +35,7 @@ namespace Jint.Native.Json
 
         private JsValue AbstractWalkOperation(ObjectInstance thisObject, string prop)
         {
-            JsValue value = thisObject.Get(prop);
+            JsValue value = thisObject.Get(prop, thisObject);
             if (value.IsObject())
             {
                 var valueAsObject = value.AsObject();
@@ -50,7 +49,7 @@ namespace Jint.Native.Json
                         var newValue = AbstractWalkOperation(valAsArray, TypeConverter.ToString(i));
                         if (newValue.IsUndefined())
                         {
-                            valAsArray.Delete(TypeConverter.ToString(i), false);
+                            valAsArray.Delete(TypeConverter.ToString(i));
                         }
                         else
                         {
@@ -61,9 +60,7 @@ namespace Jint.Native.Json
                                 (
                                     value: newValue,
                                     PropertyFlag.ConfigurableEnumerableWritable
-                                ),
-                                false
-                            );
+                                ));
                         }
                         i = i + 1;
                     }
@@ -76,7 +73,7 @@ namespace Jint.Native.Json
                         var newElement = AbstractWalkOperation(valueAsObject, p.Key);
                         if (newElement.IsUndefined())
                         {
-                            valueAsObject.Delete(p.Key, false);
+                            valueAsObject.Delete(p.Key);
                         }
                         else
                         {
@@ -86,9 +83,7 @@ namespace Jint.Native.Json
                                 (
                                     value: newElement,
                                     PropertyFlag.ConfigurableEnumerableWritable
-                                ),
-                                false
-                            );
+                                ));
                         }
                     }
                 }
@@ -109,9 +104,7 @@ namespace Jint.Native.Json
                     new PropertyDescriptor(
                         value: res,
                         PropertyFlag.ConfigurableEnumerableWritable
-                    ),
-                    false
-                );
+                    ));
                 return AbstractWalkOperation(revRes, "");
             }
             return res;

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

@@ -672,7 +672,7 @@ namespace Jint.Native.Json
 
         private ObjectInstance ParseJsonArray()
         {
-            var elements = new System.Collections.Generic.List<JsValue>();
+            var elements = new List<JsValue>();
 
             Expect("[");
 
@@ -826,7 +826,7 @@ namespace Jint.Native.Json
             {
                 if (options.Tokens)
                 {
-                    _extra.Tokens = new System.Collections.Generic.List<Token>();
+                    _extra.Tokens = new List<Token>();
                 }
 
             }
@@ -856,7 +856,7 @@ namespace Jint.Native.Json
             public int? Loc;
             public int[] Range;
 
-            public System.Collections.Generic.List<Token> Tokens;
+            public List<Token> Tokens;
         }
 
         private enum Tokens

+ 7 - 8
Jint/Native/Json/JsonSerializer.cs

@@ -19,7 +19,7 @@ namespace Jint.Native.Json
 
         Stack<object> _stack;
         string _indent, _gap;
-        List<string> _propertyList;
+        List<Key> _propertyList;
         JsValue _replacerFunction = Undefined.Instance;
 
         public JsValue Serialize(JsValue value, JsValue replacer, JsValue space)
@@ -44,7 +44,7 @@ namespace Jint.Native.Json
                     var replacerObj = replacer.AsObject();
                     if (replacerObj.Class == "Array")
                     {
-                        _propertyList = new List<string>();
+                        _propertyList = new List<Key>();
                     }
 
                     foreach (var property in replacerObj.GetOwnProperties().Select(x => x.Value))
@@ -114,7 +114,7 @@ namespace Jint.Native.Json
             }
 
             var wrapper = _engine.Object.Construct(Arguments.Empty);
-            wrapper.DefineOwnProperty("", new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable), false);
+            wrapper.DefineOwnProperty("", new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable));
 
             return Str("", wrapper);
         }
@@ -122,14 +122,13 @@ namespace Jint.Native.Json
         private JsValue Str(string key, ObjectInstance holder)
         {
 
-            var value = holder.Get(key);
+            var value = holder.Get(key, holder);
             if (value.IsObject())
             {
-                var toJson = value.AsObject().Get("toJSON");
+                var toJson = value.AsObject().Get("toJSON", value);
                 if (toJson.IsObject())
                 {
-                    var callableToJson = toJson.AsObject() as ICallable;
-                    if (callableToJson != null)
+                    if (toJson.AsObject() is ICallable callableToJson)
                     {
                         value = callableToJson.Call(value, Arguments.From(key));
                     }
@@ -259,7 +258,7 @@ namespace Jint.Native.Json
             var stepback = _indent;
             _indent = _indent + _gap;
             var partial = new List<string>();
-            var len = TypeConverter.ToUint32(value.Get("length"));
+            var len = TypeConverter.ToUint32(value.Get("length", value));
             for (int i = 0; i < len; i++)
             {
                 var strP = Str(TypeConverter.ToString(i), value);

+ 6 - 8
Jint/Native/Map/MapConstructor.cs

@@ -25,8 +25,7 @@ namespace Jint.Native.Map
         {
             var obj = new MapConstructor(engine)
             {
-                Extensible = true,
-                Prototype = engine.Function.PrototypeObject
+                _prototype = engine.Function.PrototypeObject
             };
 
             // The value of the [[Prototype]] internal property of the Map constructor is the Function prototype object
@@ -35,7 +34,7 @@ namespace Jint.Native.Map
             obj._length = new PropertyDescriptor(0, PropertyFlag.Configurable);
 
             // The initial value of Map.prototype is the Map prototype object
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -44,7 +43,7 @@ namespace Jint.Native.Map
         {
             _properties = new StringDictionarySlim<PropertyDescriptor>(2)
             {
-                [GlobalSymbolRegistry.Species._value] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(_engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable)
+                [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(_engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable)
             };
 
         }
@@ -61,15 +60,14 @@ namespace Jint.Native.Map
                 ExceptionHelper.ThrowTypeError(_engine, "Constructor Map requires 'new'");
             }
 
-            return Construct(arguments);
+            return Construct(arguments, thisObject);
         }
 
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             var instance = new MapInstance(Engine)
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                _prototype = PrototypeObject
             };
 
             if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())

+ 0 - 39
Jint/Native/Map/MapInstance.cs

@@ -15,45 +15,6 @@ namespace Jint.Native.Map
             _map = new OrderedDictionary<JsValue, JsValue>();
         }
 
-        /// Implementation from ObjectInstance official specs as the one
-        /// in ObjectInstance is optimized for the general case and wouldn't work
-        /// for arrays
-        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
-        {
-            if (!CanPut(propertyName))
-            {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(Engine);
-                }
-
-                return;
-            }
-
-            var ownDesc = GetOwnProperty(propertyName);
-
-            if (ownDesc.IsDataDescriptor())
-            {
-                var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
-                DefineOwnProperty(propertyName, valueDesc, throwOnError);
-                return;
-            }
-
-            // property is an accessor or inherited
-            var desc = GetProperty(propertyName);
-
-            if (desc.IsAccessorDescriptor())
-            {
-                var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(this, new[] {value});
-            }
-            else
-            {
-                var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
-                DefineOwnProperty(propertyName, newDesc, throwOnError);
-            }
-        }
-
         public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
             if (propertyName == KnownKeys.Size)

+ 3 - 4
Jint/Native/Map/MapPrototype.cs

@@ -23,8 +23,7 @@ namespace Jint.Native.Map
         {
             var obj = new MapPrototype(engine)
             {
-                Extensible = true,
-                Prototype = engine.Object.PrototypeObject,
+                _prototype = engine.Object.PrototypeObject,
                 _mapConstructor = mapConstructor
             };
 
@@ -37,8 +36,8 @@ namespace Jint.Native.Map
             {
                 ["length"] = new PropertyDescriptor(0, PropertyFlag.Configurable),
                 ["constructor"] = new PropertyDescriptor(_mapConstructor, PropertyFlag.NonEnumerable),
-                [GlobalSymbolRegistry.Iterator._value] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Iterator, 1, PropertyFlag.Configurable), true, false, true),
-                [GlobalSymbolRegistry.ToStringTag._value] = new PropertyDescriptor("Map", false, false, true),
+                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Iterator, 1, PropertyFlag.Configurable), true, false, true),
+                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Map", false, false, true),
                 ["clear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "clear", Clear, 0, PropertyFlag.Configurable), true, false, true),
                 ["delete"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "delete", Delete, 1, PropertyFlag.Configurable), true, false, true),
                 ["entries"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "entries", Iterator, 0, PropertyFlag.Configurable), true, false, true),

+ 1 - 2
Jint/Native/Math/MathInstance.cs

@@ -20,8 +20,7 @@ namespace Jint.Native.Math
         {
             var math = new MathInstance(engine)
             {
-                Extensible = true,
-                Prototype = engine.Object.PrototypeObject
+                _prototype = engine.Object.PrototypeObject
             };
 
             return math;

+ 5 - 7
Jint/Native/Number/NumberConstructor.cs

@@ -24,8 +24,7 @@ namespace Jint.Native.Number
         {
             var obj = new NumberConstructor(engine)
             {
-                Extensible = true,
-                Prototype = engine.Function.PrototypeObject
+                _prototype = engine.Function.PrototypeObject
             };
 
             // The value of the [[Prototype]] internal property of the Number constructor is the Function prototype object
@@ -34,7 +33,7 @@ namespace Jint.Native.Number
             obj._length = new PropertyDescriptor(1, PropertyFlag.AllForbidden);
 
             // The initial value of Number.prototype is the Number prototype object
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -134,7 +133,7 @@ namespace Jint.Native.Number
         /// </summary>
         /// <param name="arguments"></param>
         /// <returns></returns>
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             return Construct(arguments.Length > 0 ? TypeConverter.ToNumber(arguments[0]) : 0);
         }
@@ -150,9 +149,8 @@ namespace Jint.Native.Number
         {
             var instance = new NumberInstance(Engine)
             {
-                Prototype = PrototypeObject,
-                NumberData = value,
-                Extensible = true
+                _prototype = PrototypeObject,
+                NumberData = value
             };
 
             return instance;

+ 1 - 2
Jint/Native/Number/NumberPrototype.cs

@@ -26,9 +26,8 @@ namespace Jint.Native.Number
         {
             var obj = new NumberPrototype(engine)
             {
-                Prototype = engine.Object.PrototypeObject,
+                _prototype = engine.Object.PrototypeObject,
                 NumberData = JsNumber.Create(0),
-                Extensible = true,
                 _numberConstructor = numberConstructor
             };
 

+ 112 - 126
Jint/Native/Object/ObjectConstructor.cs

@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using System.Linq;
 using Jint.Collections;
 using Jint.Native.Array;
 using Jint.Native.Function;
@@ -19,28 +18,26 @@ namespace Jint.Native.Object
 
         public static ObjectConstructor CreateObjectConstructor(Engine engine)
         {
-            var obj = new ObjectConstructor(engine)
-            {
-                Extensible = true
-            };
+            var obj = new ObjectConstructor(engine);
 
             obj.PrototypeObject = ObjectPrototype.CreatePrototypeObject(engine, obj);
 
             obj._length = PropertyDescriptor.AllForbiddenDescriptor.NumberOne;
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
 
         protected override void Initialize()
         {
-            Prototype = Engine.Function.PrototypeObject;
+            _prototype = Engine.Function.PrototypeObject;
 
             _properties = new StringDictionarySlim<PropertyDescriptor>(15)
             {
                 ["getPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getPrototypeOf", GetPrototypeOf, 1), true, false, true),
                 ["getOwnPropertyDescriptor"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyDescriptor", GetOwnPropertyDescriptor, 2), true, false, true),
                 ["getOwnPropertyNames"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyNames", GetOwnPropertyNames, 1), true, false, true),
+                ["getOwnPropertySymbols"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertySymbols", GetOwnPropertySymbols, 1), true, false, true),
                 ["create"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "create", Create, 2), true, false, true),
                 ["defineProperty"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "defineProperty", DefineProperty, 3), true, false, true),
                 ["defineProperties"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "defineProperties", DefineProperties, 2), true, false, true),
@@ -50,7 +47,8 @@ namespace Jint.Native.Object
                 ["isSealed"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isSealed", IsSealed, 1), true, false, true),
                 ["isFrozen"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isFrozen", IsFrozen, 1), true, false, true),
                 ["isExtensible"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isExtensible", IsExtensible, 1), true, false, true),
-                ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Keys, 1), true, false, true)
+                ["keys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "keys", Keys, 1), true, false, true),
+                ["setPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setPrototypeOf", SetPrototypeOf, 2), true, false, true)
             };
         }
 
@@ -80,9 +78,12 @@ namespace Jint.Native.Object
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.2.2.1
         /// </summary>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
         public ObjectInstance Construct(JsValue[] arguments)
+        {
+            return Construct(arguments, this);
+        }
+
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             if (arguments.Length > 0)
             {
@@ -100,10 +101,9 @@ namespace Jint.Native.Object
             }
 
             var obj = new ObjectInstance(_engine)
-                {
-                    Extensible = true,
-                    Prototype = Engine.Object.PrototypeObject
-                };
+            {
+                _prototype = Engine.Object.PrototypeObject
+            };
 
             return obj;
         }
@@ -112,8 +112,7 @@ namespace Jint.Native.Object
         {
             var obj = new ObjectInstance(_engine)
             {
-                Extensible = true,
-                Prototype = Engine.Object.PrototypeObject,
+                _prototype = Engine.Object.PrototypeObject,
                 _properties =  propertyCount > 1
                     ? new StringDictionarySlim<PropertyDescriptor>(propertyCount)
                     : null
@@ -123,25 +122,37 @@ namespace Jint.Native.Object
         }
 
         public JsValue GetPrototypeOf(JsValue thisObject, JsValue[] arguments)
+        {
+            var o = arguments.As<ObjectInstance>(0, _engine);
+            return o.Prototype ?? Null;
+        }
+
+        public JsValue SetPrototypeOf(JsValue thisObject, JsValue[] arguments)
         {
             var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
+            TypeConverter.CheckObjectCoercible(_engine, oArg);
+
+            var prototype = arguments.At(1);
+            if (!prototype.IsObject() && !prototype.IsNull())
             {
-                ExceptionHelper.ThrowTypeError(Engine);
+                ExceptionHelper.ThrowTypeError(_engine, $"Object prototype may only be an Object or null: {prototype}");
             }
 
-            return o.Prototype ?? Null;
+            if (!(oArg is ObjectInstance o))
+            {
+                return oArg;
+            }
+
+            if (!o.SetPrototypeOf(prototype))
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+            return o;
         }
 
         public JsValue GetOwnPropertyDescriptor(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
+            var o = arguments.As<ObjectInstance>(0, _engine);
 
             var p = arguments.At(1);
             var name = TypeConverter.ToPropertyKey(p);
@@ -152,52 +163,52 @@ namespace Jint.Native.Object
 
         public JsValue GetOwnPropertyNames(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
+            var o = arguments.As<ObjectInstance>(0, _engine);
 
             uint n = 0;
 
             ArrayInstance array = null;
-            var ownProperties = o.GetOwnProperties().ToList();
+            var ownProperties = o.GetOwnPropertyKeys(Types.String);
             if (o is StringInstance s)
             {
                 var length = s.PrimitiveValue.Length;
-                array = Engine.Array.ConstructFast((uint) (ownProperties.Count + length));
+                array = Engine.Array.ConstructFast((uint) (ownProperties.Count + length + 1));
                 for (var i = 0; i < length; i++)
                 {
-                    array.SetIndexValue(n, TypeConverter.ToString(i), updateLength: false);
-                    n++;
+                    array.SetIndexValue(n++, TypeConverter.ToString(i), updateLength: false);
                 }
+
+                array.SetIndexValue(n++, JsString.LengthString, updateLength: false);
             }
 
             array = array ?? Engine.Array.ConstructFast((uint) ownProperties.Count);
             for (var i = 0; i < ownProperties.Count; i++)
             {
                 var p = ownProperties[i];
-                array.SetIndexValue(n, p.Key, false);
-                n++;
+                array.SetIndexValue(n++, p, false);
             }
 
             array.SetLength(n);
             return array;
         }
 
-        public JsValue Create(JsValue thisObject, JsValue[] arguments)
+        public JsValue GetOwnPropertySymbols(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
+            var o = arguments.As<ObjectInstance>(0, _engine);
+            var keys = o.GetOwnPropertyKeys(Types.Symbol);
+            return _engine.Array.Construct(keys.ToArray());
+        }
 
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null) && !oArg.IsNull())
+        public JsValue Create(JsValue thisObject, JsValue[] arguments)
+        {
+            var prototype = arguments.At(0);
+            if (!prototype.IsObject() && !prototype.IsNull())
             {
-                ExceptionHelper.ThrowTypeError(Engine);
+                ExceptionHelper.ThrowTypeError(_engine, "Object prototype may only be an Object or null: " + prototype);
             }
 
             var obj = Engine.Object.Construct(Arguments.Empty);
-            obj.Prototype = o;
+            obj._prototype = prototype.IsNull() ? null : prototype.AsObject();
 
             var properties = arguments.At(1);
             if (!properties.IsUndefined())
@@ -214,32 +225,20 @@ namespace Jint.Native.Object
 
         public JsValue DefineProperty(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
+            var o = arguments.As<ObjectInstance>(0, _engine);
             var p = arguments.At(1);
             var name = TypeConverter.ToPropertyKey(p);
 
             var attributes = arguments.At(2);
             var desc = PropertyDescriptor.ToPropertyDescriptor(Engine, attributes);
+            o.DefinePropertyOrThrow(name, desc);
 
-            o.DefineOwnProperty(name, desc, true);
             return o;
         }
 
         public JsValue DefineProperties(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
+            var o = arguments.As<ObjectInstance>(0, _engine);
             var properties = arguments.At(1);
             var props = TypeConverter.ToObject(Engine, properties);
             var descriptors = new List<KeyValuePair<string, PropertyDescriptor>>();
@@ -250,13 +249,13 @@ namespace Jint.Native.Object
                     continue;
                 }
 
-                var descObj = props.Get(p.Key);
+                var descObj = props.Get(p.Key, props);
                 var desc = PropertyDescriptor.ToPropertyDescriptor(Engine, descObj);
                 descriptors.Add(new KeyValuePair<string, PropertyDescriptor>(p.Key, desc));
             }
             foreach (var pair in descriptors)
             {
-                o.DefineOwnProperty(pair.Key, pair.Value, true);
+                o.DefinePropertyOrThrow(pair.Key, pair.Value);
             }
 
             return o;
@@ -264,14 +263,8 @@ namespace Jint.Native.Object
 
         public JsValue Seal(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
-            var properties = new List<KeyValuePair<string, PropertyDescriptor>>(o.GetOwnProperties());
+            var o = arguments.As<ObjectInstance>(0, _engine);
+            var properties = new List<KeyValuePair<Key, PropertyDescriptor>>(o.GetOwnProperties());
             foreach (var prop in properties)
             {
                 var propertyDescriptor = prop.Value;
@@ -281,24 +274,18 @@ namespace Jint.Native.Object
                     FastSetProperty(prop.Key, propertyDescriptor);
                 }
 
-                o.DefineOwnProperty(prop.Key, propertyDescriptor, true);
+                o.DefinePropertyOrThrow(prop.Key, propertyDescriptor);
             }
 
-            o.Extensible = false;
+            o.PreventExtensions();
 
             return o;
         }
 
         public JsValue Freeze(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
-            var properties = new List<KeyValuePair<string, PropertyDescriptor>>(o.GetOwnProperties());
+            var o = arguments.As<ObjectInstance>(0, _engine);
+            var properties = new List<KeyValuePair<Key, PropertyDescriptor>>(o.GetOwnProperties());
             foreach (var p in properties)
             {
                 var desc = o.GetOwnProperty(p.Key);
@@ -317,37 +304,24 @@ namespace Jint.Native.Object
                     mutable.Configurable = false;
                     desc = mutable;
                 }
-                o.DefineOwnProperty(p.Key, desc, true);
+                o.DefinePropertyOrThrow(p.Key, desc);
             }
 
-            o.Extensible = false;
+            o.PreventExtensions();
 
             return o;
         }
 
         public JsValue PreventExtensions(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
-            o.Extensible = false;
-
+            var o = arguments.As<ObjectInstance>(0, _engine);
+            o.PreventExtensions();
             return o;
         }
 
         public JsValue IsSealed(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
+            var o = arguments.As<ObjectInstance>(0, _engine);
             foreach (var prop in o.GetOwnProperties())
             {
                 if (prop.Value.Configurable)
@@ -366,13 +340,7 @@ namespace Jint.Native.Object
 
         public JsValue IsFrozen(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
+            var o = arguments.As<ObjectInstance>(0, _engine);
             foreach (var pair in o.GetOwnProperties())
             {
                 var desc = pair.Value;
@@ -399,39 +367,57 @@ namespace Jint.Native.Object
 
         public JsValue IsExtensible(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
+            var o = arguments.As<ObjectInstance>(0, _engine);
             return o.Extensible;
         }
 
         public JsValue Keys(JsValue thisObject, JsValue[] arguments)
         {
-            var oArg = arguments.At(0);
-            var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null))
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
+            return EnumerableOwnPropertyNames(arguments, EnumerableOwnPropertyNamesKind.Key);
+        }
 
-            var enumerableProperties = o.GetOwnProperties()
-                .Where(x => x.Value.Enumerable)
-                .ToArray();
-            var n = enumerableProperties.Length;
+        private JsValue EnumerableOwnPropertyNames(JsValue[] arguments, EnumerableOwnPropertyNamesKind kind)
+        {
+            var o = arguments.As<ObjectInstance>(0, _engine);
+            var ownKeys = o.GetOwnPropertyKeys(Types.String);
 
-            var array = Engine.Array.ConstructFast((uint) n);
+            var array = Engine.Array.ConstructFast((uint) ownKeys.Count);
             uint index = 0;
-            foreach (var prop in enumerableProperties)
+            foreach (var key in ownKeys)
             {
-                var p = prop.Key;
-                array.SetIndexValue(index, p, updateLength: false);
-                index++;
+                var propertyName = key.ToPropertyKey();
+                var desc = o.GetOwnProperty(propertyName);
+                if (desc != PropertyDescriptor.Undefined && desc.Enumerable)
+                {
+                    if (kind == EnumerableOwnPropertyNamesKind.Key)
+                    {
+                        array.SetIndexValue(index, key, updateLength: false);
+                    }
+                    else
+                    {
+                        var value = o.Get(propertyName, o);
+                        if (kind == EnumerableOwnPropertyNamesKind.Value)
+                        {
+                            array.SetIndexValue(index, value, updateLength: false);
+                        }
+                        else
+                        {
+                            array.SetIndexValue(index, _engine.Array.Construct(new [] { key, value }), updateLength: false);
+                        }
+                    }
+                    index++;
+                }
             }
+
+            array.SetLength(index);
             return array;
         }
+
+        private enum EnumerableOwnPropertyNamesKind
+        {
+            Key,
+            Value,
+            KeyValue
+        }
     }
 }

+ 333 - 168
Jint/Native/Object/ObjectInstance.cs

@@ -21,11 +21,10 @@ namespace Jint.Native.Object
 {
     public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     {
-        private static readonly string ToPrimitiveSymbolName = GlobalSymbolRegistry.ToPrimitive._value;
-
         internal StringDictionarySlim<PropertyDescriptor> _properties;
 
         private bool _initialized;
+        internal ObjectInstance _prototype;
         private readonly string _class;
         protected readonly Engine _engine;
 
@@ -37,6 +36,7 @@ namespace Jint.Native.Object
         {
             _engine = engine;
             _class = objectClass;
+            Extensible = true;
         }
 
         public Engine Engine => _engine;
@@ -44,13 +44,13 @@ namespace Jint.Native.Object
         /// <summary>
         /// The prototype of this object.
         /// </summary>
-        public ObjectInstance Prototype { get; set; }
+        public ObjectInstance Prototype => GetPrototypeOf();
 
         /// <summary>
         /// If true, own properties may be added to the
         /// object.
         /// </summary>
-        public bool Extensible { get; set; }
+        public virtual bool Extensible { get; private set; }
 
         /// <summary>
         /// A String value indicating a specification defined
@@ -58,7 +58,7 @@ namespace Jint.Native.Object
         /// </summary>
         public string Class => _class;
 
-        public virtual IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
+        public virtual IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
         {
             EnsureInitialized();
 
@@ -70,6 +70,57 @@ namespace Jint.Native.Object
                 }
             }
         }
+        
+        internal virtual List<JsValue> GetOwnPropertyKeys(Types types)
+        {
+            EnsureInitialized();
+
+            if (_properties == null)
+            {
+                return new List<JsValue>();
+            }
+            
+            var keys = new List<JsValue>(_properties.Count);
+            var propertyKeys = new List<JsValue>();
+            List<JsValue> symbolKeys = null;
+            
+            foreach (var pair in _properties)
+            {
+                if ((pair.Key.Type & types) == 0)
+                {
+                    continue;
+                }
+                
+                var isArrayIndex = ulong.TryParse(pair.Key, out var index);
+                if (pair.Key.Type != Types.Symbol)
+                {
+                    if (isArrayIndex)
+                    {
+                        keys.Add(pair.Key);
+                    }
+                    else
+                    {
+                        propertyKeys.Add(pair.Key);
+                    }
+                }
+                else
+                {
+                    symbolKeys ??= new List<JsValue>();
+                    symbolKeys.Add(pair.Key);
+                }
+            }
+            
+            keys.Sort((v1, v2) => TypeConverter.ToNumber(v1).CompareTo(TypeConverter.ToNumber(v2)));
+
+            keys.AddRange(propertyKeys);
+            if (symbolKeys != null)
+            {
+                keys.AddRange(symbolKeys);
+            }
+
+            return keys;
+        }
+
 
         protected virtual void AddProperty(in Key propertyName, PropertyDescriptor descriptor)
         {
@@ -106,16 +157,20 @@ namespace Jint.Native.Object
             _properties?.Remove(propertyName);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public JsValue Get(in Key propertyName)
+        {
+            return Get(propertyName, this);
+        }
+
         /// <summary>
         /// Returns the value of the named property.
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.3
         /// </summary>
-        /// <param name="propertyName"></param>
-        /// <returns></returns>
-        public virtual JsValue Get(in Key propertyName)
+        public virtual JsValue Get(in Key propertyName, JsValue receiver)
         {
             var desc = GetProperty(propertyName);
-            return UnwrapJsValue(desc);
+            return UnwrapJsValue(desc, receiver);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -239,62 +294,93 @@ namespace Jint.Native.Object
             return Prototype.TryGetValue(propertyName, out value);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool Set(Key p, JsValue v, bool throwOnError)
+        {
+            if (!Set(p, v, this) && throwOnError)
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            return true;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public bool Set(in Key propertyName, JsValue value)
+        {
+            return Set(propertyName, value, this);
+        }
+
         /// <summary>
-        /// Sets the specified named property to the value
-        /// of the second parameter. The flag controls
-        /// failure handling.
+        /// http://www.ecma-international.org/ecma-262/#sec-ordinary-object-internal-methods-and-internal-slots-set-p-v-receiver
         /// </summary>
-        /// <param name="propertyName"></param>
-        /// <param name="value"></param>
-        /// <param name="throwOnError"></param>
-        public virtual void Put(in Key propertyName, JsValue value, bool throwOnError)
+        public virtual bool Set(in Key propertyName, JsValue value, JsValue receiver)
         {
-            if (!CanPut(propertyName))
+            var ownDesc = GetOwnProperty(propertyName);
+
+            if (ownDesc == PropertyDescriptor.Undefined)
             {
-                if (throwOnError)
+                var parent = GetPrototypeOf();
+                if (!(parent is null))
                 {
-                    ExceptionHelper.ThrowTypeError(Engine);
+                    return parent.Set(propertyName, value, receiver);
+                }
+                else
+                {
+                    ownDesc = new PropertyDescriptor(Undefined, PropertyFlag.ConfigurableEnumerableWritable);
                 }
-
-                return;
             }
 
-            var ownDesc = GetOwnProperty(propertyName);
-
             if (ownDesc.IsDataDescriptor())
             {
-                ownDesc.Value = value;
-                return;
+                if (!ownDesc.Writable)
+                {
+                    return false;
+                }
 
-                // as per specification
-                // var valueDesc = new PropertyDescriptor(value: value, writable: null, enumerable: null, configurable: null);
-                // DefineOwnProperty(propertyName, valueDesc, throwOnError);
-                // return;
-            }
+                if (!(receiver is ObjectInstance oi))
+                {
+                    return false;
+                }
 
-            // property is an accessor or inherited
-            var desc = GetProperty(propertyName);
+                var existingDescriptor = oi.GetOwnProperty(propertyName);
+                if (existingDescriptor != PropertyDescriptor.Undefined)
+                {
+                    if (existingDescriptor.IsAccessorDescriptor())
+                    {
+                        return false;
+                    }
 
-            if (desc.IsAccessorDescriptor())
-            {
-                var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(this, new[] {value});
+                    if (!existingDescriptor.Writable)
+                    {
+                        return false;
+                    }
+
+                    var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
+                    return oi.DefineOwnProperty(propertyName, valueDesc);
+                }
+                else
+                {
+                    return oi.CreateDataProperty(propertyName, value);
+                }
             }
-            else
+
+            if (!(ownDesc.Set is ICallable setter))
             {
-                var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
-                DefineOwnProperty(propertyName, newDesc, throwOnError);
+                return false;
             }
-        }
 
+            setter.Call(receiver, new[] {value});
+
+            return true;
+        }
+        
         /// <summary>
         /// Returns a Boolean value indicating whether a
         /// [[Put]] operation with PropertyName can be
         /// performed.
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.12.4
         /// </summary>
-        /// <param name="propertyName"></param>
-        /// <returns></returns>
         public bool CanPut(in Key propertyName)
         {
             var desc = GetOwnProperty(propertyName);
@@ -347,27 +433,43 @@ namespace Jint.Native.Object
         }
 
         /// <summary>
-        /// Returns a Boolean value indicating whether the
-        /// object already has a property with the given
-        /// name.
+        /// http://www.ecma-international.org/ecma-262/#sec-ordinary-object-internal-methods-and-internal-slots-hasproperty-p
         /// </summary>
-        /// <param name="propertyName"></param>
-        /// <returns></returns>
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool HasProperty(in Key propertyName)
+        public virtual bool HasProperty(in Key propertyName)
         {
-            return GetProperty(propertyName) != PropertyDescriptor.Undefined;
+            var hasOwn = GetOwnProperty(propertyName);
+            if (hasOwn != PropertyDescriptor.Undefined)
+            {
+                return true;
+            }
+
+            var parent = GetPrototypeOf();
+            if (parent != null)
+            {
+                return parent.HasProperty(propertyName);
+            }
+
+            return false;
         }
 
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/#sec-deletepropertyorthrow
+        /// </summary>
+        public bool DeletePropertyOrThrow(in Key propertyName)
+        {
+            if (!Delete(propertyName))
+            {
+                ExceptionHelper.ThrowTypeError(Engine);
+            }
+            return true;
+        }
+        
         /// <summary>
         /// Removes the specified named own property
         /// from the object. The flag controls failure
         /// handling.
         /// </summary>
-        /// <param name="propertyName"></param>
-        /// <param name="throwOnError"></param>
-        /// <returns></returns>
-        public virtual bool Delete(in Key propertyName, bool throwOnError)
+        public virtual bool Delete(in Key propertyName)
         {
             var desc = GetOwnProperty(propertyName);
 
@@ -382,11 +484,6 @@ namespace Jint.Native.Object
                 return true;
             }
 
-            if (throwOnError)
-            {
-                ExceptionHelper.ThrowTypeError(Engine);
-            }
-
             return false;
         }
 
@@ -399,7 +496,7 @@ namespace Jint.Native.Object
 
             if (hint == Types.String || (hint == Types.None && Class == "Date"))
             {
-                var jsValue = Get(ToPrimitiveSymbolName);
+                var jsValue = Get(GlobalSymbolRegistry.ToPrimitive, this);
                 if (!jsValue.IsNullOrUndefined())
                 {
                     if (jsValue is ICallable toPrimitive)
@@ -419,7 +516,7 @@ namespace Jint.Native.Object
                     const string message = "'Value returned for property 'Symbol(Symbol.toPrimitive)' of object is not a function";
                     return ExceptionHelper.ThrowTypeError<JsValue>(_engine, message);
                 }
-                if (Get("toString") is ICallable toString)
+                if (Get("toString", this) is ICallable toString)
                 {
                     var str = toString.Call(this, Arguments.Empty);
                     if (str.IsPrimitive())
@@ -428,7 +525,7 @@ namespace Jint.Native.Object
                     }
                 }
 
-                if (Get("valueOf") is ICallable valueOf)
+                if (Get("valueOf", this) is ICallable valueOf)
                 {
                     var val = valueOf.Call(this, Arguments.Empty);
                     if (val.IsPrimitive())
@@ -442,7 +539,7 @@ namespace Jint.Native.Object
 
             if (hint == Types.Number || hint == Types.None)
             {
-                var jsValue = Get(ToPrimitiveSymbolName);
+                var jsValue = Get(GlobalSymbolRegistry.ToPrimitive, this);
                 if (!jsValue.IsNullOrUndefined())
                 {
                     if (jsValue is ICallable toPrimitive)
@@ -463,7 +560,7 @@ namespace Jint.Native.Object
                     return ExceptionHelper.ThrowTypeError<JsValue>(_engine, message);
                 }
 
-                if (Get("valueOf") is ICallable valueOf)
+                if (Get("valueOf", this) is ICallable valueOf)
                 {
                     var val = valueOf.Call(this, Arguments.Empty);
                     if (val.IsPrimitive())
@@ -472,7 +569,7 @@ namespace Jint.Native.Object
                     }
                 }
 
-                if (Get("toString") is ICallable toString)
+                if (Get("toString", this) is ICallable toString)
                 {
                     var str = toString.Call(this, Arguments.Empty);
                     if (str.IsPrimitive())
@@ -487,37 +584,48 @@ namespace Jint.Native.Object
             return ToString();
         }
 
+        public bool DefinePropertyOrThrow(in Key propertyName, PropertyDescriptor desc)
+        {
+            if (!DefineOwnProperty(propertyName, desc))
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            return true;
+        } 
+        
         /// <summary>
         /// Creates or alters the named own property to
         /// have the state described by a Property
         /// Descriptor. The flag controls failure handling.
         /// </summary>
-        /// <param name="propertyName"></param>
-        /// <param name="desc"></param>
-        /// <param name="throwOnError"></param>
-        /// <returns></returns>
-        public virtual bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc, bool throwOnError)
+        public virtual bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
         {
             var current = GetOwnProperty(propertyName);
+            var extensible = Extensible;
 
             if (current == desc)
             {
                 return true;
             }
 
+            return ValidateAndApplyPropertyDescriptor(this, propertyName, extensible, desc, current);
+        }
+
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/#sec-validateandapplypropertydescriptor
+        /// </summary>
+        protected static bool ValidateAndApplyPropertyDescriptor(ObjectInstance o, in Key propertyName, bool extensible, PropertyDescriptor desc, PropertyDescriptor current)
+        {
             var descValue = desc.Value;
             if (current == PropertyDescriptor.Undefined)
             {
-                if (!Extensible)
+                if (!extensible)
                 {
-                    if (throwOnError)
-                    {
-                        ExceptionHelper.ThrowTypeError(Engine);
-                    }
-
                     return false;
                 }
-                else
+
+                if (o is object)
                 {
                     if (desc.IsGenericDescriptor() || desc.IsDataDescriptor())
                     {
@@ -538,18 +646,18 @@ namespace Jint.Native.Object
                             };
                         }
 
-                        SetOwnProperty(propertyName, propertyDescriptor);
+                        o.SetOwnProperty(propertyName, propertyDescriptor);
                     }
                     else
                     {
-                        SetOwnProperty(propertyName, new GetSetPropertyDescriptor(desc));
+                        o.SetOwnProperty(propertyName, new GetSetPropertyDescriptor(desc));
                     }
                 }
 
                 return true;
             }
 
-            // Step 5
+            // Step 3
             var currentGet = current.Get;
             var currentSet = current.Set;
             var currentValue = current.Value;
@@ -581,21 +689,11 @@ namespace Jint.Native.Object
             {
                 if (desc.Configurable)
                 {
-                    if (throwOnError)
-                    {
-                        ExceptionHelper.ThrowTypeError(Engine);
-                    }
-
                     return false;
                 }
 
                 if (desc.EnumerableSet && (desc.Enumerable != current.Enumerable))
                 {
-                    if (throwOnError)
-                    {
-                        ExceptionHelper.ThrowTypeError(Engine);
-                    }
-
                     return false;
                 }
             }
@@ -606,30 +704,28 @@ namespace Jint.Native.Object
                 {
                     if (!current.Configurable)
                     {
-                        if (throwOnError)
-                        {
-                            ExceptionHelper.ThrowTypeError(Engine);
-                        }
-
                         return false;
                     }
 
-                    if (current.IsDataDescriptor())
-                    {
-                        var flags = current.Flags & ~(PropertyFlag.Writable | PropertyFlag.WritableSet);
-                        SetOwnProperty(propertyName, current = new GetSetPropertyDescriptor(
-                            get: JsValue.Undefined,
-                            set: JsValue.Undefined,
-                            flags
-                        ));
-                    }
-                    else
+
+                    if (o is object)
                     {
                         var flags = current.Flags & ~(PropertyFlag.Writable | PropertyFlag.WritableSet);
-                        SetOwnProperty(propertyName, current = new PropertyDescriptor(
-                            value: JsValue.Undefined,
-                            flags
-                        ));
+                        if (current.IsDataDescriptor())
+                        {
+                            o.SetOwnProperty(propertyName, current = new GetSetPropertyDescriptor(
+                                get: Undefined,
+                                set: Undefined,
+                                flags
+                            ));
+                        }
+                        else
+                        {
+                            o.SetOwnProperty(propertyName, current = new PropertyDescriptor(
+                                value: Undefined,
+                                flags
+                            ));
+                        }
                     }
                 }
                 else if (current.IsDataDescriptor() && desc.IsDataDescriptor())
@@ -638,11 +734,6 @@ namespace Jint.Native.Object
                     {
                         if (!current.Writable && desc.Writable)
                         {
-                            if (throwOnError)
-                            {
-                                ExceptionHelper.ThrowTypeError(Engine);
-                            }
-
                             return false;
                         }
 
@@ -650,11 +741,6 @@ namespace Jint.Native.Object
                         {
                             if (!ReferenceEquals(descValue, null) && !JintExpression.SameValue(descValue, currentValue))
                             {
-                                if (throwOnError)
-                                {
-                                    ExceptionHelper.ThrowTypeError(Engine);
-                                }
-
                                 return false;
                             }
                         }
@@ -668,54 +754,53 @@ namespace Jint.Native.Object
                             ||
                             (!ReferenceEquals(descGet, null) && !JintExpression.SameValue(descGet, currentGet ?? Undefined)))
                         {
-                            if (throwOnError)
-                            {
-                                ExceptionHelper.ThrowTypeError(Engine);
-                            }
-
                             return false;
                         }
                     }
                 }
             }
 
-            if (!ReferenceEquals(descValue, null))
+            if (o is object)
             {
-                current.Value = descValue;
-            }
-
-            if (desc.WritableSet)
-            {
-                current.Writable = desc.Writable;
-            }
+                
+                if (!ReferenceEquals(descValue, null))
+                {
+                    current.Value = descValue;
+                }
 
-            if (desc.EnumerableSet)
-            {
-                current.Enumerable = desc.Enumerable;
-            }
+                if (desc.WritableSet)
+                {
+                    current.Writable = desc.Writable;
+                }
 
-            if (desc.ConfigurableSet)
-            {
-                current.Configurable = desc.Configurable;
-            }
+                if (desc.EnumerableSet)
+                {
+                    current.Enumerable = desc.Enumerable;
+                }
 
-            PropertyDescriptor mutable = null;
-            if (!ReferenceEquals(descGet, null))
-            {
-                mutable = new GetSetPropertyDescriptor(mutable ?? current);
-                ((GetSetPropertyDescriptor) mutable).SetGet(descGet);
-            }
+                if (desc.ConfigurableSet)
+                {
+                    current.Configurable = desc.Configurable;
+                }
+                
+                PropertyDescriptor mutable = null;
+                if (!ReferenceEquals(descGet, null))
+                {
+                    mutable = new GetSetPropertyDescriptor(mutable ?? current);
+                    ((GetSetPropertyDescriptor) mutable).SetGet(descGet);
+                }
 
-            if (!ReferenceEquals(descSet, null))
-            {
-                mutable = new GetSetPropertyDescriptor(mutable ?? current);
-                ((GetSetPropertyDescriptor) mutable).SetSet(descSet);
-            }
+                if (!ReferenceEquals(descSet, null))
+                {
+                    mutable = new GetSetPropertyDescriptor(mutable ?? current);
+                    ((GetSetPropertyDescriptor) mutable).SetSet(descSet);
+                }
 
-            if (mutable != null)
-            {
-                // replace old with new type that supports get and set
-                FastSetProperty(propertyName, mutable);
+                if (mutable != null)
+                {
+                    // replace old with new type that supports get and set
+                    o.FastSetProperty(propertyName, mutable);
+                }
             }
 
             return true;
@@ -775,7 +860,7 @@ namespace Jint.Native.Object
                 case "Array":
                     if (this is ArrayInstance arrayInstance)
                     {
-                        var len = TypeConverter.ToInt32(arrayInstance.Get("length"));
+                        var len = TypeConverter.ToInt32(arrayInstance.Get("length", arrayInstance));
                         var result = new object[len];
                         for (var k = 0; k < len; k++)
                         {
@@ -783,7 +868,7 @@ namespace Jint.Native.Object
                             var kpresent = arrayInstance.HasProperty(pk);
                             if (kpresent)
                             {
-                                var kvalue = arrayInstance.Get(pk);
+                                var kvalue = arrayInstance.Get(pk, arrayInstance);
                                 result[k] = kvalue.ToObject();
                             }
                             else
@@ -862,7 +947,7 @@ namespace Jint.Native.Object
                             continue;
                         }
 
-                        o.Add(p.Key, Get(p.Key).ToObject());
+                        o.Add(p.Key, Get(p.Key, this).ToObject());
                     }
 
                     return o;
@@ -906,14 +991,14 @@ namespace Jint.Native.Object
 
                 return (long) System.Math.Max(
                     0, 
-                    System.Math.Min(len, ArrayPrototype.ArrayOperations.MaxArrayLikeLength));
+                    System.Math.Min(len, ArrayOperations.MaxArrayLikeLength));
             }
 
             bool TryGetValue(uint idx, out JsValue jsValue)
             {
                 var property = TypeConverter.ToString(idx);
                 var kPresent = HasProperty(property);
-                jsValue = kPresent ? Get(property) : Undefined;
+                jsValue = kPresent ? Get(property, this) : Undefined;
                 return kPresent;
             }
 
@@ -965,24 +1050,104 @@ namespace Jint.Native.Object
             return null;
         }
 
-        internal virtual bool IsConcatSpreadable => TryGetIsConcatSpreadable(out var isConcatSpreadable) && isConcatSpreadable;
+        internal bool IsConcatSpreadable
+        {
+            get
+            {
+                var spreadable = Get(GlobalSymbolRegistry.IsConcatSpreadable, this);
+                if (!spreadable.IsUndefined())
+                {
+                    return TypeConverter.ToBoolean(spreadable);
+                }
+                return IsArray();
+            }
+        }
 
         internal virtual bool IsArrayLike => TryGetValue("length", out var lengthValue)
                                              && lengthValue.IsNumber()
                                              && ((JsNumber) lengthValue)._value >= 0;
 
-        protected bool TryGetIsConcatSpreadable(out bool isConcatSpreadable)
+        public virtual JsValue PreventExtensions()
         {
-            isConcatSpreadable = false;
-            if (TryGetValue(GlobalSymbolRegistry.IsConcatSpreadable._value, out var isConcatSpreadableValue)
-                && !ReferenceEquals(isConcatSpreadableValue, null)
-                && !isConcatSpreadableValue.IsUndefined())
+            Extensible = false;
+            return JsBoolean.True;
+        }
+
+        protected virtual ObjectInstance GetPrototypeOf()
+        {
+            return _prototype;
+        }
+
+        /// <summary>
+        /// https://tc39.es/ecma262/#sec-ordinarysetprototypeof
+        /// </summary>
+        public virtual bool SetPrototypeOf(JsValue value)
+        {
+            if (!value.IsObject() && !value.IsNull())
+            {
+                ExceptionHelper.ThrowArgumentException();
+            }
+
+            var current = _prototype ?? Null;
+            if (ReferenceEquals(value, current))
             {
-                isConcatSpreadable = TypeConverter.ToBoolean(isConcatSpreadableValue);
                 return true;
             }
 
-            return false;
+            if (!Extensible)
+            {
+                return false;
+            }
+
+            if (value.IsNull())
+            {
+                _prototype = null;
+                return true;
+            }
+
+            // validate chain
+            var p = value as ObjectInstance;
+            bool done = false;
+            while (!done)
+            {
+                if (p is null)
+                {
+                    done = true;
+                }
+                else if (ReferenceEquals(p, this))
+                {
+                    return false;
+                }
+                else
+                {
+                    p = p._prototype;
+                }
+            }
+
+            _prototype = value as ObjectInstance;
+            return true;
+        }
+        
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/#sec-createdatapropertyorthrow
+        /// </summary>
+        internal bool CreateDataProperty(Key p, JsValue v)
+        {
+            var newDesc = new PropertyDescriptor(v, PropertyFlag.ConfigurableEnumerableWritable);
+            return DefineOwnProperty(p, newDesc);
+        }   
+        
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/#sec-createdataproperty
+        /// </summary>
+        internal bool CreateDataPropertyOrThrow( Key p, JsValue v)
+        {
+            if (!CreateDataProperty(p, v))
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            return true;
         }
 
         public override bool Equals(JsValue obj)

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

@@ -17,7 +17,6 @@ namespace Jint.Native.Object
         {
             var obj = new ObjectPrototype(engine)
             {
-                Extensible = true,
                 _objectConstructor = objectConstructor
             };
 
@@ -87,7 +86,7 @@ namespace Jint.Native.Object
         private JsValue ToLocaleString(JsValue thisObject, JsValue[] arguments)
         {
             var o = TypeConverter.ToObject(Engine, thisObject);
-            var toString = o.Get("toString").TryCast<ICallable>(x =>
+            var toString = o.Get("toString", o).TryCast<ICallable>(x =>
             {
                 ExceptionHelper.ThrowTypeError(Engine);
             });

+ 92 - 0
Jint/Native/Proxy/ProxyConstructor.cs

@@ -0,0 +1,92 @@
+using System;
+using Jint.Collections;
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.Proxy
+{
+    public sealed class ProxyConstructor : FunctionInstance, IConstructor
+    {
+        private static readonly JsString _name = new JsString("Proxy");
+
+        private ProxyConstructor(Engine engine)
+            : base(engine, _name, strict: false)
+        {
+        }
+
+        public static ProxyConstructor CreateProxyConstructor(Engine engine)
+        {
+            var obj = new ProxyConstructor(engine);
+            obj._length = new PropertyDescriptor(2, PropertyFlag.Configurable);
+            return obj;
+        }
+
+        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Constructor Proxy requires 'new'");
+        }
+
+        /// <summary>
+        /// https://www.ecma-international.org/ecma-262/6.0/index.html#sec-proxy-object-internal-methods-and-internal-slots-construct-argumentslist-newtarget
+        /// </summary>
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
+        {
+            var target = arguments.At(0);
+            var handler = arguments.At(1);
+
+            if (!target.IsObject() || !handler.IsObject())
+            {
+                return ExceptionHelper.ThrowTypeError<ObjectInstance>(_engine, "Cannot create proxy with a non-object as target or handler");
+            }
+            return Construct(target.AsObject(), handler.AsObject());
+        }
+
+        protected override void Initialize()
+        {
+            _properties = new StringDictionarySlim<PropertyDescriptor>(3)
+            {
+                ["revocable"] = new PropertyDescriptor(new ClrFunctionInstance(_engine, "revocable", Revocable, 2, PropertyFlag.Configurable), true, true, true)
+            };
+        }
+
+        protected override ObjectInstance GetPrototypeOf()
+        {
+            return _engine.Function.Prototype;
+        }
+
+        public ProxyInstance Construct(ObjectInstance target, ObjectInstance handler)
+        {
+            if (target is ProxyInstance targetProxy && targetProxy._handler is null)
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+            if (handler is ProxyInstance handlerProxy && handlerProxy._handler is null)
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+            var instance = new ProxyInstance(Engine, target, handler);
+            return instance;
+        }
+
+        private JsValue Revocable(JsValue thisObject, JsValue[] arguments)
+        {
+            var p = _engine.Proxy.Construct(arguments, Undefined);
+            var result = _engine.Object.Construct(ArrayExt.Empty<JsValue>());
+            result.DefineOwnProperty("revoke", new PropertyDescriptor(new ClrFunctionInstance(_engine, name: null, Revoke, 0, PropertyFlag.Configurable), PropertyFlag.ConfigurableEnumerableWritable));
+            result.DefineOwnProperty("proxy", new PropertyDescriptor(Construct(arguments, thisObject), PropertyFlag.ConfigurableEnumerableWritable));
+            return result;
+        }
+
+        private JsValue Revoke(JsValue thisObject, JsValue[] arguments)
+        {
+            var o = thisObject.AsObject();
+            var proxy = (ProxyInstance) o.Get("proxy");
+            proxy._handler = null;
+            proxy._target = null;
+            return Undefined;
+        }
+    }
+}

+ 486 - 0
Jint/Native/Proxy/ProxyInstance.cs

@@ -0,0 +1,486 @@
+using System.Collections.Generic;
+using Jint.Native.Function;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.Proxy
+{
+    public class ProxyInstance : FunctionInstance, IConstructor
+    {
+        internal ObjectInstance _target;
+        internal ObjectInstance _handler;
+
+        private static readonly Key TrapApply = "apply";
+        private static readonly Key TrapGet = "get";
+        private static readonly Key TrapSet = "set";
+        private static readonly Key TrapPreventExtensions = "preventExtensions";
+        private static readonly Key TrapIsExtensible = "isExtensible";
+        private static readonly Key TrapDefineProperty = "defineProperty";
+        private static readonly Key TrapDeleteProperty = "deleteProperty";
+        private static readonly Key TrapGetOwnPropertyDescriptor = "getOwnPropertyDescriptor";
+        private static readonly Key TrapHas = "has";
+        private static readonly Key TrapGetProtoTypeOf = "getPrototypeOf";
+        private static readonly Key TrapSetProtoTypeOf = "setPrototypeOf";
+        private static readonly Key TrapOwnKeys = "ownKeys";
+        private static readonly Key TrapConstruct = "construct";
+
+        private static readonly Key KeyFunctionRevoke = "revoke";
+
+        public ProxyInstance(
+            Engine engine,
+            ObjectInstance target,
+            ObjectInstance handler)
+            : base(engine, JsString.Empty, false, "Proxy")
+        {
+            _target = target;
+            _handler = handler;
+        }
+
+        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        {
+            var jsValues = new[] { _target, thisObject, _engine.Array.Construct(arguments) };
+            if (TryCallHandler(TrapApply, jsValues, out var result))
+            {
+                return result;
+            }
+
+            return ((ICallable) _target).Call(thisObject, arguments);
+        }
+
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
+        {
+            var argArray = _engine.Array.Construct(arguments, _engine.Array);
+
+            if (!TryCallHandler(TrapConstruct, new[] { _target, argArray, newTarget }, out var result))
+            {
+                if (!(_target is IConstructor constructor))
+                {
+                    return ExceptionHelper.ThrowTypeError<ObjectInstance>(_engine);
+                }
+                return constructor.Construct(arguments, newTarget);
+            }
+
+            if (!(result is ObjectInstance oi))
+            {
+                return ExceptionHelper.ThrowTypeError<ObjectInstance>(_engine);
+            }
+           
+            return oi;
+        }
+
+        public override bool IsArray()
+        {
+            if (_handler is null)
+            {
+                return ExceptionHelper.ThrowTypeError<bool>(_engine);
+            }
+            return _target.IsArray();
+        }
+
+        internal override bool IsConstructor => 
+            _handler.TryGetValue(TrapConstruct, out var handlerFunction) && handlerFunction is IConstructor;
+
+        public override JsValue Get(in Key propertyName, JsValue receiver)
+        {
+            if (propertyName == KeyFunctionRevoke || !TryCallHandler(TrapGet, new JsValue[] {_target, propertyName, this}, out var result))
+            {
+                AssertTargetNotRevoked(propertyName);
+                return _target.Get(in propertyName, receiver);
+            }
+
+            AssertTargetNotRevoked(propertyName);
+            var targetDesc = _target.GetOwnProperty(propertyName);
+            if (targetDesc != PropertyDescriptor.Undefined)
+            {
+                if (targetDesc.IsDataDescriptor() && !targetDesc.Configurable && !targetDesc.Writable && !ReferenceEquals(result, targetDesc._value))
+                {
+                   ExceptionHelper.ThrowTypeError(_engine);
+                }
+                if (targetDesc.IsAccessorDescriptor() && !targetDesc.Configurable && targetDesc.Get.IsUndefined() && !result.IsUndefined())
+                {
+                   ExceptionHelper.ThrowTypeError(_engine, $"'get' on proxy: property '{propertyName}' is a non-configurable accessor property on the proxy target and does not have a getter function, but the trap did not return 'undefined' (got '{result}')");
+                }
+            }
+
+            return result;
+        }
+
+        internal override List<JsValue> GetOwnPropertyKeys(Types types)
+        {
+            if (!TryCallHandler(TrapOwnKeys, new JsValue[] {_target }, out var result))
+            {
+                return _target.GetOwnPropertyKeys(types);
+            }
+
+            var trapResult = new List<JsValue>(_engine.Function.PrototypeObject.CreateListFromArrayLike(result, Types.String | Types.Symbol));
+
+            if (trapResult.Count != new HashSet<JsValue>(trapResult).Count)
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            var extensibleTarget = _target.Extensible;
+            var targetKeys = _target.GetOwnPropertyKeys(types);
+            var targetConfigurableKeys = new List<Key>();
+            var targetNonconfigurableKeys = new List<Key>();
+
+            foreach (var jsValue in targetKeys)
+            {
+                var key = jsValue.ToPropertyKey();
+                var desc = _target.GetOwnProperty(key);
+                if (desc != PropertyDescriptor.Undefined && !desc.Configurable)
+                {
+                    targetNonconfigurableKeys.Add(key);
+                }
+                else
+                {
+                    targetConfigurableKeys.Add(key);
+                }
+                
+            }
+
+            if (extensibleTarget && targetNonconfigurableKeys.Count == 0)
+            {
+                return trapResult;
+            }
+            
+            var uncheckedResultKeys = new HashSet<Key>();
+            foreach (var jsValue in trapResult)
+            {
+                uncheckedResultKeys.Add(jsValue.ToPropertyKey());
+            }
+
+            for (var i = 0; i < targetNonconfigurableKeys.Count; i++)
+            {
+                var key = targetNonconfigurableKeys[i];
+                if (!uncheckedResultKeys.Remove(key))
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
+            }
+
+            if (extensibleTarget)
+            {
+                return trapResult;
+            }
+
+            for (var i = 0; i < targetConfigurableKeys.Count; i++)
+            {
+                var key = targetConfigurableKeys[i];
+                if (!uncheckedResultKeys.Remove(key))
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
+            }
+
+            if (uncheckedResultKeys.Count > 0)
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            return trapResult;
+        }
+
+        public override PropertyDescriptor GetOwnProperty(in Key propertyName)
+        {
+            if (!TryCallHandler(TrapGetOwnPropertyDescriptor, new JsValue[] {_target, propertyName, this}, out var result))
+            {
+                return _target.GetOwnProperty(propertyName);
+            }
+
+            if (!result.IsObject() && !result.IsUndefined())
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            var targetDesc = _target.GetOwnProperty(propertyName);
+
+            if (result.IsUndefined())
+            {
+                if (targetDesc == PropertyDescriptor.Undefined)
+                {
+                    return targetDesc;
+                }
+
+                if (!targetDesc.Configurable || !_target.Extensible)
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
+
+                return PropertyDescriptor.Undefined;
+            }
+
+            var extensibleTarget = _target.Extensible;
+            var resultDesc = PropertyDescriptor.ToPropertyDescriptor(_engine, result);
+
+            var valid = IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc);
+            if (!valid)
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            if (!resultDesc.Configurable && (targetDesc == PropertyDescriptor.Undefined || targetDesc.Configurable))
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            return resultDesc;
+        }
+
+        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
+        {
+            if (!TryCallHandler(TrapSet, new[] { _target, propertyName, value, this }, out var trapResult))
+            {
+                return _target.Set(propertyName, value, receiver);
+            }
+
+            var result = TypeConverter.ToBoolean(trapResult);
+            if (!result)
+            {
+                return false;
+            }
+
+            var targetDesc  = _target.GetOwnProperty(propertyName);
+            if (targetDesc != PropertyDescriptor.Undefined)
+            {
+                if (targetDesc.IsDataDescriptor() && !targetDesc.Configurable && !targetDesc.Writable)
+                {
+                    if (targetDesc.Value != value)
+                    {
+                        ExceptionHelper.ThrowTypeError(_engine);
+                    }
+                }
+
+                if (targetDesc.IsAccessorDescriptor() && !targetDesc.Configurable)
+                {
+                    if (targetDesc.Set.IsUndefined())
+                    {
+                        ExceptionHelper.ThrowTypeError(_engine);
+                    }
+                }
+            }
+            
+            return true;
+        }
+
+        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
+        {
+            var arguments = new[] { _target, propertyName, PropertyDescriptor.FromPropertyDescriptor(_engine, desc) };
+            if (!TryCallHandler(TrapDefineProperty, arguments, out var result))
+            {
+                return _target.DefineOwnProperty(propertyName, desc);
+            }
+
+            var success = TypeConverter.ToBoolean(result);
+            if (!success)
+            {
+                return false;
+            }
+
+            var targetDesc = _target.GetOwnProperty(propertyName);
+            var extensibleTarget = _target.Extensible;
+            var settingConfigFalse = !desc.Configurable;
+
+            if (targetDesc == PropertyDescriptor.Undefined)
+            {
+                if (!extensibleTarget || settingConfigFalse)
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
+            }
+            else
+            {
+                if (!IsCompatiblePropertyDescriptor(extensibleTarget, desc, targetDesc))
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
+                if (targetDesc.Configurable && settingConfigFalse)
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
+            }
+
+            return true;
+        }
+
+        private static bool IsCompatiblePropertyDescriptor(bool extensible, PropertyDescriptor desc, PropertyDescriptor current)
+        {
+            return ValidateAndApplyPropertyDescriptor(null, "", extensible, desc, current);
+        }
+
+        public override bool HasProperty(in Key propertyName)
+        {
+            if (!TryCallHandler(TrapHas, new JsValue[] { _target, propertyName }, out var jsValue))
+            {
+                return _target.HasProperty(propertyName);
+            }
+
+            var trapResult = TypeConverter.ToBoolean(jsValue);
+
+            if (!trapResult)
+            {
+                var targetDesc = _target.GetOwnProperty(propertyName);
+                if (targetDesc != PropertyDescriptor.Undefined)
+                {
+                    if (!targetDesc.Configurable)
+                    {
+                        ExceptionHelper.ThrowTypeError(_engine);
+                    }
+
+                    if (!_target.Extensible)
+                    {
+                        ExceptionHelper.ThrowTypeError(_engine);
+                    }
+                }
+            }
+
+            return trapResult;
+        }
+
+        public override bool Delete(in Key propertyName)
+        {
+            if (!TryCallHandler(TrapDeleteProperty, new JsValue[] { _target, propertyName }, out var result))
+            {
+                return _target.Delete(propertyName);
+            }
+
+            var success = TypeConverter.ToBoolean(result);
+
+            if (success)
+            {
+                var targetDesc = _target.GetOwnProperty(propertyName);
+                if (targetDesc != PropertyDescriptor.Undefined && !targetDesc.Configurable)
+                {
+                    ExceptionHelper.ThrowTypeError(_engine, $"'deleteProperty' on proxy: trap returned truish for property '{propertyName}' which is non-configurable in the proxy target");
+                }
+            }
+
+            return success;
+        }
+
+        public override JsValue PreventExtensions()
+        {
+            if (!TryCallHandler(TrapPreventExtensions, new[] { _target }, out var result))
+            {
+                return _target.PreventExtensions();
+            }
+
+            var success = TypeConverter.ToBoolean(result);
+
+            if (success && _target.Extensible)
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            return success ? JsBoolean.True : JsBoolean.False;
+        }
+
+        public override bool Extensible
+        {
+            get
+            {
+                if (!TryCallHandler(TrapIsExtensible, new[] { _target }, out var result))
+                {
+                    return _target.Extensible;
+                }
+
+                var booleanTrapResult = TypeConverter.ToBoolean(result);
+                var targetResult = _target.Extensible;
+                if (booleanTrapResult != targetResult)
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
+                return booleanTrapResult;
+            }
+        }
+
+        protected override ObjectInstance GetPrototypeOf()
+        {
+            if (!TryCallHandler(TrapGetProtoTypeOf, new [] { _target }, out var handlerProto ))
+            {
+                return _target.Prototype;
+            }
+
+            if (!handlerProto.IsObject() && !handlerProto.IsNull())
+            {
+                ExceptionHelper.ThrowTypeError(_engine, "'getPrototypeOf' on proxy: trap returned neither object nor null");
+            }
+
+            if (_target.Extensible)
+            {
+                return (ObjectInstance) handlerProto;
+            }
+
+            if (!ReferenceEquals(handlerProto, _target.Prototype))
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            return (ObjectInstance) handlerProto;
+        }
+
+        public override bool SetPrototypeOf(JsValue value)
+        {
+            if (!TryCallHandler(TrapSetProtoTypeOf, new[] { _target, value }, out var result))
+            {
+                return _target.SetPrototypeOf(value);
+            }
+
+            var success = TypeConverter.ToBoolean(result);
+
+            if (!success)
+            {
+                return false;
+            }
+
+            if (_target.Extensible)
+            {
+                return true;
+            }
+
+            if (!ReferenceEquals(value, _target.Prototype))
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
+
+            return true;
+        }
+
+        private bool TryCallHandler(in Key propertyName, JsValue[] arguments, out JsValue result)
+        {
+            AssertNotRevoked(propertyName);
+            
+            result = Undefined;
+            if (_handler.TryGetValue(propertyName, out var handlerFunction)
+                && !handlerFunction.IsNullOrUndefined())
+            {
+                if (!(handlerFunction is ICallable callable))
+                {
+                    return ExceptionHelper.ThrowTypeError<bool>(_engine, $"{_handler} returned for property '{propertyName}' of object '{_target}' is not a function");
+                }
+
+                result = callable.Call(_handler, arguments);
+                return true;
+            }
+
+            return false;
+        }
+
+        internal void AssertNotRevoked(in Key key)
+        {
+            if (_handler is null)
+            {
+                ExceptionHelper.ThrowTypeError(_engine, $"Cannot perform '{key}' on a proxy that has been revoked");
+            }
+        }
+
+        internal void AssertTargetNotRevoked(in Key key)
+        {
+            if (_target is null)
+            {
+                ExceptionHelper.ThrowTypeError(_engine, $"Cannot perform '{key}' on a proxy that has been revoked");
+            }
+        }
+    }
+}

+ 37 - 0
Jint/Native/Proxy/ProxyPrototype.cs

@@ -0,0 +1,37 @@
+using Jint.Collections;
+using Jint.Native.Object;
+using Jint.Runtime.Descriptors;
+
+namespace Jint.Native.Proxy
+{
+    /// <summary>
+    /// https://www.ecma-international.org/ecma-262/6.0/index.html#sec-proxy-object-internal-methods-and-internal-slots
+    /// </summary>
+    public sealed class ProxyPrototype : ObjectInstance
+    {
+        private ProxyConstructor _proxyConstructor;
+
+        private ProxyPrototype(Engine engine) : base(engine)
+        {
+        }
+
+        public static ProxyPrototype CreatePrototypeObject(Engine engine, ProxyConstructor proxyConstructor)
+        {
+            var obj = new ProxyPrototype(engine)
+            {
+                _prototype = engine.Object.PrototypeObject,
+                _proxyConstructor = proxyConstructor
+            };
+
+            return obj;
+        }
+
+        protected override void Initialize()
+        {
+            _properties = new StringDictionarySlim<PropertyDescriptor>(3)
+            {
+                ["constructor"] = new PropertyDescriptor(_proxyConstructor, PropertyFlag.NonEnumerable)
+            };
+        }
+    }
+}

+ 203 - 0
Jint/Native/Reflect/ReflectInstance.cs

@@ -0,0 +1,203 @@
+using Jint.Collections;
+using Jint.Native.Object;
+using Jint.Runtime;
+using Jint.Runtime.Descriptors;
+using Jint.Runtime.Interop;
+
+namespace Jint.Native.Reflect
+{
+    /// <summary>
+    /// https://www.ecma-international.org/ecma-262/6.0/index.html#sec-reflect-object
+    /// </summary>
+    public sealed class ReflectInstance : ObjectInstance
+    {
+        private ReflectInstance(Engine engine) : base(engine, "Reflect")
+        {
+        }
+
+        public static ReflectInstance CreateReflectObject(Engine engine)
+        {
+            var math = new ReflectInstance(engine)
+            {
+                _prototype = engine.Object.PrototypeObject
+            };
+
+            return math;
+        }
+
+        protected override void Initialize()
+        {
+            _properties = new StringDictionarySlim<PropertyDescriptor>(14)
+            {
+                ["apply"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "apply", Apply, 3, PropertyFlag.Configurable), true, false, true),
+                ["construct"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "construct", Construct, 2, PropertyFlag.Configurable), true, false, true),
+                ["defineProperty"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "defineProperty", DefineProperty, 3, PropertyFlag.Configurable), true, false, true),
+                ["deleteProperty"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "deleteProperty", DeleteProperty, 2, PropertyFlag.Configurable), true, false, true),
+                ["get"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "get", Get, 2, PropertyFlag.Configurable), true, false, true),
+                ["getOwnPropertyDescriptor"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getOwnPropertyDescriptor", GetOwnPropertyDescriptor, 2, PropertyFlag.Configurable), true, false, true),
+                ["getPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "getPrototypeOf", GetPrototypeOf, 1, PropertyFlag.Configurable), true, false, true),
+                ["has"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "has", Has, 2, PropertyFlag.Configurable), true, false, true),
+                ["isExtensible"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "isExtensible", IsExtensible, 1, PropertyFlag.Configurable), true, false, true),
+                ["ownKeys"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "ownKeys", OwnKeys, 1, PropertyFlag.Configurable), true, false, true),
+                ["preventExtensions"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "preventExtensions", PreventExtensions, 1, PropertyFlag.Configurable), true, false, true),
+                ["set"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "set", Set, 3, PropertyFlag.Configurable), true, false, true),
+                ["setPrototypeOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "setPrototypeOf", SetPrototypeOf, 2, PropertyFlag.Configurable), true, false, true),
+            };
+        }
+
+        private JsValue Apply(JsValue thisObject, JsValue[] arguments)
+        {
+            return _engine.Function.PrototypeObject.Apply(arguments.At(0), new[]
+            {
+                arguments.At(1),
+                arguments.At(2)
+            });
+        }
+
+        private JsValue Construct(JsValue thisObject, JsValue[] arguments)
+        {
+            var targetArgument = arguments.At(0);
+            if (!(targetArgument is IConstructor target))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, targetArgument + " is not a constructor");
+            }
+
+            var newTargetArgument = arguments.At(2, arguments[0]);
+            if (!(newTargetArgument is IConstructor newTarget))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, newTargetArgument + " is not a constructor");
+            }
+
+            var args = _engine.Function.PrototypeObject.CreateListFromArrayLike(arguments.At(1));
+
+            return target.Construct(args, newTargetArgument);
+        }
+
+        private JsValue DefineProperty(JsValue thisObject, JsValue[] arguments)
+        {
+            var o = arguments.As<ObjectInstance>(0, _engine);
+            var p = arguments.At(1);
+            var name = TypeConverter.ToPropertyKey(p);
+
+            var attributes = arguments.At(2);
+            var desc = PropertyDescriptor.ToPropertyDescriptor(Engine, attributes);
+
+            return o.DefineOwnProperty(name, desc);
+        }
+
+        private JsValue DeleteProperty(JsValue thisObject, JsValue[] arguments)
+        {
+            var target = arguments.At(0);
+            if (!(target is ObjectInstance o))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Reflect.deleteProperty called on non-object");
+            }
+
+            var property = TypeConverter.ToPropertyKey(arguments.At(1));
+            return o.Delete(property) ? JsBoolean.True : JsBoolean.False;
+        }
+
+        private JsValue Has(JsValue thisObject, JsValue[] arguments)
+        {
+            var target = arguments.At(0);
+            if (!(target is ObjectInstance o))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Reflect.has called on non-object");
+            }
+
+            var property = TypeConverter.ToPropertyKey(arguments.At(1));
+            return o.HasProperty(property) ? JsBoolean.True : JsBoolean.False;
+        }
+
+        private JsValue Set(JsValue thisObject, JsValue[] arguments)
+        {
+            var target = arguments.At(0);
+            var property = TypeConverter.ToPropertyKey(arguments.At(1));
+            var value = arguments.At(2);
+            var receiver = arguments.At(3, target);
+
+            if (!(target is ObjectInstance o))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Reflect.set called on non-object");
+            }
+
+            return o.Set(property, value, receiver);
+        }
+
+        private JsValue Get(JsValue thisObject, JsValue[] arguments)
+        {
+            var target = arguments.At(0);
+            if (!(target is ObjectInstance o))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Reflect.get called on non-object");
+            }
+
+            var receiver = arguments.At(2, target);
+            var property = TypeConverter.ToPropertyKey(arguments.At(1));
+            return o.Get(property, receiver);
+        }
+
+        private JsValue GetOwnPropertyDescriptor(JsValue thisObject, JsValue[] arguments)
+        {
+            return _engine.Object.GetOwnPropertyDescriptor(Undefined, arguments);
+        }
+
+        private JsValue OwnKeys(JsValue thisObject, JsValue[] arguments)
+        {
+            var target = arguments.At(0);
+            if (!(target is ObjectInstance o))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Reflect.get called on non-object");
+            }
+
+            var keys = o.GetOwnPropertyKeys(Types.String | Types.Symbol);
+            return _engine.Array.CreateArrayFromList(keys);
+        }
+
+        private JsValue IsExtensible(JsValue thisObject, JsValue[] arguments)
+        {
+            return _engine.Object.IsExtensible(Undefined, arguments);
+        }
+
+        private JsValue PreventExtensions(JsValue thisObject, JsValue[] arguments)
+        {
+            var target = arguments.At(0);
+            if (!(target is ObjectInstance o))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Reflect.preventExtensions called on non-object");
+            }
+
+            return o.PreventExtensions();
+        }
+
+        private JsValue GetPrototypeOf(JsValue thisObject, JsValue[] arguments)
+        {
+            var target = arguments.At(0);
+
+            if (!target.IsObject())
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Reflect.getPrototypeOf called on non-object");
+            }
+
+            return _engine.Object.GetPrototypeOf(Undefined, arguments);
+        }
+
+        private JsValue SetPrototypeOf(JsValue thisObject, JsValue[] arguments)
+        {
+            var target = arguments.At(0);
+
+            if (!(target is ObjectInstance o))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Reflect.setPrototypeOf called on non-object");
+            }
+
+            var prototype = arguments.At(1);
+            if (!prototype.IsObject() && !prototype.IsNull())
+            {
+                ExceptionHelper.ThrowTypeError(_engine, $"Object prototype may only be an Object or null: {prototype}");
+            }
+
+            return o.SetPrototypeOf(prototype);
+        }
+    }
+}

+ 12 - 13
Jint/Native/RegExp/RegExpConstructor.cs

@@ -21,8 +21,7 @@ namespace Jint.Native.RegExp
         {
             var obj = new RegExpConstructor(engine)
             {
-                Extensible = true,
-                Prototype = engine.Function.PrototypeObject
+                _prototype = engine.Function.PrototypeObject
             };
 
             // The value of the [[Prototype]] internal property of the RegExp constructor is the Function prototype object
@@ -31,7 +30,7 @@ namespace Jint.Native.RegExp
             obj._length = new PropertyDescriptor(2, PropertyFlag.AllForbidden);
 
             // The initial value of RegExp.prototype is the RegExp prototype object
-            obj._prototype= new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor= new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -48,16 +47,19 @@ namespace Jint.Native.RegExp
                 return pattern;
             }
 
-            return Construct(arguments);
+            return Construct(arguments, thisObject);
+        }
+
+        public ObjectInstance Construct(JsValue[] arguments)
+        {
+            return Construct(arguments, this);
         }
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-7.8.5
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.10.4
         /// </summary>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             string p;
             string f;
@@ -88,8 +90,7 @@ namespace Jint.Native.RegExp
             f = !flags.IsUndefined() ? TypeConverter.ToString(flags) : "";
 
             r = new RegExpInstance(Engine);
-            r.Prototype = PrototypeObject;
-            r.Extensible = true;
+            r._prototype = PrototypeObject;
 
             try
             {
@@ -127,8 +128,7 @@ namespace Jint.Native.RegExp
         public RegExpInstance Construct(string regExp, Engine engine)
         {
             var r = new RegExpInstance(Engine);
-            r.Prototype = PrototypeObject;
-            r.Extensible = true;
+            r._prototype = PrototypeObject;
 
             var scanner = new Scanner(regExp, new ParserOptions { AdaptRegexp = true });
             var body = (string)scanner.ScanRegExpBody().Value;
@@ -153,8 +153,7 @@ namespace Jint.Native.RegExp
         public RegExpInstance Construct(Regex regExp, string flags, Engine engine)
         {
             var r = new RegExpInstance(Engine);
-            r.Prototype = PrototypeObject;
-            r.Extensible = true;
+            r._prototype = PrototypeObject;
 
             r.Flags = flags;
             AssignFlags(r, flags);

+ 6 - 7
Jint/Native/RegExp/RegExpPrototype.cs

@@ -21,8 +21,7 @@ namespace Jint.Native.RegExp
         {
             var obj = new RegExpPrototype(engine)
             {
-                Prototype = engine.Object.PrototypeObject,
-                Extensible = true,
+                _prototype = engine.Object.PrototypeObject,
                 _regExpConstructor = regExpConstructor
             };
 
@@ -81,7 +80,7 @@ namespace Jint.Native.RegExp
 
             var s = TypeConverter.ToString(arguments.At(0));
             var length = s.Length;
-            var lastIndex = TypeConverter.ToNumber(R.Get("lastIndex"));
+            var lastIndex = TypeConverter.ToNumber(R.Get("lastIndex", R));
             var i = TypeConverter.ToInteger(lastIndex);
             var global = R.Global;
 
@@ -94,14 +93,14 @@ namespace Jint.Native.RegExp
             {
                 // "aaa".match() => [ '', index: 0, input: 'aaa' ]
                 var aa = InitReturnValueArray((ArrayInstance) Engine.Array.Construct(Arguments.Empty), s, 1, 0);
-                aa.DefineOwnProperty("0", new PropertyDescriptor("", PropertyFlag.ConfigurableEnumerableWritable), true);
+                aa.DefinePropertyOrThrow("0", new PropertyDescriptor("", PropertyFlag.ConfigurableEnumerableWritable));
                 return aa;
             }
 
             Match r = null;
             if (i < 0 || i > length)
             {
-                R.Put("lastIndex", (double) 0, true);
+                R.Set("lastIndex", (double) 0, true);
                 return Null;
             }
 
@@ -109,7 +108,7 @@ namespace Jint.Native.RegExp
 
             if (!r.Success)
             {
-                R.Put("lastIndex", (double) 0, true);
+                R.Set("lastIndex", (double) 0, true);
                 return Null;
             }
 
@@ -117,7 +116,7 @@ namespace Jint.Native.RegExp
 
             if (global)
             {
-                R.Put("lastIndex", (double) e, true);
+                R.Set("lastIndex", (double) e, true);
             }
             var n = r.Groups.Count;
             var matchIndex = r.Index;

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

@@ -25,8 +25,7 @@ namespace Jint.Native.Set
         {
             var obj = new SetConstructor(engine)
             {
-                Extensible = true,
-                Prototype = engine.Function.PrototypeObject
+                _prototype = engine.Function.PrototypeObject
             };
 
             // The value of the [[Prototype]] internal property of the Set constructor is the Function prototype object
@@ -35,7 +34,7 @@ namespace Jint.Native.Set
             obj._length = new PropertyDescriptor(0, PropertyFlag.Configurable);
 
             // The initial value of Set.prototype is the Set prototype object
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -44,7 +43,7 @@ namespace Jint.Native.Set
         {
             _properties = new StringDictionarySlim<PropertyDescriptor>(2)
             {
-                [GlobalSymbolRegistry.Species._value] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(_engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable)
+                [GlobalSymbolRegistry.Species] = new GetSetPropertyDescriptor(get: new ClrFunctionInstance(_engine, "get [Symbol.species]", Species, 0, PropertyFlag.Configurable), set: Undefined, PropertyFlag.Configurable)
             };
         }
 
@@ -60,15 +59,14 @@ namespace Jint.Native.Set
                 ExceptionHelper.ThrowTypeError(_engine, "Constructor Set requires 'new'");
             }
 
-            return Construct(arguments);
+            return Construct(arguments, thisObject);
         }
 
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             var instance = new SetInstance(Engine)
             {
-                Prototype = PrototypeObject,
-                Extensible = true
+                _prototype = PrototypeObject
             };
             if (arguments.Length > 0 && !arguments[0].IsNullOrUndefined())
             {

+ 0 - 39
Jint/Native/Set/SetInstance.cs

@@ -14,45 +14,6 @@ namespace Jint.Native.Set
             _set = new OrderedSet<JsValue>();
         }
 
-        /// Implementation from ObjectInstance official specs as the one
-        /// in ObjectInstance is optimized for the general case and wouldn't work
-        /// for arrays
-        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
-        {
-            if (!CanPut(propertyName))
-            {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(Engine);
-                }
-
-                return;
-            }
-
-            var ownDesc = GetOwnProperty(propertyName);
-
-            if (ownDesc.IsDataDescriptor())
-            {
-                var valueDesc = new PropertyDescriptor(value, PropertyFlag.None);
-                DefineOwnProperty(propertyName, valueDesc, throwOnError);
-                return;
-            }
-
-            // property is an accessor or inherited
-            var desc = GetProperty(propertyName);
-
-            if (desc.IsAccessorDescriptor())
-            {
-                var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(this, new[] {value});
-            }
-            else
-            {
-                var newDesc = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
-                DefineOwnProperty(propertyName, newDesc, throwOnError);
-            }
-        }
-
         public override PropertyDescriptor GetOwnProperty(in Key propertyName)
         {
             if (propertyName == KnownKeys.Size)

+ 3 - 4
Jint/Native/Set/SetPrototype.cs

@@ -23,8 +23,7 @@ namespace Jint.Native.Set
         {
             var obj = new SetPrototype(engine)
             {
-                Extensible = true, 
-                Prototype = engine.Object.PrototypeObject,
+                _prototype = engine.Object.PrototypeObject,
                 _mapConstructor = mapConstructor
             };
 
@@ -37,8 +36,8 @@ namespace Jint.Native.Set
             {
                 ["length"] = new PropertyDescriptor(0, PropertyFlag.Configurable),
                 ["constructor"] = new PropertyDescriptor(_mapConstructor, PropertyFlag.NonEnumerable),
-                [GlobalSymbolRegistry.Iterator._value] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Values, 1, PropertyFlag.Configurable), true, false, true),
-                [GlobalSymbolRegistry.ToStringTag._value] = new PropertyDescriptor("Set", false, false, true),
+                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "iterator", Values, 1, PropertyFlag.Configurable), true, false, true),
+                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor("Set", false, false, true),
                 ["add"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "add", Add, 1, PropertyFlag.Configurable), true, false, true),
                 ["clear"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "clear", Clear, 0, PropertyFlag.Configurable), true, false, true),
                 ["delete"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "delete", Delete, 1, PropertyFlag.Configurable), true, false, true),

+ 6 - 10
Jint/Native/String/StringConstructor.cs

@@ -23,8 +23,7 @@ namespace Jint.Native.String
         {
             var obj = new StringConstructor(engine)
             {
-                Extensible = true,
-                Prototype = engine.Function.PrototypeObject
+                _prototype = engine.Function.PrototypeObject
             };
 
             // The value of the [[Prototype]] internal property of the String constructor is the Function prototype object
@@ -33,7 +32,7 @@ namespace Jint.Native.String
             obj._length = PropertyDescriptor.AllForbiddenDescriptor.NumberOne;
 
             // The initial value of String.prototype is the String prototype object
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -105,9 +104,9 @@ namespace Jint.Native.String
         private JsValue Raw(JsValue thisObj, JsValue[] arguments)
         {
             var cooked = TypeConverter.ToObject(_engine, arguments.At(0));
-            var raw = TypeConverter.ToObject(_engine, cooked.Get("raw"));
+            var raw = TypeConverter.ToObject(_engine, cooked.Get("raw", cooked));
 
-            var operations = ArrayPrototype.ArrayOperations.For(raw);
+            var operations = ArrayOperations.For(raw);
             var length = operations.GetLength();
 
             if (length <= 0)
@@ -152,9 +151,7 @@ namespace Jint.Native.String
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.7.2.1
         /// </summary>
-        /// <param name="arguments"></param>
-        /// <returns></returns>
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             string value = "";
             if (arguments.Length > 0)
@@ -175,9 +172,8 @@ namespace Jint.Native.String
         {
             var instance = new StringInstance(Engine)
             {
-                Prototype = PrototypeObject,
+                _prototype = PrototypeObject,
                 PrimitiveValue = value,
-                Extensible = true,
                 _length = PropertyDescriptor.AllForbiddenDescriptor.ForNumber(value.Length)
             };
 

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

@@ -71,11 +71,11 @@ namespace Jint.Native.String
             return new PropertyDescriptor(resultStr, PropertyFlag.OnlyEnumerable);
         }
 
-        public override IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
+        public override IEnumerable<KeyValuePair<Key, PropertyDescriptor>> GetOwnProperties()
         {
             if (_length != null)
             {
-                yield return new KeyValuePair<string, PropertyDescriptor>(KnownKeys.Length, _length);
+                yield return new KeyValuePair<Key, PropertyDescriptor>(KnownKeys.Length, _length);
             }
 
             foreach (var entry in base.GetOwnProperties())

+ 7 - 8
Jint/Native/String/StringPrototype.cs

@@ -31,9 +31,8 @@ namespace Jint.Native.String
         {
             var obj = new StringPrototype(engine)
             {
-                Prototype = engine.Object.PrototypeObject,
+                _prototype = engine.Object.PrototypeObject,
                 PrimitiveValue = JsString.Empty,
-                Extensible = true,
                 _length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero,
                 _stringConstructor = stringConstructor,
             };
@@ -76,7 +75,7 @@ namespace Jint.Native.String
                 ["includes"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "includes", Includes, 1, PropertyFlag.Configurable), true, false, true),
                 ["normalize"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "normalize", Normalize, 0, PropertyFlag.Configurable), true, false, true),
                 ["repeat"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "repeat", Repeat, 1, PropertyFlag.Configurable), true, false, true),
-                [GlobalSymbolRegistry.Iterator._value] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.iterator]", Iterator, 0, PropertyFlag.Configurable), true, false, true)
+                [GlobalSymbolRegistry.Iterator] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "[Symbol.iterator]", Iterator, 0, PropertyFlag.Configurable), true, false, true)
             };
         }
 
@@ -679,14 +678,14 @@ namespace Jint.Native.String
 
             rx = rx ?? (RegExpInstance) Engine.RegExp.Construct(new[] {regex});
 
-            var global = ((JsBoolean) rx.Get("global"))._value;
+            var global = ((JsBoolean) rx.Get("global", this))._value;
             if (!global)
             {
                 return Engine.RegExp.PrototypeObject.Exec(rx, Arguments.From(s));
             }
             else
             {
-                rx.Put("lastIndex", 0, false);
+                rx.Set("lastIndex", 0, rx);
                 var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
                 int previousLastIndex = 0;
                 uint n = 0;
@@ -700,14 +699,14 @@ namespace Jint.Native.String
                     }
                     else
                     {
-                        var thisIndex = (int) ((JsNumber) rx.Get("lastIndex"))._value;
+                        var thisIndex = (int) ((JsNumber) rx.Get("lastIndex", this))._value;
                         if (thisIndex == previousLastIndex)
                         {
-                            rx.Put("lastIndex", thisIndex + 1, false);
+                            rx.Set("lastIndex", thisIndex + 1, rx);
                             previousLastIndex = thisIndex + 1;
                         }
 
-                        var matchStr = result.Get("0");
+                        var matchStr = result.Get("0", this);
                         a.SetIndexValue(n, matchStr, updateLength: false);
                         n++;
                     }

+ 70 - 14
Jint/Native/Symbol/GlobalSymbolRegistry.cs

@@ -1,19 +1,75 @@
-using System.Collections.Generic;
+using System.Threading;
+using Jint.Collections;
 
 namespace Jint.Native.Symbol
 {
-    public class GlobalSymbolRegistry : Dictionary<string, JsSymbol>
+    public class GlobalSymbolRegistry
     {
-        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");
-        public static readonly JsSymbol Match = new JsSymbol("Symbol.match");
-        public static readonly JsSymbol Replace = new JsSymbol("Symbol.replace");
-        public static readonly JsSymbol Search = new JsSymbol("Symbol.search");
-        public static readonly JsSymbol Species = new JsSymbol("Symbol.species");
-        public static readonly JsSymbol Split = new JsSymbol("Symbol.split");
-        public static readonly JsSymbol ToPrimitive = new JsSymbol("Symbol.toPrimitive");
-        public static readonly JsSymbol ToStringTag = new JsSymbol("Symbol.toStringTag");
-        public static readonly JsSymbol Unscopables = new JsSymbol("Symbol.unscopables");
+        public static readonly JsSymbol HasInstance = new JsSymbol("Symbol.hasInstance", 1);
+        public static readonly JsSymbol IsConcatSpreadable = new JsSymbol("Symbol.isConcatSpreadable", 2);
+        public static readonly JsSymbol Iterator = new JsSymbol("Symbol.iterator", 3);
+        public static readonly JsSymbol Match = new JsSymbol("Symbol.match", 4);
+        public static readonly JsSymbol Replace = new JsSymbol("Symbol.replace", 5);
+        public static readonly JsSymbol Search = new JsSymbol("Symbol.search", 6);
+        public static readonly JsSymbol Species = new JsSymbol("Symbol.species", 7);
+        public static readonly JsSymbol Split = new JsSymbol("Symbol.split", 8);
+        public static readonly JsSymbol ToPrimitive = new JsSymbol("Symbol.toPrimitive", 9);
+        public static readonly JsSymbol ToStringTag = new JsSymbol("Symbol.toStringTag", 10);
+        public static readonly JsSymbol Unscopables = new JsSymbol("Symbol.unscopables", 11);
+
+        // well-known globals
+        private static readonly StringDictionarySlim<JsSymbol> _globalLookup;
+
+        // engine-specific created by scripts
+        private StringDictionarySlim<JsSymbol> _customSymbolLookup;
+
+        // custom symbol identity is based on running counter 
+        private int _symbolCounter = 100;
+
+        static GlobalSymbolRegistry()
+        {
+            _globalLookup = new StringDictionarySlim<JsSymbol>
+            {
+                [HasInstance._value] = HasInstance,
+                [IsConcatSpreadable._value] = IsConcatSpreadable,
+                [Iterator._value] = Iterator,
+                [Match._value] = Match,
+                [Replace._value] = Replace,
+                [Search._value] = Search,
+                [Species._value] = Species,
+                [Split._value] = Split,
+                [ToPrimitive._value] = ToPrimitive,
+                [ToStringTag._value] = ToStringTag,
+                [Unscopables._value] = Unscopables
+            };
+        }
+
+        internal bool TryGetSymbol(string key, out JsSymbol symbol)
+        {
+            if (_globalLookup.TryGetValue(key, out symbol))
+            {
+                return true;
+            }
+
+            return _customSymbolLookup != null && _customSymbolLookup.TryGetValue(key, out symbol);
+        }
+
+        internal void Add(JsSymbol symbol)
+        {
+            _customSymbolLookup ??= new StringDictionarySlim<JsSymbol>();
+            _customSymbolLookup[symbol._value] = symbol;
+        }
+
+        internal JsSymbol CreateSymbol(JsValue description)
+        {
+            var identity = Interlocked.Increment(ref _symbolCounter);
+            return new JsSymbol(description, identity);
+        }
+
+        internal JsSymbol CreateSymbol(string description)
+        {
+            var identity = Interlocked.Increment(ref _symbolCounter);
+            return new JsSymbol(description, identity);
+        }
     }
-}
+}

+ 12 - 16
Jint/Native/Symbol/SymbolConstructor.cs

@@ -24,8 +24,7 @@ namespace Jint.Native.Symbol
         {
             var obj = new SymbolConstructor(engine)
             {
-                Extensible = true,
-                Prototype = engine.Function.PrototypeObject
+                _prototype = engine.Function.PrototypeObject
             };
 
             // The value of the [[Prototype]] internal property of the Symbol constructor is the Function prototype object
@@ -34,7 +33,7 @@ namespace Jint.Native.Symbol
             obj._length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero;
 
             // The initial value of String.prototype is the String prototype object
-            obj._prototype = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(obj.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -75,7 +74,7 @@ namespace Jint.Native.Symbol
                 return descString;
             }
 
-            var value = new JsSymbol(description);
+            var value = _engine.GlobalSymbolRegistry.CreateSymbol(description);
             return value;
         }
 
@@ -85,10 +84,10 @@ namespace Jint.Native.Symbol
 
             // 2. ReturnIfAbrupt(stringKey).
 
-            if (!Engine.GlobalSymbolRegistry.TryGetValue(stringKey, out var symbol))
+            if (!_engine.GlobalSymbolRegistry.TryGetSymbol(stringKey, out var symbol))
             {
-                symbol = new JsSymbol(stringKey);
-                Engine.GlobalSymbolRegistry.Add(stringKey, symbol);
+                symbol = _engine.GlobalSymbolRegistry.CreateSymbol(stringKey);
+                _engine.GlobalSymbolRegistry.Add(symbol);
             }
 
             return symbol;
@@ -103,8 +102,7 @@ namespace Jint.Native.Symbol
                 ExceptionHelper.ThrowTypeError(Engine);
             }
 
-            JsSymbol symbol;
-            if (!Engine.GlobalSymbolRegistry.TryGetValue(sym.AsSymbol(), out symbol))
+            if (!_engine.GlobalSymbolRegistry.TryGetSymbol(sym.AsSymbol(), out _))
             {
                 return Undefined;
             }
@@ -112,24 +110,22 @@ namespace Jint.Native.Symbol
             return sym.AsSymbol();
         }
 
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
-            ExceptionHelper.ThrowTypeError(Engine);
-            return null;
+            return ExceptionHelper.ThrowTypeError<ObjectInstance>(Engine);
         }
 
         public SymbolInstance Construct(string description)
         {
-            return Construct(new JsSymbol(description));
+            return Construct(_engine.GlobalSymbolRegistry.CreateSymbol(description));
         }
 
         public SymbolInstance Construct(JsSymbol symbol)
         {
             var instance = new SymbolInstance(Engine)
             {
-                Prototype = PrototypeObject,
-                SymbolData = symbol,
-                Extensible = true
+                _prototype = PrototypeObject,
+                SymbolData = symbol
             };
 
             return instance;

+ 4 - 5
Jint/Native/Symbol/SymbolPrototype.cs

@@ -22,8 +22,7 @@ namespace Jint.Native.Symbol
         {
             var obj = new SymbolPrototype(engine)
             {
-                Prototype = engine.Object.PrototypeObject,
-                Extensible = true,
+                _prototype = engine.Object.PrototypeObject,
                 _symbolConstructor = symbolConstructor
             };
 
@@ -39,12 +38,12 @@ namespace Jint.Native.Symbol
                 ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToSymbolString), true, false, true),
                 ["valueOf"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "valueOf", ValueOf), true, false, true),
                 ["toStringTag"] = new PropertyDescriptor(new JsString("Symbol"), false, false, true),
-                [GlobalSymbolRegistry.ToPrimitive._value] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toPrimitive", ToPrimitive), false, false, true),
-                [GlobalSymbolRegistry.ToStringTag._value] = new PropertyDescriptor(new JsString("Symbol"), false, false, true)
+                [GlobalSymbolRegistry.ToPrimitive] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toPrimitive", ToPrimitive), false, false, true),
+                [GlobalSymbolRegistry.ToStringTag] = new PropertyDescriptor(new JsString("Symbol"), false, false, true)
             };
         }
 
-        public string SymbolDescriptiveString(JsSymbol sym)
+        private string SymbolDescriptiveString(JsSymbol sym)
         {
             return $"Symbol({sym.AsSymbol()})";
         }

+ 1 - 2
Jint/Pooling/ArgumentsInstancePool.cs

@@ -25,8 +25,7 @@ namespace Jint.Pooling
         {
             return new ArgumentsInstance(_engine)
             {
-                Prototype = _engine.Object.PrototypeObject,
-                Extensible = true
+                _prototype = _engine.Object.PrototypeObject
             };
         }
 

+ 12 - 0
Jint/Runtime/Arguments.cs

@@ -32,6 +32,18 @@ namespace Jint.Runtime
             return At(args, index, Undefined.Instance);
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static T As<T>(this JsValue[] args, int index, Engine engine) where T : JsValue
+        {
+            var value = (uint) index < (uint) args.Length ? args[index] as T : null;
+            if (value is null)
+            {
+                ExceptionHelper.ThrowTypeError<JsValue>(engine);
+            }
+
+            return value;
+        }
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static JsValue[] Skip(this JsValue[] args, int count)
         {

+ 3 - 5
Jint/Runtime/Debugger/DebugHandler.cs

@@ -40,18 +40,16 @@ namespace Jint.Runtime.Debugger
 
         internal void AddToDebugCallStack(CallExpression callExpression)
         {
-            var identifier = callExpression.Callee as Esprima.Ast.Identifier;
-            if (identifier != null)
+            if (callExpression.Callee is Identifier identifier)
             {
                 var stack = identifier.Name + "(";
-                var paramStrings = new System.Collections.Generic.List<string>();
+                var paramStrings = new List<string>();
 
                 foreach (var argument in callExpression.Arguments)
                 {
                     if (argument != null)
                     {
-                        var argIdentifier = argument as Esprima.Ast.Identifier;
-                        paramStrings.Add(argIdentifier != null ? argIdentifier.Name : "null");
+                        paramStrings.Add(argument is Identifier argIdentifier ? argIdentifier.Name : "null");
                     }
                     else
                     {

+ 11 - 11
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -238,7 +238,7 @@ namespace Jint.Runtime.Environments
         }
 
         /// <inheritdoc />
-        public override string[] GetAllBindingNames()
+        public override Key[] GetAllBindingNames()
         {
             int size = _set ? 1 : 0;
             if (!ReferenceEquals(_argumentsBinding.Value, null))
@@ -251,7 +251,7 @@ namespace Jint.Runtime.Environments
                 size += _dictionary.Count;
             }
 
-            var keys = size > 0 ? new string[size] : ArrayExt.Empty<string>();
+            var keys = size > 0 ? new Key[size] : ArrayExt.Empty<Key>();
             int n = 0;
             if (_set)
             {
@@ -307,7 +307,7 @@ namespace Jint.Runtime.Environments
         {
             var argument = arguments.Length > index ? arguments[index] : Undefined;
 
-            if (parameter is Esprima.Ast.Identifier identifier)
+            if (parameter is Identifier identifier)
             {
                 SetItemSafely(identifier.Name, argument, initiallyEmpty);
             }
@@ -327,7 +327,7 @@ namespace Jint.Runtime.Environments
 
                 argument = rest;
 
-                if (restElement.Argument is Esprima.Ast.Identifier restIdentifier)
+                if (restElement.Argument is Identifier restIdentifier)
                 {
                     SetItemSafely(restIdentifier.Name, argument, initiallyEmpty);
                 }
@@ -392,18 +392,18 @@ namespace Jint.Runtime.Environments
                 var jsValues = _engine._jsValueArrayPool.RentArray(1);
                 foreach (var property in objectPattern.Properties)
                 {
-                    if (property.Key is Esprima.Ast.Identifier propertyIdentifier)
+                    if (property.Key is Identifier propertyIdentifier)
                     {
-                        argument = argumentObject.Get(propertyIdentifier.Name);
+                        argument = argumentObject.Get(propertyIdentifier.Name, this);
                     }
                     else if (property.Key is Literal propertyLiteral)
                     {
-                        argument = argumentObject.Get(propertyLiteral.Raw);
+                        argument = argumentObject.Get(propertyLiteral.Raw, this);
                     }
                     else if (property.Key is CallExpression callExpression)
                     {
                         var jintCallExpression = JintExpression.Build(_engine, callExpression);
-                        argument = argumentObject.Get(jintCallExpression.GetValue().AsString());
+                        argument = argumentObject.Get(jintCallExpression.GetValue().AsString(), this);
                     }
 
                     jsValues[0] = argument;
@@ -413,9 +413,9 @@ namespace Jint.Runtime.Environments
             }
             else if (parameter is AssignmentPattern assignmentPattern)
             {
-                var idLeft = assignmentPattern.Left as Esprima.Ast.Identifier;
+                var idLeft = assignmentPattern.Left as Identifier;
                 if (idLeft != null
-                    && assignmentPattern.Right is Esprima.Ast.Identifier idRight
+                    && assignmentPattern.Right is Identifier idRight
                     && idLeft.Name == idRight.Name)
                 {
                     ExceptionHelper.ThrowReferenceError(_engine, idRight.Name);
@@ -490,7 +490,7 @@ namespace Jint.Runtime.Environments
                 for (var j = 0; j < declarationsCount; j++)
                 {
                     var d = variableDeclaration.Declarations[j];
-                    if (d.Id is Esprima.Ast.Identifier id)
+                    if (d.Id is Identifier id)
                     {
                         Key dn = id.Name;
                         if (!ContainsKey(dn))

+ 1 - 1
Jint/Runtime/Environments/EnvironmentRecord.cs

@@ -69,7 +69,7 @@ namespace Jint.Runtime.Environments
         /// Returns an array of all the defined binding names
         /// </summary>
         /// <returns>The array of all defined bindings</returns>
-        public abstract string[] GetAllBindingNames();
+        public abstract Key[] GetAllBindingNames();
         
         public override object ToObject()
         {

+ 10 - 7
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -35,13 +35,13 @@ namespace Jint.Runtime.Environments
             // we unwrap by name
             binding = default;
 
-            var desc = _bindingObject.GetProperty(name);
-            if (desc == PropertyDescriptor.Undefined)
+            if (!_bindingObject.HasProperty(name))
             {
                 value = default;
                 return false;
             }
 
+            var desc = _bindingObject.GetProperty(name);
             value = ObjectInstance.UnwrapJsValue(desc, this);
             return true;
         }
@@ -55,12 +55,15 @@ namespace Jint.Runtime.Environments
                 ? new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable)
                 : new PropertyDescriptor(value, PropertyFlag.NonConfigurable);
 
-            _bindingObject.SetOwnProperty(name, propertyDescriptor);
+            _bindingObject.DefinePropertyOrThrow(name, propertyDescriptor);
         }
 
         public override void SetMutableBinding(in Key name, JsValue value, bool strict)
         {
-            _bindingObject.Put(name, value, strict);
+            if (!_bindingObject.Set(name, value) && strict)
+            {
+                ExceptionHelper.ThrowTypeError(_engine);
+            }
         }
 
         public override JsValue GetBindingValue(in Key name, bool strict)
@@ -76,7 +79,7 @@ namespace Jint.Runtime.Environments
 
         public override bool DeleteBinding(in Key name)
         {
-            return _bindingObject.Delete(name, false);
+            return _bindingObject.Delete(name);
         }
 
         public override JsValue ImplicitThisValue()
@@ -89,14 +92,14 @@ namespace Jint.Runtime.Environments
             return Undefined;
         }
 
-        public override string[] GetAllBindingNames()
+        public override Key[] GetAllBindingNames()
         {
             if (!ReferenceEquals(_bindingObject, null))
             {
                 return _bindingObject.GetOwnProperties().Select( x=> x.Key).ToArray();
             }
 
-            return ArrayExt.Empty<string>();
+            return ArrayExt.Empty<Key>();
         }
 
         public override bool Equals(JsValue other)

+ 2 - 3
Jint/Runtime/Interop/ClrFunctionInstance.cs

@@ -18,12 +18,11 @@ namespace Jint.Runtime.Interop
             Func<JsValue, JsValue[], JsValue> func,
             int length = 0,
             PropertyFlag lengthFlags = PropertyFlag.AllForbidden)
-            : base(engine, new JsString(name), strict: false)
+            : base(engine, !string.IsNullOrWhiteSpace(name) ? new JsString(name) : null, strict: false)
         {
             _func = func;
 
-            Prototype = engine.Function.PrototypeObject;
-            Extensible = true;
+            _prototype = engine.Function.PrototypeObject;
 
             _length = lengthFlags == PropertyFlag.AllForbidden
                 ? PropertyDescriptor.AllForbiddenDescriptor.ForNumber(length)

+ 12 - 3
Jint/Runtime/Interop/DefaultTypeConverter.cs

@@ -13,11 +13,16 @@ namespace Jint.Runtime.Interop
     public class DefaultTypeConverter : ITypeConverter
     {
         private readonly Engine _engine;
+
+#if NETSTANDARD
+        private static readonly ConcurrentDictionary<(Type Source, Type Target), bool> _knownConversions = new ConcurrentDictionary<(Type Source, Type Target), bool>();
+#else
         private static readonly ConcurrentDictionary<string, bool> _knownConversions = new ConcurrentDictionary<string, bool>();
+#endif
 
-        private static readonly MethodInfo convertChangeType = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(Type), typeof(IFormatProvider) });
-        private static readonly MethodInfo jsValueFromObject = typeof(JsValue).GetMethod("FromObject");
-        private static readonly MethodInfo jsValueToObject = typeof(JsValue).GetMethod("ToObject");
+        private static readonly MethodInfo convertChangeType = typeof(Convert).GetMethod("ChangeType", new [] { typeof(object), typeof(Type), typeof(IFormatProvider) });
+        private static readonly MethodInfo jsValueFromObject = typeof(JsValue).GetMethod(nameof(JsValue.FromObject));
+        private static readonly MethodInfo jsValueToObject = typeof(JsValue).GetMethod(nameof(JsValue.ToObject));
 
         public DefaultTypeConverter(Engine engine)
         {
@@ -262,7 +267,11 @@ namespace Jint.Runtime.Interop
 
         public virtual bool TryConvert(object value, Type type, IFormatProvider formatProvider, out object converted)
         {
+#if NETSTANDARD
+            var key = value == null ? (null, type) : (value.GetType(), type);
+#else
             var key = value == null ? $"Null->{type}" : $"{value.GetType()}->{type}";
+#endif
 
             var canConvert = _knownConversions.GetOrAdd(key, _ =>
             {

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

@@ -19,7 +19,7 @@ namespace Jint.Runtime.Interop
             : base(engine, "delegate", null, null, false)
         {
             _d = d;
-            Prototype = engine.Function.PrototypeObject;
+            _prototype = engine.Function.PrototypeObject;
 
             var parameterInfos = _d.Method.GetParameters();
 
@@ -116,7 +116,7 @@ namespace Jint.Runtime.Interop
             }
             try
             {
-                return JsValue.FromObject(Engine, _d.DynamicInvoke(parameters));
+                return FromObject(Engine, _d.DynamicInvoke(parameters));
             }
             catch (TargetInvocationException exception)
             {

+ 3 - 3
Jint/Runtime/Interop/MethodInfoFunctionInstance.cs

@@ -14,7 +14,7 @@ namespace Jint.Runtime.Interop
             : base(engine, "Function", null, null, false)
         {
             _methods = methods;
-            Prototype = engine.Function.PrototypeObject;
+            _prototype = engine.Function.PrototypeObject;
         }
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
@@ -53,11 +53,11 @@ namespace Jint.Runtime.Interop
                         // Handle specific case of F(params JsValue[])
 
                         var arrayInstance = arguments[i].AsArray();
-                        var len = TypeConverter.ToInt32(arrayInstance.Get("length"));
+                        var len = TypeConverter.ToInt32(arrayInstance.Get("length", this));
                         var result = new JsValue[len];
                         for (uint k = 0; k < len; k++)
                         {
-                            result[k] = arrayInstance.TryGetValue(k, out var value) ? value : JsValue.Undefined;
+                            result[k] = arrayInstance.TryGetValue(k, out var value) ? value : Undefined;
                         }
                         parameters[i] = result;
                     }

+ 3 - 13
Jint/Runtime/Interop/NamespaceReference.cs

@@ -23,23 +23,13 @@ namespace Jint.Runtime.Interop
             _path = path;
         }
 
-        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
         {
-            if (throwOnError)
-            {
-                ExceptionHelper.ThrowTypeError(_engine, "Can't define a property of a NamespaceReference");
-            }
-
             return false;
         }
 
-        public override bool Delete(in Key propertyName, bool throwOnError)
+        public override bool Delete(in Key propertyName)
         {
-            if (throwOnError)
-            {
-                ExceptionHelper.ThrowTypeError(_engine, "Can't delete a property of a NamespaceReference");
-            }
-
             return false;
         }
 
@@ -80,7 +70,7 @@ namespace Jint.Runtime.Interop
             }
         }
 
-        public override JsValue Get(in Key propertyName)
+        public override JsValue Get(in Key propertyName, JsValue receiver)
         {
             var newPath = _path + "." + propertyName;
 

+ 7 - 16
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -23,7 +23,7 @@ namespace Jint.Runtime.Interop
                 IsArrayLike = true;
                 // create a forwarder to produce length from Count
                 var functionInstance = new ClrFunctionInstance(engine, "length", (thisObj, arguments) => collection.Count);
-                var descriptor = new GetSetPropertyDescriptor(functionInstance, Undefined, PropertyFlag.AllForbidden);
+                var descriptor = new GetSetPropertyDescriptor(functionInstance, Undefined, PropertyFlag.Configurable);
                 AddProperty("length", descriptor);
             }
         }
@@ -32,31 +32,22 @@ namespace Jint.Runtime.Interop
 
         internal override bool IsArrayLike { get; }
 
-        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
+        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
         {
             if (!CanPut(propertyName))
             {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(Engine);
-                }
-
-                return;
+                return false;
             }
 
             var ownDesc = GetOwnProperty(propertyName);
 
             if (ownDesc == null)
             {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(_engine, "Unknown member: " + propertyName);
-                }
-            }
-            else
-            {
-                ownDesc.Value = value;
+                return false;
             }
+
+            ownDesc.Value = value;
+            return true;
         }
 
         public override PropertyDescriptor GetOwnProperty(in Key propertyName)

+ 12 - 33
Jint/Runtime/Interop/TypeReference.cs

@@ -22,15 +22,15 @@ namespace Jint.Runtime.Interop
         public static TypeReference CreateTypeReference(Engine engine, Type type)
         {
             var obj = new TypeReference(engine);
-            obj.Extensible = false;
+            obj.PreventExtensions();
             obj.ReferenceType = type;
 
             // The value of the [[Prototype]] internal property of the TypeReference constructor is the Function prototype object
-            obj.Prototype = engine.Function.PrototypeObject;
+            obj._prototype = engine.Function.PrototypeObject;
             obj._length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero;
 
             // The initial value of Boolean.prototype is the Boolean prototype object
-            obj._prototype = new PropertyDescriptor(engine.Object.PrototypeObject, PropertyFlag.AllForbidden);
+            obj._prototypeDescriptor = new PropertyDescriptor(engine.Object.PrototypeObject, PropertyFlag.AllForbidden);
 
             return obj;
         }
@@ -38,15 +38,15 @@ namespace Jint.Runtime.Interop
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
             // direct calls on a TypeReference constructor object is equivalent to the new operator
-            return Construct(arguments);
+            return Construct(arguments, thisObject);
         }
 
-        public ObjectInstance Construct(JsValue[] arguments)
+        public ObjectInstance Construct(JsValue[] arguments, JsValue newTarget)
         {
             if (arguments.Length == 0 && ReferenceType.IsValueType)
             {
                 var instance = Activator.CreateInstance(ReferenceType);
-                var result = TypeConverter.ToObject(Engine, JsValue.FromObject(Engine, instance));
+                var result = TypeConverter.ToObject(Engine, FromObject(Engine, instance));
 
                 return result;
             }
@@ -108,53 +108,32 @@ namespace Jint.Runtime.Interop
             return base.HasInstance(v);
         }
 
-        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc, bool throwOnError)
+        public override bool DefineOwnProperty(in Key propertyName, PropertyDescriptor desc)
         {
-            if (throwOnError)
-            {
-                ExceptionHelper.ThrowTypeError(_engine, "Can't define a property of a TypeReference");
-            }
-
             return false;
         }
 
-        public override bool Delete(in Key propertyName, bool throwOnError)
+        public override bool Delete(in Key propertyName)
         {
-            if (throwOnError)
-            {
-                ExceptionHelper.ThrowTypeError(_engine, "Can't delete a property of a TypeReference");
-            }
-
             return false;
         }
 
-        public override void Put(in Key propertyName, JsValue value, bool throwOnError)
+        public override bool Set(in Key propertyName, JsValue value, JsValue receiver)
         {
             if (!CanPut(propertyName))
             {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(Engine);
-                }
-
-                return;
+                return false;
             }
 
             var ownDesc = GetOwnProperty(propertyName);
 
             if (ownDesc == null)
             {
-                if (throwOnError)
-                {
-                    ExceptionHelper.ThrowTypeError(_engine, "Unknown member: " + propertyName);
-                }
-                else
-                {
-                    return;
-                }
+                return false;
             }
 
             ownDesc.Value = value;
+            return true;
         }
 
         public override PropertyDescriptor GetOwnProperty(in Key key)

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

@@ -63,11 +63,11 @@ namespace Jint.Runtime.Interpreter.Expressions
 
             var obj = TypeConverter.ToObject(engine, argument);
 
-            ArrayPrototype.ArrayOperations arrayOperations = null;
+            ArrayOperations arrayOperations = null;
             IIterator iterator = null;
             if (obj.IsArrayLike)
             {
-                arrayOperations = ArrayPrototype.ArrayOperations.For(obj);
+                arrayOperations = ArrayOperations.For(obj);
             }
             else
             {
@@ -82,7 +82,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             for (uint i = 0; i < pattern.Elements.Count; i++)
             {
                 var left = pattern.Elements[(int) i];
-                if (left is Esprima.Ast.Identifier identifier)
+                if (left is Identifier identifier)
                 {
                     JsValue value;
                     if (arrayOperations != null)
@@ -162,7 +162,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                             value = jintExpression.GetValue();
                         }
 
-                        if (assignmentPattern.Left is Esprima.Ast.Identifier leftIdentifier)
+                        if (assignmentPattern.Left is Identifier leftIdentifier)
                         {
                             if (assignmentPattern.Right.IsFunctionWithName())
                             {
@@ -196,7 +196,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
                 var left = pattern.Properties[(int) i];
                 string sourceKey;
-                Esprima.Ast.Identifier identifier = left.Key as Esprima.Ast.Identifier;
+                var identifier = left.Key as Identifier;
                 if (identifier == null)
                 {
                     var keyExpression = Build(engine, left.Key);
@@ -223,7 +223,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         continue;
                     }
                     
-                    var target = assignmentPattern.Left as Esprima.Ast.Identifier ?? identifier;
+                    var target = assignmentPattern.Left as Identifier ?? identifier;
 
                     if (assignmentPattern.Right.IsFunctionWithName())
                     {
@@ -238,7 +238,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 }
                 else
                 {
-                    var target = left.Value as Esprima.Ast.Identifier ?? identifier;
+                    var target = left.Value as Identifier ?? identifier;
                     AssignToIdentifier(engine, target.Name, value);
                 }
             }

+ 3 - 2
Jint/Runtime/Interpreter/Expressions/JintNewExpression.cs

@@ -40,13 +40,14 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
 
             // todo: optimize by defining a common abstract class or interface
-            if (!(_calleeExpression.GetValue() is IConstructor callee))
+            var jsValue = _calleeExpression.GetValue();
+            if (!(jsValue is IConstructor callee))
             {
                 return ExceptionHelper.ThrowTypeError<object>(_engine, "The object can't be used as constructor.");
             }
 
             // construct the new instance using the Function's constructor method
-            var instance = callee.Construct(arguments);
+            var instance = callee.Construct(arguments, jsValue);
 
             _engine._jsValueArrayPool.ReturnArray(arguments);
 

+ 14 - 14
Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs

@@ -26,16 +26,16 @@ namespace Jint.Runtime.Interpreter.Expressions
 
         private class ObjectProperty
         {
-            private readonly Key _name;
+            private readonly Key _key;
             internal readonly Property _value;
 
-            public ObjectProperty(in Key propName, Property property)
+            public ObjectProperty(in Key key, Property property)
             {
-                _name = propName;
+                _key = key;
                 _value = property;
             }
 
-            public ref readonly Key Name => ref _name;
+            public ref readonly Key Key => ref _key;
         }
 
         public JintObjectExpression(Engine engine, ObjectExpression expression) : base(engine, expression)
@@ -63,7 +63,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     propName = literal.Value as string ?? Convert.ToString(literal.Value, provider: null);
                 }
 
-                if (property.Key is Esprima.Ast.Identifier identifier)
+                if (!property.Computed && property.Key is Identifier identifier)
                 {
                     propName = identifier.Name;
                 }
@@ -106,7 +106,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var objectProperty = _properties[i];
                 var valueExpression = _valueExpressions[i];
                 var propValue = valueExpression.GetValue();
-                properties[objectProperty.Name] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
+                properties[objectProperty.Key] = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
             }
 
             obj._properties = properties;
@@ -115,16 +115,16 @@ namespace Jint.Runtime.Interpreter.Expressions
                                                 
         private object BuildObjectNormal()
         {
-            var obj = _engine.Object.Construct(System.Math.Max(2, _properties.Length));
+            var obj = _engine.Object.Construct(Math.Max(2, _properties.Length));
             bool isStrictModeCode = StrictModeScope.IsStrictModeCode;
 
             for (var i = 0; i < _properties.Length; i++)
             {
                 var objectProperty = _properties[i];
                 var property = objectProperty._value;
-                var propName = !string.IsNullOrEmpty(objectProperty.Name)
-                    ? objectProperty.Name
-                    : (Key) objectProperty._value.Key.GetKey(_engine);
+                var propName = objectProperty.Key.Name.Length > 0
+                    ? objectProperty.Key
+                    : property.GetKey(_engine);
 
                 PropertyDescriptor propDesc;
 
@@ -135,7 +135,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     if (expr._expression.IsFunctionWithName())
                     {
                         var functionInstance = (FunctionInstance) propValue;
-                        functionInstance.SetFunctionName(objectProperty.Name);
+                        functionInstance.SetFunctionName(propName);
                     }
                     propDesc = new PropertyDescriptor(propValue, PropertyFlag.ConfigurableEnumerableWritable);
                 }
@@ -149,8 +149,8 @@ namespace Jint.Runtime.Interpreter.Expressions
                         _engine.ExecutionContext.LexicalEnvironment,
                         isStrictModeCode
                     );
-                    functionInstance.SetFunctionName(objectProperty.Name);
-                    functionInstance._prototype = null;
+                    functionInstance.SetFunctionName(propName);
+                    functionInstance._prototypeDescriptor = null;
 
                     propDesc = new GetSetPropertyDescriptor(
                         get: property.Kind == PropertyKind.Get ? functionInstance : null,
@@ -162,7 +162,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                     return ExceptionHelper.ThrowArgumentOutOfRangeException<object>();
                 }
 
-                obj.DefineOwnProperty(propName, propDesc, false);
+                obj.DefineOwnProperty(propName, propDesc);
             }
 
             return obj;

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

@@ -12,7 +12,7 @@ namespace Jint.Runtime.Interpreter.Expressions
         public JintSpreadExpression(Engine engine, SpreadElement expression) : base(engine, expression)
         {
             _argument = Build(engine, expression.Argument);
-            _argumentName = (expression.Argument as Esprima.Ast.Identifier)?.Name;
+            _argumentName = (expression.Argument as Identifier)?.Name;
         }
 
         protected override object EvaluateInternal()

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

@@ -62,7 +62,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                 rawObj.SetIndexValue((uint) i, templateElementValue.Raw, updateLength: false);
             }
 
-            template.DefineOwnProperty("raw", new PropertyDescriptor(rawObj, PropertyFlag.AllForbidden), false);
+            template.DefineOwnProperty("raw", new PropertyDescriptor(rawObj, PropertyFlag.AllForbidden));
             return template;
         }
     }

+ 14 - 3
Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs

@@ -1,5 +1,6 @@
 using Esprima.Ast;
 using Jint.Native;
+using Jint.Native.Proxy;
 using Jint.Runtime.Environments;
 using Jint.Runtime.References;
 
@@ -80,9 +81,14 @@ namespace Jint.Runtime.Interpreter.Expressions
                     if (r.IsPropertyReference())
                     {
                         var o = TypeConverter.ToObject(_engine, r.GetBase());
-                        var jsValue = o.Delete(r.GetReferencedName(), r._strict);
+                        var deleteStatus  = o.Delete(r.GetReferencedName());
+                        if (!deleteStatus && r._strict)
+                        {
+                            ExceptionHelper.ThrowTypeError(_engine);
+                        }
+
                         _engine._referencePool.Return(r);
-                        return jsValue ? JsBoolean.True : JsBoolean.False;
+                        return deleteStatus ? JsBoolean.True : JsBoolean.False;
                     }
 
                     if (r._strict)
@@ -131,7 +137,12 @@ namespace Jint.Runtime.Interpreter.Expressions
                         case Types.String: return JsString.StringString;
                     }
 
-                    if (v.TryCast<ICallable>() != null)
+                    if (v is ProxyInstance)
+                    {
+                        return JsString.ObjectString;
+                    }
+
+                    if (v is ICallable)
                     {
                         return JsString.FunctionString;
                     }

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

@@ -49,9 +49,9 @@ namespace Jint.Runtime.Interpreter
             _body = JintStatement.Build(engine, bodyStatement);
         }
 
-        private IEnumerable<Esprima.Ast.Identifier> GetParameterIdentifiers(INode parameter)
+        private IEnumerable<Identifier> GetParameterIdentifiers(INode parameter)
         {
-            if (parameter is Esprima.Ast.Identifier identifier)
+            if (parameter is Identifier identifier)
             {
                 return new [] { identifier };
             }
@@ -73,7 +73,7 @@ namespace Jint.Runtime.Interpreter
                 return GetParameterIdentifiers(assignmentPattern.Left);
             }
 
-            return Enumerable.Empty<Esprima.Ast.Identifier>();
+            return Enumerable.Empty<Identifier>();
         }
 
         private string[] GetParameterNames(IFunction functionDeclaration)
@@ -85,7 +85,7 @@ namespace Jint.Runtime.Interpreter
             for (var i = 0; i < count; i++)
             {
                 var parameter = functionDeclarationParams[i];
-                if (parameter is Esprima.Ast.Identifier id)
+                if (parameter is Identifier id)
                 {
                     parameterNames.Add(id.Name);
                     if (onlyIdentifiers)

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

@@ -20,7 +20,7 @@ namespace Jint.Runtime.Interpreter.Statements
         {
             if (_statement.Left.Type == Nodes.VariableDeclaration)
             {
-                _identifier = JintExpression.Build(engine, (Esprima.Ast.Identifier) ((VariableDeclaration) _statement.Left).Declarations[0].Id);
+                _identifier = JintExpression.Build(engine, (Identifier) ((VariableDeclaration) _statement.Left).Declarations[0].Id);
             }
             else if (_statement.Left.Type == Nodes.MemberExpression)
             {
@@ -28,7 +28,7 @@ namespace Jint.Runtime.Interpreter.Statements
             }
             else
             {
-                _identifier = JintExpression.Build(engine, (Esprima.Ast.Identifier) _statement.Left);
+                _identifier = JintExpression.Build(engine, (Identifier) _statement.Left);
             }
 
             _body = Build(engine, _statement.Body);

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

@@ -19,7 +19,7 @@ namespace Jint.Runtime.Interpreter.Statements
             if (_statement.Handler != null)
             {
                 _catch = Build(engine, _statement.Handler.Body);
-                _catchParamName = ((Esprima.Ast.Identifier) _statement.Handler.Param).Name;
+                _catchParamName = ((Identifier) _statement.Handler.Param).Name;
             }
 
             if (statement.Finalizer != null)

+ 3 - 3
Jint/Runtime/JavaScriptException.cs

@@ -52,7 +52,7 @@ namespace Jint.Runtime
                             sb.Builder.Append(", ");
                         var arg = cse.CallExpression.Arguments[index];
                         if (arg is Expression pke)
-                            sb.Builder.Append(pke.GetKey(engine));
+                            sb.Builder.Append(pke.GetKey(engine, computed: false));
                         else
                             sb.Builder.Append(arg);
                     }
@@ -77,7 +77,7 @@ namespace Jint.Runtime
             if (error.IsObject())
             {
                 var oi = error.AsObject();
-                var message = oi.Get("message").ToString();
+                var message = oi.Get("message", oi).ToString();
                 return message;
             }
             if (error.IsString())
@@ -103,7 +103,7 @@ namespace Jint.Runtime
                     return null;
                 if (Error.IsObject() == false)
                     return null;
-                var callstack = Error.AsObject().Get("callstack");
+                var callstack = Error.AsObject().Get("callstack", Error);
                 if (callstack.IsUndefined())
                     return null;
                 return callstack.AsString();

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

@@ -51,6 +51,13 @@ namespace Jint.Runtime.References
             return _baseValue._type == InternalTypes.Undefined;
         }
 
+        public bool IsSuperReference()
+        {
+            // TODO super not implemented
+            return false;
+        }
+        
+
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsPropertyReference()
         {

+ 21 - 19
Jint/Runtime/TypeConverter.cs

@@ -13,31 +13,33 @@ using Jint.Pooling;
 
 namespace Jint.Runtime
 {
+    [Flags]
     public enum Types
     {
         None = 0,
         Undefined = 1,
         Null = 2,
-        Boolean = 3,
-        String = 4,
-        Number = 5,
-        Symbol = 9,
-        Object = 10,
-        Completion = 20
+        Boolean = 4,
+        String = 8,
+        Number = 16,
+        Symbol = 64,
+        Object = 128,
+        Completion = 256
     }
 
+    [Flags]
     internal enum InternalTypes
     {
         None = 0,
         Undefined = 1,
         Null = 2,
-        Boolean = 3,
-        String = 4,
-        Number = 5,
-        Integer = 6,
-        Symbol = 9,
-        Object = 10,
-        Completion = 20
+        Boolean = 4,
+        String = 8,
+        Number = 16,
+        Integer = 32,
+        Symbol = 64,
+        Object = 128,
+        Completion = 256
     }
 
     public static class TypeConverter
@@ -369,12 +371,12 @@ namespace Jint.Runtime
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/6.0/#sec-topropertykey
         /// </summary>
-        public static string ToPropertyKey(JsValue o)
+        public static Key ToPropertyKey(JsValue o)
         {
             var key = ToPrimitive(o, Types.String);
             if (key is JsSymbol s)
             {
-                return s._value;
+                return s.ToPropertyKey();
             }
 
             return ToString(key);
@@ -496,7 +498,7 @@ namespace Jint.Runtime
 
         public static IEnumerable<Tuple<MethodBase, JsValue[]>> FindBestMatch<T>(Engine engine, T[] methods, Func<T, bool, JsValue[]> argumentProvider) where T : MethodBase
         {
-            System.Collections.Generic.List<Tuple<T, JsValue[]>> matchingByParameterCount = null;
+            List<Tuple<T, JsValue[]>> matchingByParameterCount = null;
             foreach (var m in methods)
             {
                 bool hasParams = false;
@@ -519,7 +521,7 @@ namespace Jint.Runtime
                         yield break;
                     }
 
-                    matchingByParameterCount = matchingByParameterCount ?? new System.Collections.Generic.List<Tuple<T, JsValue[]>>();
+                    matchingByParameterCount = matchingByParameterCount ?? new List<Tuple<T, JsValue[]>>();
                     matchingByParameterCount.Add(new Tuple<T, JsValue[]>(m, arguments));
                 }
                 else if (parameterInfos.Length > arguments.Length)
@@ -535,7 +537,7 @@ namespace Jint.Runtime
                     {
                         // create missing arguments from default values
 
-                        var argsWithDefaults = new System.Collections.Generic.List<JsValue>(arguments);
+                        var argsWithDefaults = new List<JsValue>(arguments);
                         for (var i = arguments.Length; i < parameterInfos.Length; i++)
                         {
                             var param = parameterInfos[i];
@@ -543,7 +545,7 @@ namespace Jint.Runtime
                             argsWithDefaults.Add(value);
                         }
 
-                        matchingByParameterCount = matchingByParameterCount ?? new System.Collections.Generic.List<Tuple<T, JsValue[]>>();
+                        matchingByParameterCount = matchingByParameterCount ?? new List<Tuple<T, JsValue[]>>();
                         matchingByParameterCount.Add(new Tuple<T, JsValue[]>(m, argsWithDefaults.ToArray()));
                     }
                 }

Some files were not shown because too many files changed in this diff