瀏覽代碼

Enable function tests (#736)

* some toString fixes
* implement Function.prototype [ @@hasInstance ] ( V )
* fix issues with bind, etc
* better error messages
* callee and caller semantics have changed
* only define arguments if non-strict function
* bind results no longer have arguments
* function prototype has arguments that throws on get/set
* function prototype call.length is now configurable
* function prototype has caller that throws on get/set
* improve function constructor parsing logic for commented parameters
* tweak some proxy behavior
* fix FunctionPrototype.@@hasInstance
Marko Lahma 5 年之前
父節點
當前提交
1fec0a93af

+ 36 - 36
Jint.Tests.Ecma/TestCases/alltests.json

@@ -16920,8 +16920,8 @@
     source: "ch13/13.2/13.2-28-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller no longer present for functions",
     source: "ch13/13.2/13.2-29-s.js"
   },
   {
@@ -16930,38 +16930,38 @@
     source: "ch13/13.2/13.2-3-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller no longer present for functions",
     source: "ch13/13.2/13.2-30-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller no longer present for functions",
     source: "ch13/13.2/13.2-31-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller no longer present for functions",
     source: "ch13/13.2/13.2-32-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller no longer present for functions",
     source: "ch13/13.2/13.2-33-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller no longer present for functions",
     source: "ch13/13.2/13.2-34-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller no longer present for functions",
     source: "ch13/13.2/13.2-35-s.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "caller no longer present for functions",
     source: "ch13/13.2/13.2-36-s.js"
   },
   {
@@ -37235,8 +37235,8 @@
     source: "ch15/15.3/15.3.4/15.3.4.2/S15.3.4.2_A8.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "the spec has changed",
     source: "ch15/15.3/15.3.4/15.3.4.2/S15.3.4.2_A9.js"
   },
   {
@@ -37490,8 +37490,8 @@
     source: "ch15/15.3/15.3.4/15.3.4.3/S15.3.4.3_A8_T6.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "the spec has changed",
     source: "ch15/15.3/15.3.4/15.3.4.3/S15.3.4.3_A9.js"
   },
   {
@@ -37735,8 +37735,8 @@
     source: "ch15/15.3/15.3.4/15.3.4.4/S15.3.4.4_A7_T6.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "the spec has changed",
     source: "ch15/15.3/15.3.4/15.3.4.4/S15.3.4.4_A9.js"
   },
   {
@@ -37795,8 +37795,8 @@
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-15-1.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "the spec has changed",
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-15-2.js"
   },
   {
@@ -37905,8 +37905,8 @@
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-2-9.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "function no longer has caller present",
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-1.js"
   },
   {
@@ -37920,18 +37920,18 @@
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-3.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "function no longer has caller present",
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-4.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "function no longer has caller present",
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-20-5.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "bind no longer has arguments",
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-1.js"
   },
   {
@@ -37945,13 +37945,13 @@
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-3.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "bind no longer has arguments",
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-4.js"
   },
   {
-    skip: false,
-    reason: "",
+    skip: true,
+    reason: "bind no longer has arguments",
     source: "ch15/15.3/15.3.4/15.3.4.5/15.3.4.5-21-5.js"
   },
   {

+ 15 - 0
Jint.Tests.Test262/BuiltIns/FunctionTests.cs

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

+ 4 - 1
Jint.Tests.Test262/Test262Test.cs

@@ -52,7 +52,10 @@ namespace Jint.Tests.Test262
                 "assertRelativeDateMs.js",
                 "regExpUtils.js",
                 "nans.js",
-                "compareIterator.js"
+                "compareIterator.js",
+                "nativeFunctionMatcher.js",
+                "wellKnownIntrinsicObjects.js",
+                "fnGlobalObject.js"
             };
 
             Sources = new Dictionary<string, string>(files.Length);

+ 74 - 0
Jint.Tests.Test262/test/skipped.json

@@ -33,6 +33,12 @@
   },
 
 
+  {
+    "source": "built-ins/Function/prototype/toString/method-computed-property-name.js",
+    "reason": "requires investigation how to process complex function name evaluation for property"
+  },
+
+
   // http://www.ecma-international.org/ecma-262/#sec-block-level-function-declarations-web-legacy-compatibility-semantics
   {
     "source": "language/statements/let/block-local-closure-set-before-initialization.js",
@@ -688,6 +694,74 @@
 
 
   // class support
+  {
+    "source": "built-ins/Function/prototype/toString/class-declaration-complex-heritage.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/class-declaration-explicit-ctor.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/class-declaration-implicit-ctor.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/class-expression-explicit-ctor.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/class-expression-implicit-ctor.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/getter-class-expression-static.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/getter-class-expression.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/getter-class-statement-static.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/getter-class-statement.js",
+    "reason": "class not implemented"
+  }, 
+  {
+    "source": "built-ins/Function/prototype/toString/method-class-expression-static.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/method-class-expression.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/method-class-statement-static.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/method-class-statement.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/setter-class-expression-static.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/setter-class-expression.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/setter-class-statement-static.js",
+    "reason": "class not implemented"
+  },
+  {
+    "source": "built-ins/Function/prototype/toString/setter-class-statement.js",
+    "reason": "class not implemented"
+  },
   {
     "source": "language/global-code/script-decl-lex.js",
     "reason": "class not implemented"

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

@@ -19,7 +19,7 @@ var b = a.user.name;
 
             var engine = new Engine();
             var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
-            Assert.Equal("user is undefined", e.Message);
+            Assert.Equal("Cannot read property 'name' of undefined", e.Message);
             Assert.Equal(4, e.Location.Start.Line);
             Assert.Equal(8, e.Location.Start.Column);
         }
@@ -40,7 +40,7 @@ var c = a(b().Length);
                 return null;
             }));
             var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
-            Assert.Equal("The value is null", e.Message);
+            Assert.Equal("Cannot read property 'Length' of null", e.Message);
             Assert.Equal(2, e.Location.Start.Line);
             Assert.Equal(10, e.Location.Start.Column);
         }
@@ -73,7 +73,7 @@ var b = function(v) {
 }", new ParserOptions("custom.js") { Loc = true });
 
             var e = Assert.Throws<JavaScriptException>(() => engine.Execute("var x = b(7);", new ParserOptions("main.js") { Loc = true } ));
-            Assert.Equal("xxx is undefined", e.Message);
+            Assert.Equal("Cannot read property 'yyy' of undefined", e.Message);
             Assert.Equal(2, e.Location.Start.Line);
             Assert.Equal(8, e.Location.Start.Column);
             Assert.Equal("custom.js", e.Location.Source);
@@ -127,7 +127,7 @@ var b = function(v) {
                 Test.recursive(folder);"
             ));
 
-            Assert.Equal("folderInstance is null", javaScriptException.Message);
+            Assert.Equal("Cannot read property 'Name' of null", javaScriptException.Message);
             Assert.Equal(@" at recursive(folderInstance.parent) @  31:8
  at recursive(folderInstance.parent) @  31:8
  at recursive(folderInstance.parent) @  31:8

+ 4 - 2
Jint/Engine.cs

@@ -105,7 +105,8 @@ namespace Jint
         };
 
         // shared frozen version
-        internal readonly PropertyDescriptor _getSetThrower;
+        internal readonly PropertyDescriptor _callerCalleeArgumentsThrowerConfigurable;
+        internal readonly PropertyDescriptor _callerCalleeArgumentsThrowerNonConfigurable;
 
         internal readonly struct ClrPropertyDescriptorFactoriesKey : IEquatable<ClrPropertyDescriptorFactoriesKey>
         {
@@ -158,7 +159,8 @@ namespace Jint
 
             Object = ObjectConstructor.CreateObjectConstructor(this);
             Function = FunctionConstructor.CreateFunctionConstructor(this);
-            _getSetThrower = new GetSetPropertyDescriptor.ThrowerPropertyDescriptor(Function.ThrowTypeError);
+            _callerCalleeArgumentsThrowerConfigurable = new GetSetPropertyDescriptor.ThrowerPropertyDescriptor(this,  PropertyFlag.Configurable | PropertyFlag.CustomJsValue, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them");
+            _callerCalleeArgumentsThrowerNonConfigurable = new GetSetPropertyDescriptor.ThrowerPropertyDescriptor(this, PropertyFlag.CustomJsValue, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them");
 
             Symbol = SymbolConstructor.CreateSymbolConstructor(this);
             Array = ArrayConstructor.CreateArrayConstructor(this);

+ 1 - 1
Jint/Native/Argument/ArgumentsInstance.cs

@@ -66,7 +66,7 @@ namespace Jint.Native.Argument
                     CreateDataProperty(JsNumber.Create(i), val);
                 }
                 
-                DefinePropertyOrThrow(CommonProperties.Callee, _engine._getSetThrower);
+                DefinePropertyOrThrow(CommonProperties.Callee, _engine._callerCalleeArgumentsThrowerNonConfigurable);
             }
             else
             {

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

@@ -1,4 +1,3 @@
-using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -82,28 +81,5 @@ namespace Jint.Native.Function
                 return Undefined;
             }
         }
-
-        public override bool Set(JsValue property, JsValue value, JsValue receiver)
-        {
-            AssertValidPropertyName(property);
-            return base.Set(property, value, receiver);
-        }
-
-        public override JsValue Get(JsValue property, JsValue receiver)
-        {
-            AssertValidPropertyName(property);
-            return base.Get(property, receiver);
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void AssertValidPropertyName(JsValue property)
-        {
-            if (property == CommonProperties.Caller
-                || property ==  CommonProperties.Callee
-                || property == CommonProperties.Arguments)
-            {
-                ExceptionHelper.ThrowTypeError(_engine, "'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them");
-            }
-        }
     }
 }

+ 4 - 4
Jint/Native/Function/BindFunctionInstance.cs

@@ -6,10 +6,8 @@ namespace Jint.Native.Function
 {
     public sealed class BindFunctionInstance : FunctionInstance, IConstructor
     {
-        private static readonly JsString _name = new JsString("bind");
-
-        public BindFunctionInstance(Engine engine)
-            : base(engine, _name, FunctionThisMode.Global)
+        public BindFunctionInstance(Engine engine) 
+            : base(engine, name: null, thisMode: FunctionThisMode.Strict)
         {
         }
 
@@ -55,5 +53,7 @@ namespace Jint.Native.Function
         }
 
         internal override bool IsConstructor => TargetFunction is IConstructor;
+
+        public override string ToString() => "function () { [native code] }";
     }
 }

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

@@ -84,7 +84,7 @@ namespace Jint.Native.Function
                 {
                     Engine.EvalDeclarationInstantiation(script, varEnv, lexEnv, strictEval);
 
-                    var statement = JintStatement.Build(_engine, script);
+                    var statement = new JintScript(_engine, script);
                     var result = statement.Execute();
                     var value = result.GetValueOrDefault();
 

+ 35 - 18
Jint/Native/Function/FunctionConstructor.cs

@@ -11,9 +11,7 @@ namespace Jint.Native.Function
     {
         private static readonly ParserOptions ParserOptions = new ParserOptions { AdaptRegexp = true, Tolerant = false };
         private static readonly JsString _functionName = new JsString("Function");
-
-        private FunctionInstance _throwTypeError;
-        private static readonly char[] ArgumentNameSeparator = new[] { ',' };
+        private static readonly JsString _functionNameAnonymous = new JsString("anonymous");
 
         private FunctionConstructor(Engine engine)
             : base(engine, _functionName)
@@ -71,7 +69,37 @@ namespace Jint.Native.Function
             IFunction function;
             try
             {
-                var functionExpression = "function f(" + p + ") { " + body + "}";
+                string functionExpression;
+                if (argCount == 0)
+                {
+                    functionExpression = "function f(){}";
+                }
+                else
+                {
+                    functionExpression = "function f(";
+                    if (p.IndexOf('/') != -1)
+                    {
+                        // ensure comments don't screw up things
+                        functionExpression += "\n" + p + "\n";
+                    }
+                    else
+                    {
+                        functionExpression += p;
+                    }
+
+                    functionExpression += ")";
+
+                    if (body.IndexOf('/') != -1)
+                    {
+                        // ensure comments don't screw up things
+                        functionExpression += "{\n" + body + "\n}";
+                    }
+                    else
+                    {
+                        functionExpression += "{" + body + "}";
+                    }
+                }
+
                 var parser = new JavaScriptParser(functionExpression, ParserOptions);
                 function = (IFunction) parser.ParseScript().Body[0];
             }
@@ -86,6 +114,9 @@ namespace Jint.Native.Function
                 _engine.GlobalEnvironment,
                 function.Strict);
 
+            // the function is not actually a named function
+            functionObject.SetFunctionName(_functionNameAnonymous, force: true);
+
             return functionObject;
         }
 
@@ -105,20 +136,6 @@ namespace Jint.Native.Function
             return functionObject;
         }
 
-        public FunctionInstance ThrowTypeError
-        {
-            get
-            {
-                if (!ReferenceEquals(_throwTypeError, null))
-                {
-                    return _throwTypeError;
-                }
-
-                _throwTypeError = new ThrowTypeError(Engine);
-                return _throwTypeError;
-            }
-        }
-
         public object Apply(JsValue thisObject, JsValue[] arguments)
         {
             if (arguments.Length != 2)

+ 37 - 46
Jint/Native/Function/FunctionInstance.cs

@@ -19,10 +19,8 @@ namespace Jint.Native.Function
 
         protected internal PropertyDescriptor _prototypeDescriptor;
 
-        protected PropertyDescriptor _length;
-
-        private JsValue _name;
-        private PropertyDescriptor _nameDescriptor;
+        protected internal PropertyDescriptor _length;
+        internal PropertyDescriptor _nameDescriptor;
 
         protected internal LexicalEnvironment _environment;
         internal readonly JintFunctionDefinition _functionDefinition;
@@ -46,7 +44,10 @@ namespace Jint.Native.Function
             ObjectClass objectClass = ObjectClass.Function)
             : base(engine, objectClass)
         {
-            _name = name;
+            if (!(name is null))
+            {
+                _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable);
+            }
             _thisMode = thisMode;
         }
 
@@ -67,7 +68,7 @@ namespace Jint.Native.Function
                 return false;
             }
 
-            var p = Get(CommonProperties.Prototype, this);
+            var p = Get(CommonProperties.Prototype);
             if (!(p is ObjectInstance prototype))
             {
                 ExceptionHelper.ThrowTypeError(_engine, $"Function has non-object prototype '{TypeConverter.ToString(p)}' in instanceof check");
@@ -89,22 +90,6 @@ namespace Jint.Native.Function
             }
         }
 
-        /// <summary>
-        /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.4
-        /// </summary>
-        public override JsValue Get(JsValue property, JsValue receiver)
-        {
-            var v = base.Get(property, receiver);
-
-            if (property == CommonProperties.Caller
-                && v.As<FunctionInstance>()?._thisMode == FunctionThisMode.Strict)
-            {
-                ExceptionHelper.ThrowTypeError(_engine);
-            }
-
-            return v;
-        }
-
         public override IEnumerable<KeyValuePair<JsValue, PropertyDescriptor>> GetOwnProperties()
         {
             if (_prototypeDescriptor != null)
@@ -115,7 +100,7 @@ namespace Jint.Native.Function
             {
                 yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Length, _length);
             }
-            if (!(_name is null))
+            if (_nameDescriptor != null)
             {
                 yield return new KeyValuePair<JsValue, PropertyDescriptor>(CommonProperties.Name, GetOwnProperty(CommonProperties.Name));
             }
@@ -137,7 +122,7 @@ namespace Jint.Native.Function
             {
                 keys.Add(CommonProperties.Length);
             }
-            if (!(_name is null))
+            if (_nameDescriptor != null)
             {
                 keys.Add(CommonProperties.Name);
             }
@@ -159,9 +144,7 @@ namespace Jint.Native.Function
             }
             if (property == CommonProperties.Name)
             {
-                return !(_name is null)
-                    ? _nameDescriptor ??= new PropertyDescriptor(_name, PropertyFlag.Configurable)
-                    :  PropertyDescriptor.Undefined;
+                return _nameDescriptor ?? PropertyDescriptor.Undefined;
             }
 
             return base.GetOwnProperty(property);
@@ -179,7 +162,6 @@ namespace Jint.Native.Function
             }
             else if (property == CommonProperties.Name)
             {
-                _name = desc._value;
                 _nameDescriptor = desc;
             }
             else
@@ -200,7 +182,7 @@ namespace Jint.Native.Function
             }
             if (property == CommonProperties.Name)
             {
-                return !(_name is null);
+                return _nameDescriptor != null;
             }
 
             return base.HasOwnProperty(property);
@@ -218,34 +200,31 @@ namespace Jint.Native.Function
             }
             if (property == CommonProperties.Name)
             {
-                _name = null;
                 _nameDescriptor = null;
             }
 
             base.RemoveOwnProperty(property);
         }
 
-        internal void SetFunctionName(JsValue name, bool throwIfExists = false)
+        internal void SetFunctionName(JsValue name, string prefix = null, bool force = false)
         {
-            if (_name is null)
+            if (!force && _nameDescriptor != null && !UnwrapJsValue(_nameDescriptor).IsUndefined())
             {
-                JsString value;
-                if (name is JsSymbol symbol)
-                {
-                    value = new JsString(symbol._value.IsUndefined()
-                        ? ""
-                        : "[" + symbol._value + "]");
-                }
-                else
-                {
-                    value = name as JsString ?? new JsString(name.ToString());
-                }
-                _name = value;
+                return;
+            }
+            
+            if (name is JsSymbol symbol)
+            {
+                name = symbol._value.IsUndefined()
+                    ? JsString.Empty
+                    : new JsString("[" + symbol._value + "]");
             }
-            else if (throwIfExists)
+            if (!string.IsNullOrWhiteSpace(prefix))
             {
-                ExceptionHelper.ThrowError(_engine, "cannot set name");
+                name = prefix + " " + name;
             }
+
+            _nameDescriptor = new PropertyDescriptor(name, PropertyFlag.Configurable);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -269,5 +248,17 @@ namespace Jint.Native.Function
             //    Set proto to realm's intrinsic object named intrinsicDefaultProto.
             return proto ?? intrinsicDefaultProto;
         }
+
+        public override string ToString()
+        {
+            // TODO no way to extract SourceText from Esprima at the moment, just returning native code
+            var nameValue = _nameDescriptor != null ? UnwrapJsValue(_nameDescriptor) : JsString.Empty;
+            var name = "";
+            if (!nameValue.IsUndefined())
+            {
+                name = TypeConverter.ToString(nameValue);
+            }
+            return "function " + name  + "() {{[native code]}}";
+        }
     }
 }

+ 60 - 22
Jint/Native/Function/FunctionPrototype.cs

@@ -1,6 +1,7 @@
 using Jint.Collections;
 using Jint.Native.Array;
 using Jint.Native.Object;
+using Jint.Native.Symbol;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Interop;
@@ -12,10 +13,8 @@ namespace Jint.Native.Function
     /// </summary>
     public sealed class FunctionPrototype : FunctionInstance
     {
-        private static readonly JsString _functionName = new JsString("Function");
-
         private FunctionPrototype(Engine engine)
-            : base(engine, _functionName)
+            : base(engine, JsString.Empty)
         {
         }
 
@@ -33,57 +32,96 @@ namespace Jint.Native.Function
 
         protected override void Initialize()
         {
-            var properties = new PropertyDictionary(5, checkExistingKeys: false)
+            const PropertyFlag propertyFlags = PropertyFlag.Configurable | PropertyFlag.Writable;
+            const PropertyFlag lengthFlags = PropertyFlag.Configurable;
+            var properties = new PropertyDictionary(7, checkExistingKeys: false)
             {
                 ["constructor"] = new PropertyDescriptor(Engine.Function, PropertyFlag.NonEnumerable),
-                ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString), true, false, true),
-                ["apply"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "apply", Apply, 2), true, false, true),
-                ["call"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "call", CallImpl, 1), true, false, true),
-                ["bind"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "bind", Bind, 1), true, false, true)
+                ["toString"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "toString", ToString, 0, lengthFlags), propertyFlags),
+                ["apply"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "apply", Apply, 2, lengthFlags), propertyFlags),
+                ["call"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "call", CallImpl, 1, lengthFlags), propertyFlags),
+                ["bind"] = new PropertyDescriptor(new ClrFunctionInstance(Engine, "bind", Bind, 1, PropertyFlag.AllForbidden), propertyFlags),
+                ["arguments"] = _engine._callerCalleeArgumentsThrowerConfigurable,
+                ["caller"] = _engine._callerCalleeArgumentsThrowerConfigurable
             };
             SetProperties(properties);
+            
+            var symbols = new SymbolDictionary(1)
+            {
+                [GlobalSymbolRegistry.HasInstance] = new PropertyDescriptor(new ClrFunctionInstance(_engine, "[Symbol.hasInstance]", HasInstance, 1, PropertyFlag.Configurable), PropertyFlag.AllForbidden)
+            };
+            SetSymbols(symbols);
+        }
+
+        private static JsValue HasInstance(JsValue thisObj, JsValue[] arguments)
+        {
+            if (!(thisObj is FunctionInstance f))
+            {
+                return false;
+            }
+            
+            return f.HasInstance(arguments.At(0));
         }
 
         private JsValue Bind(JsValue thisObj, JsValue[] arguments)
         {
-            var target = thisObj.TryCast<ICallable>(x =>
+            if (!(thisObj is ICallable))
             {
-                ExceptionHelper.ThrowTypeError(Engine);
-            });
+                ExceptionHelper.ThrowTypeError(Engine, "Bind must be called on a function");
+            }
 
             var thisArg = arguments.At(0);
             var f = new BindFunctionInstance(Engine)
             {
                 TargetFunction = thisObj,
-                BoundThis = thisArg,
+                BoundThis = thisObj is ArrowFunctionInstance ? Undefined : thisArg,
                 BoundArgs = arguments.Skip(1),
-                _prototype = Engine.Function.PrototypeObject
+                _prototype = Engine.Function.PrototypeObject,
             };
 
-            if (target is FunctionInstance functionInstance)
+            JsNumber l;
+            var targetHasLength = thisObj.HasOwnProperty(CommonProperties.Length);
+            if (targetHasLength)
             {
-                var l = TypeConverter.ToNumber(functionInstance.Get(CommonProperties.Length, functionInstance)) - (arguments.Length - 1);
-                f.SetOwnProperty(CommonProperties.Length, new PropertyDescriptor(System.Math.Max(l, 0), PropertyFlag.AllForbidden));
+                var targetLen = thisObj.Get(CommonProperties.Length);
+                if (!targetLen.IsNumber())
+                {
+                    l = JsNumber.PositiveZero;
+                }
+                else
+                {
+                    targetLen = TypeConverter.ToInteger(targetLen);
+                    // first argument is target
+                    var argumentsLength = System.Math.Max(0, arguments.Length - 1);
+                    l = JsNumber.Create((uint) System.Math.Max(((JsNumber) targetLen)._value - argumentsLength, 0));
+                }
             }
             else
             {
-                f.SetOwnProperty(CommonProperties.Length, PropertyDescriptor.AllForbiddenDescriptor.NumberZero);
+                l = JsNumber.PositiveZero;
+            }
+
+            f._length = new PropertyDescriptor(l, PropertyFlag.Configurable);
+            
+            var targetName = thisObj.Get(CommonProperties.Name);
+            if (!targetName.IsString())
+            {
+                targetName = JsString.Empty;
             }
 
-            f.DefineOwnProperty(CommonProperties.Caller, _engine._getSetThrower);
-            f.DefineOwnProperty(CommonProperties.Arguments, _engine._getSetThrower);
+            f.SetFunctionName(targetName, "bound");
 
             return f;
         }
 
         private JsValue ToString(JsValue thisObj, JsValue[] arguments)
         {
-            if (!(thisObj is FunctionInstance))
+            if (thisObj.IsObject() && thisObj.IsCallable)
             {
-                return ExceptionHelper.ThrowTypeError<FunctionInstance>(_engine, "Function object expected.");
+                return thisObj.ToString();
             }
 
-            return "function() {{ ... }}";
+            return ExceptionHelper.ThrowTypeError<FunctionInstance>(_engine, "Function.prototype.toString requires that 'this' be a Function");
         }
 
         internal JsValue Apply(JsValue thisObject, JsValue[] arguments)

+ 4 - 4
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -11,7 +11,7 @@ namespace Jint.Native.Function
 {
     public sealed class ScriptFunctionInstance : FunctionInstance, IConstructor
     {
-        internal readonly JintFunctionDefinition _function;
+        private readonly JintFunctionDefinition _function;
 
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-13.2
@@ -45,10 +45,10 @@ namespace Jint.Native.Function
 
             _prototypeDescriptor = new PropertyDescriptor(proto, PropertyFlag.OnlyWritable);
 
-            if (thisMode == FunctionThisMode.Strict)
+            if (!function.Strict && !engine._isStrict)
             {
-                DefineOwnProperty(CommonProperties.Caller, engine._getSetThrower);
-                DefineOwnProperty(CommonProperties.Arguments, engine._getSetThrower);
+                DefineOwnProperty(CommonProperties.Arguments, engine._callerCalleeArgumentsThrowerConfigurable);
+                DefineOwnProperty(CommonProperties.Caller, new PropertyDescriptor(Undefined, PropertyFlag.Configurable));
             }
         }
 

+ 5 - 2
Jint/Native/Function/ThrowTypeError.cs

@@ -7,9 +7,12 @@ namespace Jint.Native.Function
     {
         private static readonly JsString _functionName = new JsString("throwTypeError");
 
-        public ThrowTypeError(Engine engine)
+        private readonly string _message;
+
+        public ThrowTypeError(Engine engine, string message = null)
             : base(engine, _functionName)
         {
+            _message = message;
             _length = PropertyDescriptor.AllForbiddenDescriptor.NumberZero;
             _environment = engine.GlobalEnvironment;
             PreventExtensions();
@@ -17,7 +20,7 @@ namespace Jint.Native.Function
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
-            return ExceptionHelper.ThrowTypeError<JsValue>(_engine);
+            return ExceptionHelper.ThrowTypeError<JsValue>(_engine, _message);
         }
     }
 }

+ 4 - 0
Jint/Native/JsValue.cs

@@ -413,6 +413,8 @@ namespace Jint.Native
             return callable.Call(v, arguments);
         }
 
+        public virtual bool HasOwnProperty(JsValue property) => false;
+        
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue Get(JsValue property)
         {
@@ -594,6 +596,8 @@ namespace Jint.Native
         {
             return this;
         }
+        
+        internal virtual bool IsCallable => this is ICallable;
 
         internal static bool SameValue(JsValue x, JsValue y)
         {

+ 11 - 3
Jint/Native/Object/ObjectConstructor.cs

@@ -285,7 +285,11 @@ namespace Jint.Native.Object
 
         private JsValue DefineProperty(JsValue thisObject, JsValue[] arguments)
         {
-            var o = arguments.As<ObjectInstance>(0, _engine);
+            if (!(arguments.At(0) is ObjectInstance o))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Object.defineProperty called on non-object");
+            }
+
             var p = arguments.At(1);
             var name = TypeConverter.ToPropertyKey(p);
 
@@ -293,12 +297,16 @@ namespace Jint.Native.Object
             var desc = PropertyDescriptor.ToPropertyDescriptor(Engine, attributes);
             o.DefinePropertyOrThrow(name, desc);
 
-            return o;
+            return arguments.At(0);
         }
 
         private JsValue DefineProperties(JsValue thisObject, JsValue[] arguments)
         {
-            var o = arguments.As<ObjectInstance>(0, _engine);
+            if (!(arguments.At(0) is ObjectInstance o))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Object.defineProperty called on non-object");
+            }
+
             var properties = arguments.At(1);
             var props = TypeConverter.ToObject(Engine, properties);
             var descriptors = new List<KeyValuePair<JsValue, PropertyDescriptor>>();

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

@@ -276,7 +276,7 @@ namespace Jint.Native.Object
             return _symbols?.TryGetValue((JsSymbol) key, out descriptor) == true;
         }
 
-        public virtual bool HasOwnProperty(JsValue property)
+        public override bool HasOwnProperty(JsValue property)
         {
             EnsureInitialized();
 

+ 9 - 5
Jint/Native/Proxy/ProxyInstance.cs

@@ -1,12 +1,12 @@
 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
+    public class ProxyInstance : ObjectInstance, IConstructor, ICallable
     {
         internal ObjectInstance _target;
         internal ObjectInstance _handler;
@@ -32,13 +32,13 @@ namespace Jint.Native.Proxy
             Engine engine,
             ObjectInstance target,
             ObjectInstance handler)
-            : base(engine, JsString.Empty, FunctionThisMode.Global, target.Class)
+            : base(engine, target.Class)
         {
             _target = target;
             _handler = handler;
         }
 
-        public override JsValue Call(JsValue thisObject, JsValue[] arguments)
+        public JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
             var jsValues = new[] { _target, thisObject, _engine.Array.Construct(arguments) };
             if (TryCallHandler(TrapApply, jsValues, out var result))
@@ -444,6 +444,8 @@ namespace Jint.Native.Proxy
             return true;
         }
 
+        internal override bool IsCallable => _target is ICallable;
+
         private bool TryCallHandler(JsValue propertyName, JsValue[] arguments, out JsValue result)
         {
             AssertNotRevoked(propertyName);
@@ -464,7 +466,7 @@ namespace Jint.Native.Proxy
             return false;
         }
 
-        internal void AssertNotRevoked(JsValue key)
+        private void AssertNotRevoked(JsValue key)
         {
             if (_handler is null)
             {
@@ -479,5 +481,7 @@ namespace Jint.Native.Proxy
                 ExceptionHelper.ThrowTypeError(_engine, $"Cannot perform '{key}' on a proxy that has been revoked");
             }
         }
+        
+        public override string ToString() => "function () { [native code] }";
     }
 }

+ 5 - 1
Jint/Native/Reflect/ReflectInstance.cs

@@ -76,7 +76,11 @@ namespace Jint.Native.Reflect
 
         private JsValue DefineProperty(JsValue thisObject, JsValue[] arguments)
         {
-            var o = arguments.As<ObjectInstance>(0, _engine);
+            if (!(arguments.At(0) is ObjectInstance o))
+            {
+                return ExceptionHelper.ThrowTypeError<JsValue>(_engine, "Reflect.defineProperty called on non-object");
+            }
+
             var p = arguments.At(1);
             var name = TypeConverter.ToPropertyKey(p);
 

+ 0 - 12
Jint/Runtime/Arguments.cs

@@ -32,18 +32,6 @@ 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)
         {

+ 11 - 9
Jint/Runtime/Descriptors/Specialized/GetSetPropertyDescriptor.cs

@@ -1,4 +1,5 @@
 using Jint.Native;
+using Jint.Native.Function;
 
 namespace Jint.Runtime.Descriptors.Specialized
 {
@@ -42,18 +43,19 @@ namespace Jint.Runtime.Descriptors.Specialized
 
         internal sealed class ThrowerPropertyDescriptor : PropertyDescriptor
         {
-            private readonly JsValue _get;
-            private readonly JsValue _set;
-
-            public ThrowerPropertyDescriptor(JsValue functionThrowTypeError)
-                : base(PropertyFlag.EnumerableSet | PropertyFlag.ConfigurableSet | PropertyFlag.CustomJsValue)
+            private readonly Engine _engine;
+            private readonly string _message;
+            private JsValue _thrower;
+            
+            public ThrowerPropertyDescriptor(Engine engine, PropertyFlag flags, string message)
+                : base(flags)
             {
-                _get = functionThrowTypeError;
-                _set = functionThrowTypeError;
+                _engine = engine;
+                _message = message;
             }
 
-            public override JsValue Get => _get;
-            public override JsValue Set => _set;
+            public override JsValue Get => _thrower ??= new ThrowTypeError(_engine, _message) { _prototype = _engine.Function.PrototypeObject};
+            public override JsValue Set => _thrower ??= new ThrowTypeError(_engine, _message) { _prototype = _engine.Function.PrototypeObject};
 
             protected internal override JsValue CustomValue
             {

+ 7 - 0
Jint/Runtime/Interop/ClrFunctionInstance.cs

@@ -10,6 +10,7 @@ namespace Jint.Runtime.Interop
     /// </summary>
     public sealed class ClrFunctionInstance : FunctionInstance, IEquatable<ClrFunctionInstance>
     {
+        private readonly string _name;
         internal readonly Func<JsValue, JsValue[], JsValue> _func;
 
         public ClrFunctionInstance(
@@ -20,6 +21,7 @@ namespace Jint.Runtime.Interop
             PropertyFlag lengthFlags = PropertyFlag.AllForbidden)
             : base(engine, !string.IsNullOrWhiteSpace(name) ? new JsString(name) : null)
         {
+            _name = name;
             _func = func;
 
             _prototype = engine.Function.PrototypeObject;
@@ -65,5 +67,10 @@ namespace Jint.Runtime.Interop
             
             return false;
         }
+
+        public override string ToString()
+        {
+            return $"function {_name}() {{ [native code] }}";
+        }
     }
 }

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

@@ -84,7 +84,7 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
 
             var property = _determinedProperty ?? _propertyExpression.GetValue();
-            TypeConverter.CheckObjectCoercible(_engine, baseValue, (MemberExpression) _expression, baseReferenceName);
+            TypeConverter.CheckObjectCoercible(_engine, baseValue, (MemberExpression) _expression, _determinedProperty?.ToString() ?? baseReferenceName);
             return _engine._referencePool.Rent(baseValue,  TypeConverter.ToPropertyKey(property), isStrictModeCode);
         }
     }

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

@@ -9,7 +9,7 @@ using Jint.Runtime.Descriptors.Specialized;
 namespace Jint.Runtime.Interpreter.Expressions
 {
     /// <summary>
-    /// http://www.ecma-international.org/ecma-262/5.1/#sec-11.1.5
+    /// http://www.ecma-international.org/ecma-262/#sec-object-initializer
     /// </summary>
     internal sealed class JintObjectExpression : JintExpression
     {

+ 1 - 7
Jint/Runtime/Interpreter/Expressions/JintUnaryExpression.cs

@@ -1,6 +1,5 @@
 using Esprima.Ast;
 using Jint.Native;
-using Jint.Native.Proxy;
 using Jint.Runtime.Environments;
 using Jint.Runtime.References;
 
@@ -138,12 +137,7 @@ namespace Jint.Runtime.Interpreter.Expressions
                         case Types.Symbol: return JsString.SymbolString;
                     }
 
-                    if (v is ProxyInstance)
-                    {
-                        return JsString.ObjectString;
-                    }
-
-                    if (v is ICallable)
+                    if (v.IsCallable)
                     {
                         return JsString.FunctionString;
                     }

+ 6 - 4
Jint/Runtime/TypeConverter.cs

@@ -495,8 +495,10 @@ namespace Jint.Runtime
                 InternalTypes.Number => engine.Number.Construct(((JsNumber) value)._value),
                 InternalTypes.Integer => engine.Number.Construct(((JsNumber) value)._value),
                 InternalTypes.String => engine.String.Construct(value.ToString()),
-                InternalTypes.Symbol => engine.Symbol.Construct(((JsSymbol) value)),
-                _ => ExceptionHelper.ThrowTypeError<ObjectInstance>(engine)
+                InternalTypes.Symbol => engine.Symbol.Construct((JsSymbol) value),
+                InternalTypes.Null => ExceptionHelper.ThrowTypeError<ObjectInstance>(engine, "Cannot convert undefined or null to object"),
+                InternalTypes.Undefined => ExceptionHelper.ThrowTypeError<ObjectInstance>(engine, "Cannot convert undefined or null to object"),
+                _ => ExceptionHelper.ThrowTypeError<ObjectInstance>(engine, "Cannot convert given item to object")
             };
         }
         
@@ -518,8 +520,8 @@ namespace Jint.Runtime
             MemberExpression expression,
             string referencedName)
         {
-            referencedName ??= "The value";
-            var message = $"{referencedName} is {o}";
+            referencedName ??= "unknown";
+            var message = $"Cannot read property '{referencedName}' of {o}";
             throw new JavaScriptException(engine.TypeError, message).SetCallstack(engine, expression.Location);
         }