Browse Source

Introduce JsString, JsBoolean etc and support efficient string concatenation (#463)

* introduce JsString and support efficient string concatenation

* introduce JsBoolean, JsNumber and JsObject

* introduce JsNull and JsUndefined, clean up JsValue

* make ObjectInstance JsValue

* favor instance fields for prototype objects instead of thread local

* optimize string concatenation prediction logic

* prefer ReferenceEquals for faster null and undefined checks
Marko Lahma 7 years ago
parent
commit
e0d2e2f244
59 changed files with 1331 additions and 969 deletions
  1. 5 6
      Jint.Tests.CommonScripts/Properties/AssemblyInfo.cs
  2. 0 9
      Jint.Tests.CommonScripts/packages.config
  3. 0 1
      Jint.Tests.Ecma/Properties/AssemblyInfo.cs
  4. 0 9
      Jint.Tests.Ecma/packages.config
  5. 0 1
      Jint.Tests/Parser/JavascriptParserTests.cs
  6. 0 1
      Jint.Tests/ReplaceCulture.cs
  7. 1 1
      Jint.Tests/Runtime/Domain/A.cs
  8. 1 7
      Jint.Tests/Runtime/Domain/Person.cs
  9. 34 38
      Jint.Tests/Runtime/Domain/Shape.cs
  10. 12 13
      Jint.Tests/Runtime/InteropTests.cs
  11. 7 12
      Jint.Tests/Runtime/JsValueConversionTests.cs
  12. 4 6
      Jint.Tests/Runtime/NullPropagation.cs
  13. 0 10
      Jint.Tests/packages.config
  14. 19 23
      Jint/Engine.cs
  15. 2 2
      Jint/Native/Argument/ArgumentsInstance.cs
  16. 0 10
      Jint/Native/Array/ArrayExecutionContext.cs
  17. 2 2
      Jint/Native/Array/ArrayInstance.cs
  18. 42 33
      Jint/Native/Array/ArrayPrototype.cs
  19. 1 1
      Jint/Native/Date/DateConstructor.cs
  20. 1 1
      Jint/Native/Error/ErrorConstructor.cs
  21. 1 1
      Jint/Native/Error/ErrorPrototype.cs
  22. 1 1
      Jint/Native/Function/FunctionConstructor.cs
  23. 2 2
      Jint/Native/Function/FunctionPrototype.cs
  24. 1 1
      Jint/Native/Function/FunctionShim.cs
  25. 4 6
      Jint/Native/Function/ScriptFunctionInstance.cs
  26. 34 19
      Jint/Native/Global/GlobalObject.cs
  27. 72 0
      Jint/Native/JsBoolean.cs
  28. 49 0
      Jint/Native/JsNull.cs
  29. 173 0
      Jint/Native/JsNumber.cs
  30. 210 0
      Jint/Native/JsString.cs
  31. 70 0
      Jint/Native/JsSymbol.cs
  32. 49 0
      Jint/Native/JsUndefined.cs
  33. 76 532
      Jint/Native/JsValue.cs
  34. 7 8
      Jint/Native/Json/JsonInstance.cs
  35. 1 1
      Jint/Native/Json/JsonParser.cs
  36. 2 2
      Jint/Native/Json/JsonSerializer.cs
  37. 1 1
      Jint/Native/Number/NumberPrototype.cs
  38. 4 4
      Jint/Native/Object/ObjectConstructor.cs
  39. 246 33
      Jint/Native/Object/ObjectInstance.cs
  40. 2 2
      Jint/Native/Object/ObjectPrototype.cs
  41. 7 6
      Jint/Native/RegExp/RegExpConstructor.cs
  42. 4 4
      Jint/Native/RegExp/RegExpPrototype.cs
  43. 0 43
      Jint/Native/String/StringExecutionContext.cs
  44. 96 32
      Jint/Native/String/StringPrototype.cs
  45. 3 3
      Jint/Native/Symbol/SymbolConstructor.cs
  46. 2 2
      Jint/Native/Symbol/SymbolPrototype.cs
  47. 2 2
      Jint/Native/Undefined.cs
  48. 1 1
      Jint/Runtime/Descriptors/PropertyDescriptor.cs
  49. 5 5
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  50. 5 5
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  51. 30 19
      Jint/Runtime/ExpressionIntepreter.cs
  52. 2 2
      Jint/Runtime/Interop/MethodInfoFunctionInstance.cs
  53. 7 5
      Jint/Runtime/Interop/NamespaceReference.cs
  54. 2 2
      Jint/Runtime/Interop/SetterFunctionInstance.cs
  55. 15 24
      Jint/Runtime/Interop/TypeReference.cs
  56. 2 2
      Jint/Runtime/JavaScriptException.cs
  57. 1 1
      Jint/Runtime/References/Reference.cs
  58. 1 2
      Jint/Runtime/StatementInterpreter.cs
  59. 10 10
      Jint/Runtime/TypeConverter.cs

+ 5 - 6
Jint.Tests.CommonScripts/Properties/AssemblyInfo.cs

@@ -1,8 +1,7 @@
 using System.Reflection;
-using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
-// General Information about an assembly is controlled through the following 
+// General Information about an assembly is controlled through the following
 // set of attributes. Change these attribute values to modify the information
 // associated with an assembly.
 [assembly: AssemblyTitle("Jint.Tests.CommonScripts")]
@@ -14,8 +13,8 @@ using System.Runtime.InteropServices;
 [assembly: AssemblyTrademark("")]
 [assembly: AssemblyCulture("")]
 
-// Setting ComVisible to false makes the types in this assembly not visible 
-// to COM components.  If you need to access a type in this assembly from 
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components.  If you need to access a type in this assembly from
 // COM, set the ComVisible attribute to true on that type.
 [assembly: ComVisible(false)]
 
@@ -25,11 +24,11 @@ using System.Runtime.InteropServices;
 // Version information for an assembly consists of the following four values:
 //
 //      Major Version
-//      Minor Version 
+//      Minor Version
 //      Build Number
 //      Revision
 //
-// You can specify all the values or you can default the Build and Revision Numbers 
+// You can specify all the values or you can default the Build and Revision Numbers
 // by using the '*' as shown below:
 // [assembly: AssemblyVersion("1.0.*")]
 [assembly: AssemblyVersion("1.0.0.0")]

+ 0 - 9
Jint.Tests.CommonScripts/packages.config

@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="xunit" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.assert" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.core" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.extensibility.core" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.runner.visualstudio" version="2.0.0" targetFramework="net45" />
-</packages>

+ 0 - 1
Jint.Tests.Ecma/Properties/AssemblyInfo.cs

@@ -1,5 +1,4 @@
 using System.Reflection;
-using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Xunit;
 

+ 0 - 9
Jint.Tests.Ecma/packages.config

@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="xunit" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.assert" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.core" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.extensibility.core" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.runner.visualstudio" version="2.0.0" targetFramework="net45" />
-</packages>

+ 0 - 1
Jint.Tests/Parser/JavascriptParserTests.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Diagnostics;
 using System.IO;
 using System.Linq;
 using System.Reflection;

+ 0 - 1
Jint.Tests/ReplaceCulture.cs

@@ -4,7 +4,6 @@
 using System;
 using System.Globalization;
 using System.Reflection;
-using System.Threading;
 using Xunit.Sdk;
 
 namespace Jint.Tests

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

@@ -40,7 +40,7 @@ namespace Jint.Tests.Runtime.Domain
 
         public string Call6(Func<JsValue, JsValue[], JsValue> callback)
         {
-            var thisArg = new JsValue("bar");
+            var thisArg = new JsString("bar");
             var arguments = new JsValue[] { 1, "foo" };
 
             return callback(thisArg, arguments).ToString();

+ 1 - 7
Jint.Tests/Runtime/Domain/Person.cs

@@ -1,10 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Jint.Tests.Runtime.Domain
+namespace Jint.Tests.Runtime.Domain
 {
     public class Person : IPerson
     {

+ 34 - 38
Jint.Tests/Runtime/Domain/Shape.cs

@@ -1,8 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using Jint.Tests.Runtime.Domain;
 
 namespace Shapes
@@ -15,40 +11,40 @@ namespace Shapes
     }
 
     public class Circle : Shape
-    {
-        public class Meta
-        {
-            public Meta()
-            {
-                _description = "descp";
-            }
-          private string _description;
-
-          public string Description
-          {
-            get
-            {
-              return _description;
-            }
-            set
-            {
-              _description = value;
-            }
-          }
-
-          public enum Usage
-          {
-            Public,
-            Private,
-            Internal = 11
-          }
-        }
-
-        public enum Kind
-        {
-          Unit,
-          Ellipse,
-          Round = 5
+    {
+        public class Meta
+        {
+            public Meta()
+            {
+                _description = "descp";
+            }
+          private string _description;
+
+          public string Description
+          {
+            get
+            {
+              return _description;
+            }
+            set
+            {
+              _description = value;
+            }
+          }
+
+          public enum Usage
+          {
+            Public,
+            Private,
+            Internal = 11
+          }
+        }
+
+        public enum Kind
+        {
+          Unit,
+          Ellipse,
+          Round = 5
         }
 
         public Circle()

+ 12 - 13
Jint.Tests/Runtime/InteropTests.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Collections.Generic;
-using System.Collections.ObjectModel;
 using System.Reflection;
 using Jint.Native;
 using Jint.Native.Object;
@@ -411,8 +410,8 @@ namespace Jint.Tests.Runtime
         {
             var o = new
             {
-                x = new JsValue(1),
-                y = new JsValue("string"),
+                x = new JsNumber(1),
+                y = new JsString("string"),
             };
 
             _engine.SetValue("o", o);
@@ -427,7 +426,7 @@ namespace Jint.Tests.Runtime
         public void PocosCanReturnObjectInstanceDirectly()
         {
             var x = new ObjectInstance(_engine) { Extensible = true};
-            x.Put("foo", new JsValue("bar"), false);
+            x.Put("foo", new JsString("bar"), false);
 
             var o = new
             {
@@ -1487,7 +1486,7 @@ namespace Jint.Tests.Runtime
                         try {
                             throwMyException();
                             return;
-                        } 
+                        }
                         catch(e) {
                             return;
                         }
@@ -1497,7 +1496,7 @@ namespace Jint.Tests.Runtime
                         try {
                             new Thrower().ThrowNotSupportedException();
                             return;
-                        } 
+                        }
                         catch(e) {
                             return;
                         }
@@ -1521,7 +1520,7 @@ namespace Jint.Tests.Runtime
                         try {
                             throwMyException();
                             return '';
-                        } 
+                        }
                         catch(e) {
                             return e.message;
                         }
@@ -1531,7 +1530,7 @@ namespace Jint.Tests.Runtime
                         try {
                             new Thrower().ThrowExceptionWithMessage('myExceptionMessage');
                             return;
-                        } 
+                        }
                         catch(e) {
                             return e.message;
                         }
@@ -1556,7 +1555,7 @@ namespace Jint.Tests.Runtime
                         try {
                             throwMyException1();
                             return '';
-                        } 
+                        }
                         catch(e) {
                             return e.message;
                         }
@@ -1566,7 +1565,7 @@ namespace Jint.Tests.Runtime
                         try {
                             throwMyException2();
                             return '';
-                        } 
+                        }
                         catch(e) {
                             return e.message;
                         }
@@ -1576,7 +1575,7 @@ namespace Jint.Tests.Runtime
                         try {
                             new Thrower().ThrowNotSupportedExceptionWithMessage('myExceptionMessage');
                             return '';
-                        } 
+                        }
                         catch(e) {
                             return e.message;
                         }
@@ -1586,13 +1585,13 @@ namespace Jint.Tests.Runtime
                         try {
                             new Thrower().ThrowArgumentNullException();
                             return '';
-                        } 
+                        }
                         catch(e) {
                             return e.message;
                         }
                     }
                 ");
-            
+
             Assert.Equal(engine.Invoke("throwException1").AsString(), exceptionMessage);
             Assert.Throws<ArgumentNullException>(() => engine.Invoke("throwException2"));
             Assert.Equal(engine.Invoke("throwException3").AsString(), exceptionMessage);

+ 7 - 12
Jint.Tests/Runtime/JsValueConversionTests.cs

@@ -3,11 +3,6 @@ using Jint.Native.Array;
 using Jint.Native.Date;
 using Jint.Native.Object;
 using Jint.Native.RegExp;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using Xunit;
 
 namespace Jint.Tests.Runtime
@@ -17,7 +12,7 @@ namespace Jint.Tests.Runtime
         [Fact]
         public void ShouldBeAnArray()
         {
-            var value = new JsValue(new ArrayInstance(null));
+            var value = new ArrayInstance(null);
             Assert.Equal(false, value.IsBoolean());
             Assert.Equal(true, value.IsArray());
             Assert.Equal(false, value.IsDate());
@@ -35,7 +30,7 @@ namespace Jint.Tests.Runtime
         [Fact]
         public void ShouldBeABoolean()
         {
-            var value = new JsValue(true);
+            var value = new JsBoolean(true);
             Assert.Equal(true, value.IsBoolean());
             Assert.Equal(false, value.IsArray());
             Assert.Equal(false, value.IsDate());
@@ -53,7 +48,7 @@ namespace Jint.Tests.Runtime
         [Fact]
         public void ShouldBeADate()
         {
-            var value = new JsValue(new DateInstance(null));
+            var value = new DateInstance(null);
             Assert.Equal(false, value.IsBoolean());
             Assert.Equal(false, value.IsArray());
             Assert.Equal(true, value.IsDate());
@@ -87,7 +82,7 @@ namespace Jint.Tests.Runtime
         [Fact]
         public void ShouldBeANumber()
         {
-            var value = new JsValue(2);
+            var value = new JsNumber(2);
             Assert.Equal(false, value.IsBoolean());
             Assert.Equal(false, value.IsArray());
             Assert.Equal(false, value.IsDate());
@@ -104,7 +99,7 @@ namespace Jint.Tests.Runtime
         [Fact]
         public void ShouldBeAnObject()
         {
-            var value = new JsValue(new ObjectInstance(null));
+            var value = new ObjectInstance(null);
             Assert.Equal(false, value.IsBoolean());
             Assert.Equal(false, value.IsArray());
             Assert.Equal(false, value.IsDate());
@@ -121,7 +116,7 @@ namespace Jint.Tests.Runtime
         [Fact]
         public void ShouldBeARegExp()
         {
-            var value = new JsValue(new RegExpInstance(null));
+            var value = new RegExpInstance(null);
             Assert.Equal(false, value.IsBoolean());
             Assert.Equal(false, value.IsArray());
             Assert.Equal(false, value.IsDate());
@@ -138,7 +133,7 @@ namespace Jint.Tests.Runtime
         [Fact]
         public void ShouldBeAString()
         {
-            var value = new JsValue("a");
+            var value = new JsString("a");
             Assert.Equal(false, value.IsBoolean());
             Assert.Equal(false, value.IsArray());
             Assert.Equal(false, value.IsDate());

+ 4 - 6
Jint.Tests/Runtime/NullPropagation.cs

@@ -24,12 +24,10 @@ namespace Jint.Tests.Runtime
 
             public bool TryGetCallable(Engine engine, object reference, out JsValue value)
             {
-                value = new JsValue(
-                    new ClrFunctionInstance(engine, (thisObj, values) => thisObj)
-                );
+                value = new ClrFunctionInstance(engine, (thisObj, values) => thisObj);
                 return true;
             }
-            
+
             public bool CheckCoercible(JsValue value)
             {
                 return true;
@@ -42,8 +40,8 @@ namespace Jint.Tests.Runtime
             var engine = new Engine(cfg => cfg.SetReferencesResolver(new NullPropagationReferenceResolver()));
 
             const string Script = @"
-var input = { 
-	Address : null 
+var input = {
+	Address : null
 };
 
 var address = input.Address;

+ 0 - 10
Jint.Tests/packages.config

@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
-  <package id="Newtonsoft.Json" version="5.0.6" targetFramework="net45" />
-  <package id="xunit" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.abstractions" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.assert" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.core" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.extensibility.core" version="2.0.0" targetFramework="net45" />
-  <package id="xunit.runner.visualstudio" version="2.0.0" targetFramework="net45" />
-</packages>

+ 19 - 23
Jint/Engine.cs

@@ -51,23 +51,22 @@ namespace Jint
 
         internal static Dictionary<Type, Func<Engine, object, JsValue>> TypeMappers = new Dictionary<Type, Func<Engine, object, JsValue>>()
         {
-            { typeof(bool), (Engine engine, object v) => (bool) v ? JsValue.True : JsValue.False },
-            { typeof(byte), (Engine engine, object v) => JsValue.FromInt((byte)v) },
-            { typeof(char), (Engine engine, object v) => JsValue.FromChar((char)v) },
+            { typeof(bool), (Engine engine, object v) => (bool) v ? JsBoolean.True : JsBoolean.False },
+            { typeof(byte), (Engine engine, object v) => JsNumber.Create((byte)v) },
+            { typeof(char), (Engine engine, object v) => JsString.Create((char)v) },
             { typeof(DateTime), (Engine engine, object v) => engine.Date.Construct((DateTime)v) },
             { typeof(DateTimeOffset), (Engine engine, object v) => engine.Date.Construct((DateTimeOffset)v) },
             { typeof(decimal), (Engine engine, object v) => (JsValue) (double)(decimal)v },
             { typeof(double), (Engine engine, object v) => (JsValue)(double)v },
-            { typeof(Int16), (Engine engine, object v) => JsValue.FromInt((Int16)v) },
-            { typeof(Int32), (Engine engine, object v) => JsValue.FromInt((Int32)v) },
+            { typeof(Int16), (Engine engine, object v) => JsNumber.Create((Int16)v) },
+            { typeof(Int32), (Engine engine, object v) => JsNumber.Create((Int32)v) },
             { typeof(Int64), (Engine engine, object v) => (JsValue)(Int64)v },
-            { typeof(SByte), (Engine engine, object v) => JsValue.FromInt((SByte)v) },
+            { typeof(SByte), (Engine engine, object v) => JsNumber.Create((SByte)v) },
             { typeof(Single), (Engine engine, object v) => (JsValue)(Single)v },
             { typeof(string), (Engine engine, object v) => (JsValue) (string)v },
-            { typeof(UInt16), (Engine engine, object v) => JsValue.FromInt((UInt16)v) },
-            { typeof(UInt32), (Engine engine, object v) => JsValue.FromInt((UInt32)v) },
-            { typeof(UInt64), (Engine engine, object v) => JsValue.FromInt((UInt64)v) },
-            { typeof(JsValue), (Engine engine, object v) => (JsValue)v },
+            { typeof(UInt16), (Engine engine, object v) => JsNumber.Create((UInt16)v) },
+            { typeof(UInt32), (Engine engine, object v) => JsNumber.Create((UInt32)v) },
+            { typeof(UInt64), (Engine engine, object v) => JsNumber.Create((UInt64)v) },
             { typeof(System.Text.RegularExpressions.Regex), (Engine engine, object v) => engine.RegExp.Construct((System.Text.RegularExpressions.Regex)v, "") }
         };
 
@@ -259,17 +258,17 @@ namespace Jint
 
         public Engine SetValue(string name, double value)
         {
-            return SetValue(name, JsValue.FromDouble(value));
+            return SetValue(name, JsNumber.Create(value));
         }
 
         public Engine SetValue(string name, int value)
         {
-            return SetValue(name, JsValue.FromInt(value));
+            return SetValue(name, JsNumber.Create(value));
         }
 
         public Engine SetValue(string name, bool value)
         {
-            return SetValue(name, value ? JsValue.True : JsValue.False);
+            return SetValue(name, value ? JsBoolean.True : JsBoolean.False);
         }
 
         public Engine SetValue(string name, JsValue value)
@@ -565,7 +564,7 @@ namespace Jint
                     }
 
                     var getter = desc.Get;
-                    if (getter == Undefined.Instance)
+                    if (ReferenceEquals(getter, Undefined.Instance))
                     {
                         return Undefined.Instance;
                     }
@@ -574,17 +573,14 @@ namespace Jint
                     return callable.Call(baseValue, Arguments.Empty);
                 }
             }
-            else
-            {
-                var record = baseValue.As<EnvironmentRecord>();
 
-                if (record == null)
-                {
-                    throw new ArgumentException();
-                }
-
-                return record.GetBindingValue(reference.GetReferencedName(), reference.IsStrict());
+            var record = (EnvironmentRecord) baseValue;
+            if (record == null)
+            {
+                throw new ArgumentException();
             }
+
+            return record.GetBindingValue(reference.GetReferencedName(), reference.IsStrict());
         }
 
         /// <summary>

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

@@ -160,7 +160,7 @@ namespace Jint.Native.Argument
             if (desc.IsAccessorDescriptor())
             {
                 var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(JsValue, new[] {value});
+                setter.Call(this, new[] {value});
             }
             else
             {
@@ -194,7 +194,7 @@ namespace Jint.Native.Argument
                     }
                     else
                     {
-                        if (desc.Value != null && desc.Value != Undefined.Instance)
+                        if (desc.Value != null && desc.Value != Undefined)
                         {
                             map.Put(propertyName, desc.Value, throwOnError);
                         }

+ 0 - 10
Jint/Native/Array/ArrayExecutionContext.cs

@@ -1,5 +1,4 @@
 using System.Collections.Generic;
-using System.Text;
 using System.Threading;
 
 namespace Jint.Native.Array
@@ -13,10 +12,6 @@ namespace Jint.Native.Array
         private static readonly ThreadLocal<ArrayExecutionContext> _executionContext = new ThreadLocal<ArrayExecutionContext>(() => new ArrayExecutionContext());
 
         private List<uint> _keyCache;
-        private JsValue[] _callArray1;
-        private JsValue[] _callArray3;
-        private JsValue[] _callArray4;
-        private StringBuilder _stringBuilder;
 
         private ArrayExecutionContext()
         {
@@ -24,11 +19,6 @@ namespace Jint.Native.Array
 
         public List<uint> KeyCache => _keyCache = _keyCache ?? new List<uint>();
 
-        public JsValue[] CallArray1 => _callArray1 = _callArray1 ?? new JsValue[1];
-        public JsValue[] CallArray3 => _callArray3 = _callArray3 ?? new JsValue[3];
-        public JsValue[] CallArray4 => _callArray4 = _callArray4 ?? new JsValue[4];
-        public StringBuilder StringBuilder => _stringBuilder = _stringBuilder ?? new StringBuilder();
-
         public static ArrayExecutionContext Current => _executionContext.Value;
     }
 }

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

@@ -64,7 +64,7 @@ namespace Jint.Native.Array
             if (desc.IsAccessorDescriptor())
             {
                 var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(JsValue, new[] {value});
+                setter.Call(this, new[] {value});
             }
             else
             {
@@ -192,7 +192,7 @@ namespace Jint.Native.Array
                                 var deleteSucceeded = Delete(TypeConverter.ToString(keyIndex), false);
                                 if (!deleteSucceeded)
                                 {
-                                    newLenDesc.Value = JsValue.FromInt(keyIndex + 1);
+                                    newLenDesc.Value = JsNumber.Create(keyIndex + 1);
                                     if (!newWritable)
                                     {
                                         newLenDesc.Writable = false;

+ 42 - 33
Jint/Native/Array/ArrayPrototype.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Text;
 using Jint.Native.Object;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
@@ -13,6 +14,12 @@ namespace Jint.Native.Array
     /// </summary>
     public sealed class ArrayPrototype : ArrayInstance
     {
+        private readonly StringBuilder _arrayJoinBuilder = new StringBuilder();
+        private JsValue[] _callArray1;
+        private JsValue[] _callArray2;
+        private JsValue[] _callArray3;
+        private JsValue[] _callArray4;
+
         private ArrayPrototype(Engine engine) : base(engine)
         {
         }
@@ -119,7 +126,7 @@ namespace Jint.Native.Array
             }
 
             var k = 0;
-            JsValue accumulator = Undefined.Instance;
+            JsValue accumulator = Undefined;
             if (arguments.Length > 1)
             {
                 accumulator = initialValue;
@@ -144,7 +151,7 @@ namespace Jint.Native.Array
             }
 
 
-            var args = ArrayExecutionContext.Current.CallArray4;
+            var args = _callArray4 = _callArray4 ?? new JsValue[4];
             while (k < len)
             {
                 var i = (uint) k;
@@ -154,7 +161,7 @@ namespace Jint.Native.Array
                     args[1] = kvalue;
                     args[2] = i;
                     args[3] = o.Target;
-                    accumulator = callable.Call(Undefined.Instance, args);
+                    accumulator = callable.Call(Undefined, args);
                 }
 
                 k++;
@@ -176,7 +183,7 @@ namespace Jint.Native.Array
             var a = (ArrayInstance) Engine.Array.Construct(Arguments.Empty);
 
             uint to = 0;
-            var jsValues = ArrayExecutionContext.Current.CallArray3;
+            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -206,10 +213,10 @@ namespace Jint.Native.Array
 
             var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
 
-            var args = ArrayExecutionContext.Current.CallArray1;
+            var args = _callArray1 = _callArray1 ?? new JsValue[1];
             args[0] = len;
             var a = Engine.Array.Construct(args, len);
-            var jsValues = ArrayExecutionContext.Current.CallArray3;
+            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -235,7 +242,7 @@ namespace Jint.Native.Array
 
             var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
 
-            var jsValues = ArrayExecutionContext.Current.CallArray3;
+            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -247,7 +254,7 @@ namespace Jint.Native.Array
                 }
             }
 
-            return Undefined.Instance;
+            return Undefined;
         }
 
         private JsValue Some(JsValue thisObj, JsValue[] arguments)
@@ -260,7 +267,7 @@ namespace Jint.Native.Array
 
             var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
 
-            var jsValues = ArrayExecutionContext.Current.CallArray3;
+            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -289,7 +296,7 @@ namespace Jint.Native.Array
 
             var callable = callbackfn.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "Argument must be callable"));
 
-            var jsValues = ArrayExecutionContext.Current.CallArray3;
+            var jsValues = _callArray3 = _callArray3 ?? new JsValue[3];
             for (uint k = 0; k < len; k++)
             {
                 if (o.TryGetValue(k, out var kvalue))
@@ -300,12 +307,12 @@ namespace Jint.Native.Array
                     var testResult = callable.Call(thisArg, jsValues);
                     if (false == TypeConverter.ToBoolean(testResult))
                     {
-                        return JsValue.False;
+                        return JsBoolean.False;
                     }
                 }
             }
 
-            return JsValue.True;
+            return JsBoolean.True;
         }
 
         private JsValue IndexOf(JsValue thisObj, JsValue[] arguments)
@@ -500,31 +507,34 @@ namespace Jint.Native.Array
 
             var compareArg = arguments.At(0);
             ICallable compareFn = null;
-            if (compareArg != Undefined.Instance)
+            if (compareArg != Undefined)
             {
                 compareFn = compareArg.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "The sort argument must be a function"));
             }
 
             int Comparer(JsValue x, JsValue y)
             {
-                if (x == Undefined.Instance && y == Undefined.Instance)
+                if (ReferenceEquals(x, Undefined) && ReferenceEquals(y, Undefined))
                 {
                     return 0;
                 }
 
-                if (x == Undefined.Instance)
+                if (ReferenceEquals(x, Undefined))
                 {
                     return 1;
                 }
 
-                if (y == Undefined.Instance)
+                if (ReferenceEquals(y, Undefined))
                 {
                     return -1;
                 }
 
                 if (compareFn != null)
                 {
-                    var s = TypeConverter.ToNumber(compareFn.Call(Undefined.Instance, new[] {x, y}));
+                    var args = _callArray2 = _callArray2 ?? new JsValue[2];
+                    args[0] = x;
+                    args[1] = y;
+                    var s = TypeConverter.ToNumber(compareFn.Call(Undefined, args));
                     if (s < 0)
                     {
                         return -1;
@@ -589,7 +599,7 @@ namespace Jint.Native.Array
             }
 
             uint final;
-            if (end == Undefined.Instance)
+            if (ReferenceEquals(end, Undefined))
             {
                 final = TypeConverter.ToUint32(len);
             }
@@ -628,7 +638,7 @@ namespace Jint.Native.Array
             if (len == 0)
             {
                 o.SetLength(0);
-                return Undefined.Instance;
+                return Undefined;
             }
 
             var first = o.Get(0);
@@ -691,7 +701,7 @@ namespace Jint.Native.Array
             var separator = arguments.At(0);
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
-            if (separator == Undefined.Instance)
+            if (ReferenceEquals(separator, Undefined))
             {
                 separator = ",";
             }
@@ -706,7 +716,7 @@ namespace Jint.Native.Array
 
             string StringFromJsValue(JsValue value)
             {
-                return value == Undefined.Instance || value == Null.Instance
+                return ReferenceEquals(value, Undefined) || ReferenceEquals(value, Null)
                     ? ""
                     : TypeConverter.ToString(value);
             }
@@ -717,16 +727,15 @@ namespace Jint.Native.Array
                 return s;
             }
 
-            var sb = ArrayExecutionContext.Current.StringBuilder;
-            sb.Clear();
-            sb.Append(s);
+            _arrayJoinBuilder.Clear();
+            _arrayJoinBuilder.Append(s);
             for (uint k = 1; k < len; k++)
             {
-                sb.Append(sep);
-                sb.Append(StringFromJsValue(o.Get(k)));
+                _arrayJoinBuilder.Append(sep);
+                _arrayJoinBuilder.Append(StringFromJsValue(o.Get(k)));
             }
 
-            return sb.ToString();
+            return _arrayJoinBuilder.ToString();
         }
 
         private JsValue ToLocaleString(JsValue thisObj, JsValue[] arguments)
@@ -740,7 +749,7 @@ namespace Jint.Native.Array
             }
 
             JsValue r;
-            if (!array.TryGetValue(0, out var firstElement) || firstElement == Null.Instance || firstElement == Undefined.Instance)
+            if (!array.TryGetValue(0, out var firstElement) || ReferenceEquals(firstElement, Null) || ReferenceEquals(firstElement, Undefined))
             {
                 r = "";
             }
@@ -755,7 +764,7 @@ namespace Jint.Native.Array
             for (uint k = 1; k < len; k++)
             {
                 string s = r + separator;
-                if (array.TryGetValue(k, out var nextElement) == Undefined.Instance || nextElement == Null.Instance)
+                if (!array.TryGetValue(k, out var nextElement) || ReferenceEquals(nextElement, Null))
                 {
                     r = "";
                 }
@@ -844,7 +853,7 @@ namespace Jint.Native.Array
             }
 
             int k = (int) len - 1;
-            JsValue accumulator = Undefined.Instance;
+            JsValue accumulator = Undefined;
             if (arguments.Length > 1)
             {
                 accumulator = initialValue;
@@ -877,7 +886,7 @@ namespace Jint.Native.Array
                 if (kPresent)
                 {
                     var kvalue = o.Get(pk);
-                    accumulator = callable.Call(Undefined.Instance, new[] {accumulator, kvalue, k, o});
+                    accumulator = callable.Call(Undefined, new[] {accumulator, kvalue, k, o});
                 }
             }
 
@@ -921,7 +930,7 @@ namespace Jint.Native.Array
             if (len == 0)
             {
                 o.SetLength(0);
-                return Undefined.Instance;
+                return Undefined;
             }
 
             len = len - 1;
@@ -993,7 +1002,7 @@ namespace Jint.Native.Array
                         return TypeConverter.ToUint32(desc.Value);
                     }
 
-                    var getter = desc.Get != null ? desc.Get : Undefined.Instance;
+                    var getter = desc.Get != null ? desc.Get : Undefined;
 
                     if (getter.IsUndefined())
                     {

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

@@ -131,7 +131,7 @@ namespace Jint.Native.Date
                 var v = TypeConverter.ToPrimitive(arguments[0]);
                 if (v.IsString())
                 {
-                    return Construct(Parse(Undefined.Instance, Arguments.From(v)).AsNumber());
+                    return Construct(Parse(Undefined, Arguments.From(v)).AsNumber());
                 }
 
                 return Construct(TypeConverter.ToNumber(v));

+ 1 - 1
Jint/Native/Error/ErrorConstructor.cs

@@ -47,7 +47,7 @@ namespace Jint.Native.Error
             instance.Prototype = PrototypeObject;
             instance.Extensible = true;
 
-            if (arguments.At(0) != Undefined.Instance)
+            if (arguments.At(0) != Undefined)
             {
                 instance.Put("message", TypeConverter.ToString(arguments.At(0)), false);
             }

+ 1 - 1
Jint/Native/Error/ErrorPrototype.cs

@@ -51,7 +51,7 @@ namespace Jint.Native.Error
 
             var msgProp = o.Get("message");
             string msg;
-            if (msgProp == Undefined.Instance)
+            if (ReferenceEquals(msgProp, Undefined))
             {
                 msg = "";
             }

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

@@ -161,7 +161,7 @@ namespace Jint.Native.Function
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            if (argArray == Null.Instance || argArray == Undefined.Instance)
+            if (ReferenceEquals(argArray, Undefined) || ReferenceEquals(argArray, Undefined))
             {
                 return func.Call(thisArg, Arguments.Empty);
             }

+ 2 - 2
Jint/Native/Function/FunctionPrototype.cs

@@ -94,7 +94,7 @@ namespace Jint.Native.Function
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            if (argArray == Null.Instance || argArray == Undefined.Instance)
+            if (ReferenceEquals(argArray, Null) || ReferenceEquals(argArray, Undefined))
             {
                 return func.Call(thisArg, Arguments.Empty);
             }
@@ -130,7 +130,7 @@ namespace Jint.Native.Function
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
-            return Undefined.Instance;
+            return Undefined;
         }
     }
 }

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

@@ -10,7 +10,7 @@ namespace Jint.Native.Function
 
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
-            return Undefined.Instance;
+            return Undefined;
         }
     }
 }

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

@@ -31,7 +31,7 @@ namespace Jint.Native.Function
             Extensible = true;
             Prototype = engine.Function.PrototypeObject;
 
-            DefineOwnProperty("length", new AllForbiddenPropertyDescriptor(JsValue.FromInt(FormalParameters.Length)), false);
+            DefineOwnProperty("length", new AllForbiddenPropertyDescriptor(JsNumber.Create(FormalParameters.Length)), false);
 
             var proto = engine.Object.Construct(Arguments.Empty);
             proto.SetOwnProperty("constructor", new NonEnumerablePropertyDescriptor(this));
@@ -85,7 +85,7 @@ namespace Jint.Native.Function
                 {
                     thisBinding = thisArg;
                 }
-                else if (thisArg == Undefined.Instance || thisArg == Null.Instance)
+                else if (ReferenceEquals(thisArg, Undefined) || ReferenceEquals(thisArg, Null))
                 {
                     thisBinding = Engine.Global;
                 }
@@ -130,7 +130,7 @@ namespace Jint.Native.Function
                     Engine.LeaveExecutionContext();
                 }
 
-                return Undefined.Instance;
+                return Undefined;
             }
         }
 
@@ -154,7 +154,5 @@ namespace Jint.Native.Function
 
             return obj;
         }
-
-        public ObjectInstance PrototypeObject { get; private set; }
     }
-}
+}

+ 34 - 19
Jint/Native/Global/GlobalObject.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Globalization;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using System.Text;
 using Jint.Native.Object;
 using Jint.Native.String;
@@ -11,6 +12,8 @@ namespace Jint.Native.Global
 {
     public sealed class GlobalObject : ObjectInstance
     {
+        private readonly StringBuilder _stringBuilder = new StringBuilder();
+
         private GlobalObject(Engine engine) : base(engine)
         {
         }
@@ -50,7 +53,7 @@ namespace Jint.Native.Global
 
             FastAddProperty("NaN", double.NaN, false, false, false);
             FastAddProperty("Infinity", double.PositiveInfinity, false, false, false);
-            FastAddProperty("undefined", Undefined.Instance, false, false, false);
+            FastAddProperty("undefined", Undefined, false, false, false);
 
             // Global object functions
             FastAddProperty("parseInt", new ClrFunctionInstance(Engine, ParseInt, 2), true, false, true);
@@ -397,13 +400,16 @@ namespace Jint.Native.Global
         private string Encode(string uriString, char[] unescapedUriSet)
         {
             var strLen = uriString.Length;
-            var r = new StringBuilder(uriString.Length);
+
+            _stringBuilder.EnsureCapacity(uriString.Length);
+            _stringBuilder.Clear();
+
             for (var k = 0; k < strLen; k++)
             {
                 var c = uriString[k];
                 if (System.Array.IndexOf(unescapedUriSet, c) != -1)
                 {
-                    r.Append(c);
+                    _stringBuilder.Append(c);
                 }
                 else
                 {
@@ -489,12 +495,12 @@ namespace Jint.Native.Global
                         var jOctet = octets[j];
                         var x1 = HexaMap[jOctet / 16];
                         var x2 = HexaMap[jOctet % 16];
-                        r.Append('%').Append(x1).Append(x2);
+                        _stringBuilder.Append('%').Append(x1).Append(x2);
                     }
                 }
             }
 
-            return r.ToString();
+            return _stringBuilder.ToString();
         }
 
         public JsValue DecodeUri(JsValue thisObject, JsValue[] arguments)
@@ -515,13 +521,16 @@ namespace Jint.Native.Global
         public string Decode(string uriString, char[] reservedSet)
         {
             var strLen = uriString.Length;
-            var R = new StringBuilder(strLen);
+
+            _stringBuilder.EnsureCapacity(strLen);
+            _stringBuilder.Clear();
+
             for (var k = 0; k < strLen; k++)
             {
                 var C = uriString[k];
                 if (C != '%')
                 {
-                    R.Append(C);
+                    _stringBuilder.Append(C);
                 }
                 else
                 {
@@ -544,11 +553,11 @@ namespace Jint.Native.Global
                         C = (char)B;
                         if (System.Array.IndexOf(reservedSet, C) == -1)
                         {
-                            R.Append(C);
+                            _stringBuilder.Append(C);
                         }
                         else
                         {
-                            R.Append(uriString.Substring(start, k - start + 1));
+                            _stringBuilder.Append(uriString, start, k - start + 1);
                         }
                     }
                     else
@@ -595,12 +604,12 @@ namespace Jint.Native.Global
                             Octets[j] = B;
                         }
 
-                        R.Append(Encoding.UTF8.GetString(Octets, 0, Octets.Length));
+                        _stringBuilder.Append(Encoding.UTF8.GetString(Octets, 0, Octets.Length));
                     }
                 }
             }
 
-            return R.ToString();
+            return _stringBuilder.ToString();
         }
 
         /// <summary>
@@ -612,25 +621,28 @@ namespace Jint.Native.Global
             var uriString = TypeConverter.ToString(arguments.At(0));
 
             var strLen = uriString.Length;
-            var r = new StringBuilder(strLen);
+
+            _stringBuilder.EnsureCapacity(strLen);
+            _stringBuilder.Clear();
+
             for (var k = 0; k < strLen; k++)
             {
                 var c = uriString[k];
                 if (whiteList.IndexOf(c) != -1)
                 {
-                    r.Append(c);
+                    _stringBuilder.Append(c);
                 }
                 else if (c < 256)
                 {
-                    r.Append(string.Format("%{0}", ((int)c).ToString("X2")));
+                    _stringBuilder.Append(string.Format("%{0}", ((int)c).ToString("X2")));
                 }
                 else
                 {
-                    r.Append(string.Format("%u{0}", ((int)c).ToString("X4")));
+                    _stringBuilder.Append(string.Format("%u{0}", ((int)c).ToString("X4")));
                 }
             }
 
-            return r.ToString();
+            return _stringBuilder.ToString();
         }
 
         /// <summary>
@@ -641,7 +653,10 @@ namespace Jint.Native.Global
             var uriString = TypeConverter.ToString(arguments.At(0));
 
             var strLen = uriString.Length;
-            var r = new StringBuilder(strLen);
+
+            _stringBuilder.EnsureCapacity(strLen);
+            _stringBuilder.Clear();
+
             for (var k = 0; k < strLen; k++)
             {
                 var c = uriString[k];
@@ -667,10 +682,10 @@ namespace Jint.Native.Global
                         k += 2;
                     }
                 }
-                r.Append(c);
+                _stringBuilder.Append(c);
             }
 
-            return r.ToString();
+            return _stringBuilder.ToString();
         }
     }
 }

+ 72 - 0
Jint/Native/JsBoolean.cs

@@ -0,0 +1,72 @@
+using System;
+using System.Diagnostics.Contracts;
+using Jint.Runtime;
+
+namespace Jint.Native
+{
+    public sealed class JsBoolean : JsValue, IEquatable<JsBoolean>
+    {
+        private readonly bool _value;
+
+        public static readonly JsValue False = new JsBoolean(false);
+        public static readonly JsValue True = new JsBoolean(true);
+
+        public JsBoolean(bool value)
+        {
+            _value = value;
+        }
+
+        public override Types Type => Types.Boolean;
+
+        [Pure]
+        public override bool AsBoolean()
+        {
+            return _value;
+        }
+
+        public override object ToObject()
+        {
+            return _value;
+        }
+
+        public override string ToString()
+        {
+            return _value ? bool.TrueString : bool.FalseString;
+        }
+
+        public override bool Equals(JsValue obj)
+        {
+            if (ReferenceEquals(null, obj))
+            {
+                return false;
+            }
+
+            if (!(obj is JsBoolean number))
+            {
+                return false;
+            }
+
+            return Equals(number);
+        }
+
+        public bool Equals(JsBoolean other)
+        {
+            if (ReferenceEquals(null, other))
+            {
+                return false;
+            }
+
+            if (ReferenceEquals(this, other))
+            {
+                return true;
+            }
+
+            return _value == other._value;
+        }
+
+        public override int GetHashCode()
+        {
+            return _value.GetHashCode();
+        }
+    }
+}

+ 49 - 0
Jint/Native/JsNull.cs

@@ -0,0 +1,49 @@
+using System;
+using Jint.Runtime;
+
+namespace Jint.Native
+{
+    public sealed class JsNull : JsValue, IEquatable<JsNull>
+    {
+        internal JsNull()
+        {
+        }
+
+        public override Types Type => Types.Null;
+
+        public override object ToObject()
+        {
+            return null;
+        }
+
+        public override string ToString()
+        {
+            return "null";
+        }
+
+        public override bool Equals(JsValue obj)
+        {
+            if (ReferenceEquals(this, obj))
+            {
+                return true;
+            }
+
+            if (!(obj is JsNull s))
+            {
+                return false;
+            }
+
+            return Equals(s);
+        }
+
+        public bool Equals(JsNull other)
+        {
+            if (ReferenceEquals(null, other))
+            {
+                return false;
+            }
+
+            return true;
+        }
+    }
+}

+ 173 - 0
Jint/Native/JsNumber.cs

@@ -0,0 +1,173 @@
+using System;
+using System.Diagnostics.Contracts;
+using Jint.Runtime;
+
+namespace Jint.Native
+{
+    public sealed class JsNumber : JsValue, IEquatable<JsNumber>
+    {
+        private readonly double _value;
+
+        // how many decimals to check when determining if double is actually an int
+        private const double DoubleIsIntegerTolerance = double.Epsilon * 100;
+
+        private static readonly long NegativeZeroBits = BitConverter.DoubleToInt64Bits(-0.0);
+
+        // we can cache most common values, doubles are used in indexing too at times so we also cache
+        // integer values converted to doubles
+        private const int NumbersMax = 1024 * 10;
+        private static readonly JsNumber[] _doubleToJsValue = new JsNumber[NumbersMax];
+        private static readonly JsNumber[] _intToJsValue = new JsNumber[NumbersMax];
+
+        private static readonly JsNumber DoubleNaN = new JsNumber(double.NaN);
+        private static readonly JsNumber DoubleNegativeOne = new JsNumber((double) -1);
+        private static readonly JsNumber DoublePositiveInfinity = new JsNumber(double.PositiveInfinity);
+        private static readonly JsNumber DoubleNegativeInfinity = new JsNumber(double.NegativeInfinity);
+        private static readonly JsNumber IntegerNegativeOne = new JsNumber(-1);
+
+        static JsNumber()
+        {
+            for (int i = 0; i < NumbersMax; i++)
+            {
+                _intToJsValue[i] = new JsNumber(i);
+                _doubleToJsValue[i] = new JsNumber((double) i);
+            }
+        }
+
+        public JsNumber(double value)
+        {
+            _value = value;
+        }
+
+        public JsNumber(int value)
+        {
+            _value = value;
+        }
+
+        public JsNumber(uint value)
+        {
+            _value = value;
+        }
+
+        public override Types Type => Types.Number;
+
+        [Pure]
+        public override double AsNumber()
+        {
+            return _value;
+        }
+
+        public override object ToObject()
+        {
+            return _value;
+        }
+
+        internal static JsNumber Create(double value)
+        {
+            // we can cache positive double zero, but not negative, -0 == 0 in C# but in JS it's a different story
+            if ((value == 0 && BitConverter.DoubleToInt64Bits(value) != NegativeZeroBits || value >= 1)
+                && value < _doubleToJsValue.Length
+                && System.Math.Abs(value % 1) <= DoubleIsIntegerTolerance)
+            {
+                return _doubleToJsValue[(int) value];
+            }
+
+            if (value == -1)
+            {
+                return DoubleNegativeOne;
+            }
+
+            if (value == double.NegativeInfinity)
+            {
+                return DoubleNegativeInfinity;
+            }
+
+            if (value == double.PositiveInfinity)
+            {
+                return DoublePositiveInfinity;
+            }
+
+            if (double.IsNaN(value))
+            {
+                return DoubleNaN;
+            }
+
+            return new JsNumber(value);
+        }
+
+        internal static JsNumber Create(int value)
+        {
+            if (value >= 0 && value < _intToJsValue.Length)
+            {
+                return _intToJsValue[value];
+            }
+
+            if (value == -1)
+            {
+                return IntegerNegativeOne;
+            }
+
+            return new JsNumber(value);
+        }
+
+        internal static JsNumber Create(uint value)
+        {
+            if (value >= 0 && value < _intToJsValue.Length)
+            {
+                return _intToJsValue[value];
+            }
+
+            return new JsNumber(value);
+        }
+
+        internal static JsNumber Create(ulong value)
+        {
+            if (value >= 0 && value < (ulong) _intToJsValue.Length)
+            {
+                return _intToJsValue[value];
+            }
+
+            return new JsNumber(value);
+        }
+
+        public override string ToString()
+        {
+            return _value.ToString();
+        }
+
+        public override bool Equals(JsValue obj)
+        {
+            if (ReferenceEquals(null, obj))
+            {
+                return false;
+            }
+
+            if (!(obj is JsNumber number))
+            {
+                return false;
+            }
+
+            return Equals(number);
+        }
+
+        public bool Equals(JsNumber other)
+        {
+            if (ReferenceEquals(null, other))
+            {
+                return false;
+            }
+
+            if (ReferenceEquals(this, other))
+            {
+                return true;
+            }
+
+            return _value == other._value;
+        }
+
+        public override int GetHashCode()
+        {
+            return _value.GetHashCode();
+        }
+    }
+}

+ 210 - 0
Jint/Native/JsString.cs

@@ -0,0 +1,210 @@
+using System;
+using System.Diagnostics.Contracts;
+using System.Text;
+using Jint.Runtime;
+
+namespace Jint.Native
+{
+    public class JsString : JsValue, IEquatable<JsString>
+    {
+        private const int AsciiMax = 126;
+        private static readonly JsString[] _charToJsValue;
+        private static readonly JsString[] _charToStringJsValue;
+
+        private static readonly JsString Empty = new JsString("");
+        private static readonly JsString NullString = new JsString("null");
+
+        private string _value;
+
+        static JsString()
+        {
+            _charToJsValue = new JsString[AsciiMax + 1];
+            _charToStringJsValue = new JsString[AsciiMax + 1];
+
+            for (int i = 0; i <= AsciiMax; i++)
+            {
+                _charToJsValue[i] = new JsString((char) i);
+                _charToStringJsValue[i] = new JsString(((char) i).ToString());
+            }
+        }
+
+        public JsString(string value)
+        {
+            _value = value;
+        }
+
+        public override object ToObject()
+        {
+            return _value;
+        }
+
+        public JsString(char value)
+        {
+            _value = value.ToString();
+        }
+
+        public override Types Type => Types.String;
+
+        [Pure]
+        public override string AsString()
+        {
+            if (_value == null)
+            {
+                throw new ArgumentException("The value is not defined");
+            }
+
+            return _value;
+        }
+
+        public virtual JsString Append(JsValue jsValue)
+        {
+            return new ConcatenatedString(string.Concat(_value, TypeConverter.ToString(jsValue)));
+        }
+
+        internal virtual JsString EnsureCapacity(int capacity)
+        {
+            return new ConcatenatedString(_value, capacity);
+        }
+
+        internal static JsString Create(string value)
+        {
+            if (value.Length <= 1)
+            {
+                if (value == "")
+                {
+                    return Empty;
+                }
+
+                if (value.Length == 1)
+                {
+                    if (value[0] >= 0 && value[0] <= AsciiMax)
+                    {
+                        return _charToStringJsValue[value[0]];
+                    }
+                }
+            }
+            else if (value == Native.Null.Text)
+            {
+                return NullString;
+            }
+
+            return new JsString(value);
+        }
+
+        internal static JsString Create(char value)
+        {
+            if (value >= 0 && value <= AsciiMax)
+            {
+                return _charToJsValue[value];
+            }
+
+            return new JsString(value);
+        }
+
+        public override string ToString()
+        {
+            return _value;
+        }
+
+        public override bool Equals(JsValue obj)
+        {
+            if (ReferenceEquals(null, obj))
+            {
+                return false;
+            }
+
+            if (!(obj is JsString s))
+            {
+                return false;
+            }
+
+            return Equals(s);
+        }
+
+        public bool Equals(JsString other)
+        {
+            if (ReferenceEquals(null, other))
+            {
+                return false;
+            }
+
+            if (ReferenceEquals(this, other))
+            {
+                return true;
+            }
+
+            return _value == other._value;
+        }
+
+        internal sealed class ConcatenatedString : JsString
+        {
+            private StringBuilder _stringBuilder;
+            private bool _dirty;
+
+            internal ConcatenatedString(string value, int capacity = 0) : base(value)
+            {
+                if (capacity > 0)
+                {
+                    _stringBuilder = new StringBuilder(value, capacity);
+                }
+                else
+                {
+                    _value = value;
+                }
+            }
+
+            [Pure]
+            public override string AsString()
+            {
+                if (_dirty)
+                {
+                    _value = _stringBuilder.ToString();
+                    _dirty = false;
+                }
+
+                return _value;
+            }
+
+            public override JsString Append(JsValue jsValue)
+            {
+                var value = TypeConverter.ToString(jsValue);
+                if (_stringBuilder == null)
+                {
+                    _stringBuilder = new StringBuilder(_value, _value.Length + value.Length);
+                }
+
+                _stringBuilder.Append(value);
+                _dirty = true;
+
+                return this;
+            }
+
+            internal override JsString EnsureCapacity(int capacity)
+            {
+                _stringBuilder.EnsureCapacity(capacity);
+                return this;
+            }
+
+            public override bool Equals(JsValue other)
+            {
+                if (other is ConcatenatedString cs)
+                {
+                    return _stringBuilder.Equals(cs._stringBuilder);
+                }
+
+                if (other.Type == Types.String)
+                {
+                    var otherString = other.AsString();
+                    if (otherString.Length != _stringBuilder.Length)
+                    {
+                        return false;
+                    }
+
+                    return AsString().Equals(otherString);
+                }
+
+                return base.Equals(other);
+            }
+        }
+    }
+}

+ 70 - 0
Jint/Native/JsSymbol.cs

@@ -0,0 +1,70 @@
+using System;
+using Jint.Runtime;
+
+namespace Jint.Native
+{
+    /// <summary>
+    /// The _object value of a <see cref="JsSymbol"/> is the [[Description]] internal slot.
+    /// </summary>
+    public sealed class JsSymbol : JsValue, IEquatable<JsSymbol>
+    {
+        private readonly string _value;
+
+        public JsSymbol(string value)
+        {
+            _value = value;
+        }
+
+        public override Types Type => Types.Symbol;
+
+        public override object ToObject()
+        {
+            return _value;
+        }
+
+        public override string AsSymbol()
+        {
+            if (_value == null)
+            {
+                throw new ArgumentException("The value is not defined");
+            }
+
+            return _value;
+        }
+
+        public override bool Equals(JsValue obj)
+        {
+            if (ReferenceEquals(null, obj))
+            {
+                return false;
+            }
+
+            if (!(obj is JsBoolean number))
+            {
+                return false;
+            }
+
+            return Equals(number);
+        }
+
+        public bool Equals(JsSymbol other)
+        {
+            if (ReferenceEquals(null, other))
+            {
+                return false;
+            }
+
+            if (ReferenceEquals(this, other))
+            {
+                return true;
+            }
+
+            return _value == other._value;
+        }
+
+        public override int GetHashCode()
+        {
+            return _value.GetHashCode();
+        }
+    }
+}

+ 49 - 0
Jint/Native/JsUndefined.cs

@@ -0,0 +1,49 @@
+using System;
+using Jint.Runtime;
+
+namespace Jint.Native
+{
+    public sealed class JsUndefined : JsValue, IEquatable<JsUndefined>
+    {
+        internal JsUndefined()
+        {
+        }
+
+        public override Types Type => Types.Undefined;
+
+        public override object ToObject()
+        {
+            return null;
+        }
+
+        public override string ToString()
+        {
+            return "undefined";
+        }
+
+        public override bool Equals(JsValue obj)
+        {
+            if (ReferenceEquals(this, obj))
+            {
+                return true;
+            }
+
+            if (!(obj is JsUndefined s))
+            {
+                return false;
+            }
+
+            return Equals(s);
+        }
+
+        public bool Equals(JsUndefined other)
+        {
+            if (ReferenceEquals(null, other))
+            {
+                return false;
+            }
+
+            return true;
+        }
+    }
+}

+ 76 - 532
Jint/Native/JsValue.cs

@@ -2,277 +2,134 @@
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.Contracts;
-using System.Dynamic;
-using System.Runtime.CompilerServices;
 using System.Threading;
 using Jint.Native.Array;
-using Jint.Native.Boolean;
 using Jint.Native.Date;
-using Jint.Native.Function;
-using Jint.Native.Number;
 using Jint.Native.Object;
 using Jint.Native.RegExp;
-using Jint.Native.String;
 using Jint.Runtime;
 using Jint.Runtime.Interop;
 
 namespace Jint.Native
 {
     [DebuggerTypeProxy(typeof(JsValueDebugView))]
-    public class JsValue : IEquatable<JsValue>
+    public abstract class JsValue : IEquatable<JsValue>
     {
-        // how many decimals to check when determining if double is actually an int
-        private const double DoubleIsIntegerTolerance = double.Epsilon * 100;
-
-        private static readonly long NegativeZeroBits = BitConverter.DoubleToInt64Bits(-0.0);
-
-        // we can cache most common values, doubles are used in indexing too at times so we also cache
-        // integer values converted to doubles
-        private const int NumbersMax = 1024 * 10;
-        private static readonly JsValue[] _doubleToJsValue = new JsValue[NumbersMax];
-        private static readonly JsValue[] _intToJsValue = new JsValue[NumbersMax];
-
-        private const int AsciiMax = 126;
-        private static readonly JsValue[] _charToJsValue = new JsValue[AsciiMax + 1];
-        private static readonly JsValue[] _charToStringJsValue = new JsValue[AsciiMax + 1];
-
-        private static readonly JsValue EmptyString = new JsValue("");
-        private static readonly JsValue NullString = new JsValue("null");
-
-        public static readonly JsValue Undefined = new JsValue(Types.Undefined);
-        public static readonly JsValue Null = new JsValue(Types.Null);
-        public static readonly JsValue False = new JsValue(false);
-        public static readonly JsValue True = new JsValue(true);
-
-        private static readonly JsValue DoubleNaN = new JsValue(double.NaN);
-        private static readonly JsValue DoubleNegativeOne = new JsValue((double) -1);
-        private static readonly JsValue DoublePositiveInfinity= new JsValue(double.PositiveInfinity);
-        private static readonly JsValue DoubleNegativeInfinity = new JsValue(double.NegativeInfinity);
-        private static readonly JsValue IntegerNegativeOne = new JsValue(-1);
-
-        private readonly double _double;
-        private readonly object _object;
-        protected Types _type;
-
-        static JsValue()
-        {
-            for (int i = 0; i < NumbersMax; i++)
-            {
-                _intToJsValue[i] = new JsValue(i);
-                _doubleToJsValue[i] = new JsValue((double) i);
-            }
-
-            for (int i = 0; i <= AsciiMax; i++)
-            {
-                _charToJsValue[i] = new JsValue((char) i);
-                _charToStringJsValue[i] = new JsValue(((char) i).ToString());
-            }
-        }
-
-        public JsValue(bool value)
-        {
-            _double = value ? 1.0 : 0.0;
-            _object = null;
-            _type = Types.Boolean;
-        }
-
-        public JsValue(double value)
-        {
-            _object = null;
-            _type = Types.Number;
-
-            _double = value;
-        }
-
-        public JsValue(int value)
-        {
-            _object = null;
-            _type = Types.Number;
-
-            _double = value;
-        }
-
-        public JsValue(uint value)
-        {
-            _object = null;
-            _type = Types.Number;
-
-            _double = value;
-        }
-
-        public JsValue(char value)
-        {
-            _double = double.NaN;
-            _object = value;
-            _type = Types.String;
-        }
-
-        public JsValue(string value)
-        {
-            _double = double.NaN;
-            _object = value;
-            _type = Types.String;
-        }
-
-        public JsValue(ObjectInstance value)
-        {
-            _double = double.NaN;
-            _type = Types.Object;
-
-            _object = value;
-        }
-
-        public JsValue(Completion value)
-        {
-            _double = double.NaN;
-            _type = Types.Completion;
-
-            _object = value;
-        }
-
-        private JsValue(Types type)
-        {
-            _double = double.NaN;
-            _object = null;
-            _type = type;
-        }
+        public static readonly JsValue Undefined = new JsUndefined();
+        public static readonly JsValue Null = new JsNull();
 
         [Pure]
         public bool IsPrimitive()
         {
-            return _type != Types.Object && _type != Types.None;
+            return Type != Types.Object && Type != Types.None;
         }
 
         [Pure]
         public bool IsUndefined()
         {
-            return _type == Types.Undefined;
+            return Type == Types.Undefined;
         }
 
         [Pure]
-        public bool IsArray()
+        public virtual bool IsArray()
         {
-            return _type == Types.Object && _object is ArrayInstance;
+            return false;
         }
 
         [Pure]
-        public bool IsDate()
+        public virtual bool IsDate()
         {
-            return _type == Types.Object && _object is DateInstance;
+            return false;
         }
 
         [Pure]
-        public bool IsRegExp()
+        public virtual bool IsRegExp()
         {
-            return _type == Types.Object && _object is RegExpInstance;
+            return false;
         }
 
         [Pure]
         public bool IsObject()
         {
-            return _type == Types.Object;
+            return Type == Types.Object;
         }
 
         [Pure]
         public bool IsString()
         {
-            return _type == Types.String;
+            return Type == Types.String;
         }
 
         [Pure]
         public bool IsNumber()
         {
-            return _type == Types.Number;
+            return Type == Types.Number;
         }
 
         [Pure]
         public bool IsBoolean()
         {
-            return _type == Types.Boolean;
+            return Type == Types.Boolean;
         }
 
         [Pure]
         public bool IsNull()
         {
-            return _type == Types.Null;
+            return Type == Types.Null;
         }
 
         [Pure]
         public bool IsCompletion()
         {
-            return _type == Types.Completion;
+            return Type == Types.Completion;
         }
 
         [Pure]
         public bool IsSymbol()
         {
-            return _type == Types.Symbol;
+            return Type == Types.Symbol;
         }
 
         [Pure]
-        public ObjectInstance AsObject()
+        public virtual ObjectInstance AsObject()
         {
-            if (_type != Types.Object)
-            {
-                throw new ArgumentException("The value is not an object");
-            }
-
-            return _object as ObjectInstance;
+            throw new ArgumentException("The value is not an object");
         }
 
         [Pure]
-        public TInstance AsInstance<TInstance>() where TInstance : class
+        public virtual TInstance AsInstance<TInstance>() where TInstance : class
         {
-            if (_type != Types.Object)
-            {
-                throw new ArgumentException("The value is not an object");
-            }
-
-            return _object as TInstance;
+            throw new ArgumentException("The value is not an object");
         }
 
         [Pure]
-        public ArrayInstance AsArray()
+        public virtual ArrayInstance AsArray()
         {
-            if (!IsArray())
-            {
-                throw new ArgumentException("The value is not an array");
-            }
-
-            return _object as ArrayInstance;
+            throw new ArgumentException("The value is not an array");
         }
 
         [Pure]
-        public DateInstance AsDate()
+        public virtual DateInstance AsDate()
         {
-            if (!IsDate())
-            {
-                throw new ArgumentException("The value is not a date");
-            }
-
-            return _object as DateInstance;
+            throw new ArgumentException("The value is not a date");
         }
 
         [Pure]
-        public RegExpInstance AsRegExp()
+        public virtual RegExpInstance AsRegExp()
         {
-            if (!IsRegExp())
-            {
-                throw new ArgumentException("The value is not a date");
-            }
-
-            return _object as RegExpInstance;
+            throw new ArgumentException("The value is not a date");
         }
 
         [Pure]
-        public Completion AsCompletion()
+        public virtual Completion AsCompletion()
         {
-            if (_type != Types.Completion)
+            if (Type != Types.Completion)
             {
                 throw new ArgumentException("The value is not a completion record");
             }
 
-            return (Completion)_object;
+            // TODO not implemented
+            return null;
         }
 
         [Pure]
@@ -293,177 +150,41 @@ namespace Jint.Native
             return null;
         }
 
-        public bool Is<T>()
+        public virtual bool Is<T>()
         {
-            return _type == Types.Object && _object is T;
+            return false;
         }
 
-        public T As<T>() where T : ObjectInstance
+        public virtual T As<T>() where T : ObjectInstance
         {
-            return _object as T;
+            return null;
         }
 
         [Pure]
-        public bool AsBoolean()
+        public virtual bool AsBoolean()
         {
-            if (_type != Types.Boolean)
-            {
-                throw new ArgumentException("The value is not a boolean");
-            }
-
-            return _double != 0;
+            throw new ArgumentException("The value is not a boolean");
         }
 
         [Pure]
-        public string AsString()
+        public virtual string AsString()
         {
-            if (_type != Types.String)
-            {
-                throw new ArgumentException("The value is not a string");
-            }
-
-            if (_object == null)
-            {
-                throw new ArgumentException("The value is not defined");
-            }
-
-            return (string)_object;
+            throw new ArgumentException("The value is not a string");
         }
 
         [Pure]
-        public string AsSymbol()
+        public virtual string AsSymbol()
         {
-            if (_type != Types.Symbol)
-            {
-                throw new ArgumentException("The value is not a symbol");
-            }
-
-            if (_object == null)
-            {
-                throw new ArgumentException("The value is not defined");
-            }
-
-            return (string)_object;
+            throw new ArgumentException("The value is not a symbol");
         }
 
         [Pure]
-        public double AsNumber()
-        {
-            if (_type != Types.Number)
-            {
-                throw new ArgumentException("The value is not a number");
-            }
-
-            return _double;
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public bool Equals(JsValue other)
-        {
-            if (other == null)
-            {
-                return false;
-            }
-
-            if(ReferenceEquals(this, other))
-            {
-                return true;
-            }
-
-            if (_type != other._type)
-            {
-                return false;
-            }
-
-            switch (_type)
-            {
-                case Types.None:
-                    return false;
-                case Types.Undefined:
-                    return true;
-                case Types.Null:
-                    return true;
-                case Types.Boolean:
-                case Types.Number:
-                    return _double == other._double;
-                case Types.String:
-                case Types.Object:
-                    return _object == other._object;
-                default:
-                    throw new ArgumentOutOfRangeException();
-            }
-        }
-
-        public Types Type => _type;
-
-        internal static JsValue FromDouble(double value)
-        {
-            // we can cache positive double zero, but not negative, -0 == 0 in C# but in JS it's a different story
-            if ((value == 0 && BitConverter.DoubleToInt64Bits(value) != NegativeZeroBits || value >= 1)
-                && value < _doubleToJsValue.Length
-                && System.Math.Abs(value % 1) <= DoubleIsIntegerTolerance)
-            {
-                return _doubleToJsValue[(int) value];
-            }
-            if (value == -1)
-            {
-                return DoubleNegativeOne;
-            }
-            if (value == double.NegativeInfinity)
-            {
-                return DoubleNegativeInfinity;
-            }
-            if (value == double.PositiveInfinity)
-            {
-                return DoublePositiveInfinity;
-            }
-            if (double.IsNaN(value))
-            {
-                return DoubleNaN;
-            }
-
-            return new JsValue(value);
-        }
-
-        internal static JsValue FromInt(int value)
-        {
-            if (value >= 0 && value < _intToJsValue.Length)
-            {
-                return _intToJsValue[value];
-            }
-            if (value == -1)
-            {
-                return IntegerNegativeOne;
-            }
-            return new JsValue(value);
-        }
-
-        internal static JsValue FromInt(uint value)
+        public virtual double AsNumber()
         {
-            if (value >= 0 && value < _intToJsValue.Length)
-            {
-                return _intToJsValue[value];
-            }
-            return new JsValue(value);
+            throw new ArgumentException("The value is not a number");
         }
 
-        internal static JsValue FromInt(ulong value)
-        {
-            if (value >= 0 && value < (ulong) _intToJsValue.Length)
-            {
-                return _intToJsValue[value];
-            }
-            return new JsValue(value);
-        }
-
-        internal static JsValue FromChar(char value)
-        {
-            if (value >= 0 && value <= AsciiMax)
-            {
-                return _charToJsValue[value];
-            }
-            return new JsValue(value);
-        }
+        public abstract Types Type { get; }
 
         /// <summary>
         /// Creates a valid <see cref="JsValue"/> instance from any <see cref="Object"/> instance
@@ -478,6 +199,11 @@ namespace Jint.Native
                 return Null;
             }
 
+            if (value is JsValue jsValue)
+            {
+                return jsValue;
+            }
+
             foreach (var converter in engine.Options._ObjectConverters)
             {
                 if (converter.TryConvert(value, out var result))
@@ -495,23 +221,11 @@ namespace Jint.Native
                 return typeMapper(engine, value);
             }
 
-            // if an ObjectInstance is passed directly, use it as is
-            if (value is ObjectInstance instance)
-            {
-                // Learn conversion.
-                // Learn conversion, racy, worst case we'll try again later
-                Interlocked.CompareExchange(ref Engine.TypeMappers, new Dictionary<Type, Func<Engine, object, JsValue>>(typeMappers)
-                {
-                    [valueType] = (Engine e, object v) => ((ObjectInstance)v).JsValue
-                }, typeMappers);
-                return instance.JsValue;
-            }
-
             var type = value as Type;
-            if(type != null)
+            if (type != null)
             {
                 var typeReference = TypeReference.CreateTypeReference(engine, type);
-                return typeReference.JsValue;
+                return typeReference;
             }
 
             if (value is System.Array a)
@@ -545,7 +259,7 @@ namespace Jint.Native
 
             if (value.GetType().IsEnum())
             {
-                return FromInt((int) value);
+                return JsNumber.Create((int) value);
             }
 
             // if no known type could be guessed, wrap it as an ObjectInstance
@@ -556,127 +270,7 @@ namespace Jint.Native
         /// Converts a <see cref="JsValue"/> to its underlying CLR value.
         /// </summary>
         /// <returns>The underlying CLR value of the <see cref="JsValue"/> instance.</returns>
-        public object ToObject()
-        {
-            switch (_type)
-            {
-                case Types.None:
-                case Types.Undefined:
-                case Types.Null:
-                    return null;
-                case Types.String:
-                    return _object;
-                case Types.Boolean:
-                    return _double != 0;
-                case Types.Number:
-                    return _double;
-                case Types.Object:
-                    if (_object is IObjectWrapper wrapper)
-                    {
-                        return wrapper.Target;
-                    }
-
-                    switch ((_object as ObjectInstance).Class)
-                    {
-                        case "Array":
-                            if (_object is ArrayInstance arrayInstance)
-                            {
-                                var len = TypeConverter.ToInt32(arrayInstance.Get("length"));
-                                var result = new object[len];
-                                for (var k = 0; k < len; k++)
-                                {
-                                    var pk = TypeConverter.ToString(k);
-                                    var kpresent = arrayInstance.HasProperty(pk);
-                                    if (kpresent)
-                                    {
-                                        var kvalue = arrayInstance.Get(pk);
-                                        result[k] = kvalue.ToObject();
-                                    }
-                                    else
-                                    {
-                                        result[k] = null;
-                                    }
-                                }
-                                return result;
-                            }
-                            break;
-
-                        case "String":
-                            if (_object is StringInstance stringInstance)
-                            {
-                                return stringInstance.PrimitiveValue.AsString();
-                            }
-
-                            break;
-
-                        case "Date":
-                            if (_object is DateInstance dateInstance)
-                            {
-                                return dateInstance.ToDateTime();
-                            }
-
-                            break;
-
-                        case "Boolean":
-                            if (_object is BooleanInstance booleanInstance)
-                            {
-                                return booleanInstance.PrimitiveValue.AsBoolean();
-                            }
-
-                            break;
-
-                        case "Function":
-                            if (_object is FunctionInstance function)
-                            {
-                                return (Func<JsValue, JsValue[], JsValue>)function.Call;
-                            }
-
-                            break;
-
-                        case "Number":
-                            if (_object is NumberInstance numberInstance)
-                            {
-                                return numberInstance.NumberData.AsNumber();
-                            }
-
-                            break;
-
-                        case "RegExp":
-                            if (_object is RegExpInstance regeExpInstance)
-                            {
-                                return regeExpInstance.Value;
-                            }
-
-                            break;
-
-                        case "Arguments":
-                        case "Object":
-#if __IOS__
-                                IDictionary<string, object> o = new Dictionary<string, object>();
-#else
-                            IDictionary<string, object> o = new ExpandoObject();
-#endif
-
-                            var objectInstance = (ObjectInstance) _object;
-                            foreach (var p in objectInstance.GetOwnProperties())
-                            {
-                                if (!p.Value.Enumerable.HasValue || p.Value.Enumerable.Value == false)
-                                {
-                                    continue;
-                                }
-
-                                o.Add(p.Key, objectInstance.Get(p.Key).ToObject());
-                            }
-
-                            return o;
-                    }
-
-
-                    return _object;
-                default:
-                    throw new ArgumentOutOfRangeException();
-            }
-        }
+        public abstract object ToObject();
 
         /// <summary>
         /// Invoke the current value as function.
@@ -726,24 +320,7 @@ namespace Jint.Native
 
         public override string ToString()
         {
-            switch (Type)
-            {
-                case Types.None:
-                    return "None";
-                case Types.Undefined:
-                    return "undefined";
-                case Types.Null:
-                    return "null";
-                case Types.Boolean:
-                    return _double != 0 ? bool.TrueString : bool.FalseString;
-                case Types.Number:
-                    return _double.ToString();
-                case Types.String:
-                case Types.Object:
-                    return _object.ToString();
-                default:
-                    return string.Empty;
-            }
+            return "None";
         }
 
         public static bool operator ==(JsValue a, JsValue b)
@@ -788,66 +365,62 @@ namespace Jint.Native
 
         static public implicit operator JsValue(char value)
         {
-            return FromChar(value);
+            return JsString.Create(value);
         }
 
         static public implicit operator JsValue(int value)
         {
-            return FromInt(value);
+            return JsNumber.Create(value);
         }
 
         static public implicit operator JsValue(uint value)
         {
-            return FromInt(value);
+            return JsNumber.Create(value);
         }
 
         static public implicit operator JsValue(double value)
         {
-            return FromDouble(value);
+            return JsNumber.Create(value);
         }
 
         public static implicit operator JsValue(bool value)
         {
-            return value ? True : False;
+            return value ? JsBoolean.True : JsBoolean.False;
         }
 
         public static implicit operator JsValue(string value)
         {
-            if (value.Length <= 1)
-            {
-                if (value == "")
-                {
-                    return EmptyString;
-                }
-
-                if (value.Length == 1)
-                {
-                    if (value[0] >= 0 && value[0] <= AsciiMax)
-                    {
-                        return _charToStringJsValue[value[0]];
-                    }
-                }
+            return JsString.Create(value);
+        }
 
+        public override bool Equals(object obj)
+        {
+            if (ReferenceEquals(null, obj))
+            {
+                return false;
             }
-            else if (value == Native.Null.Text)
+
+            if (ReferenceEquals(this, obj))
             {
-                return NullString;
+                return true;
             }
 
-            return new JsValue(value);
+            return obj is JsValue value && Equals(value);
         }
 
-        public static implicit operator JsValue(ObjectInstance value)
+        public abstract bool Equals(JsValue other);
+
+        public override int GetHashCode()
         {
-            return value.JsValue;
+            return Type.GetHashCode();
         }
 
         internal class JsValueDebugView
         {
             public string Value;
+
             public JsValueDebugView(JsValue value)
             {
-
                 switch (value.Type)
                 {
                     case Types.None:
@@ -880,34 +453,5 @@ namespace Jint.Native
                 }
             }
         }
-
-        public override bool Equals(object obj)
-        {
-            if (ReferenceEquals(null, obj)) return false;
-            return obj is JsValue value && Equals(value);
-        }
-
-        public override int GetHashCode()
-        {
-            unchecked
-            {
-                var hashCode = 0;
-                hashCode = (hashCode * 397) ^ _double.GetHashCode();
-                hashCode = (hashCode * 397) ^ (_object != null ? _object.GetHashCode() : 0);
-                hashCode = (hashCode * 397) ^ (int)_type;
-                return hashCode;
-            }
-        }
-    }
-
-    /// <summary>
-    /// The _object value of a <see cref="JsSymbol"/> is the [[Description]] internal slot.
-    /// </summary>
-    public class JsSymbol : JsValue
-    {
-        public JsSymbol(string description) : base(description)
-        {
-            _type = Types.Symbol;
-        }
     }
 }

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

@@ -45,9 +45,9 @@ namespace Jint.Native.Json
         public JsValue Stringify(JsValue thisObject, JsValue[] arguments)
         {
             JsValue
-                value = Undefined.Instance,
-                replacer = Undefined.Instance,
-                space = Undefined.Instance;
+                value = Undefined,
+                replacer = Undefined,
+                space = Undefined;
 
             if (arguments.Length > 2)
             {
@@ -65,12 +65,11 @@ namespace Jint.Native.Json
             }
 
             var serializer = new JsonSerializer(_engine);
-            if (value == Undefined.Instance && replacer == Undefined.Instance) {
-                return Undefined.Instance;
-            }
-            else {
-                return serializer.Serialize(value, replacer, space);
+            if (ReferenceEquals(value, Undefined) && ReferenceEquals(replacer, Undefined)) {
+                return Undefined;
             }
+
+            return serializer.Serialize(value, replacer, space);
         }
     }
 }

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

@@ -789,7 +789,7 @@ namespace Jint.Native.Json
                     return Null.Instance;
                 case Tokens.BooleanLiteral:
                     // implicit conversion operator goes through caching
-                    return (bool) Lex().Value ? JsValue.True : JsValue.False;
+                    return (bool) Lex().Value ? JsBoolean.True : JsBoolean.False;
                 case Tokens.String:
                     // implicit conversion operator goes through caching
                     return (string) Lex().Value;

+ 2 - 2
Jint/Native/Json/JsonSerializer.cs

@@ -29,7 +29,7 @@ namespace Jint.Native.Json
 
             // for JSON.stringify(), any function passed as the first argument will return undefined
             // if the replacer is not defined. The function is not called either.
-            if (value.Is<ICallable>() && replacer == Undefined.Instance)
+            if (value.Is<ICallable>() && ReferenceEquals(replacer, Undefined.Instance))
             {
                 return Undefined.Instance;
             }
@@ -165,7 +165,7 @@ namespace Jint.Native.Json
                 }
             }
 
-            if (value == Null.Instance)
+            if (ReferenceEquals(value, Null.Instance))
             {
                 return "null";
             }

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

@@ -137,7 +137,7 @@ namespace Jint.Native.Number
         {
             var x = TypeConverter.ToNumber(thisObj);
 
-            if (arguments.At(0) == Undefined.Instance)
+            if (ReferenceEquals(arguments.At(0), Undefined))
             {
                 return TypeConverter.ToString(x);
             }

+ 4 - 4
Jint/Native/Object/ObjectConstructor.cs

@@ -66,7 +66,7 @@ namespace Jint.Native.Object
                 return Construct(arguments);
             }
 
-            if(arguments[0] == Null.Instance || arguments[0] == Undefined.Instance)
+            if(ReferenceEquals(arguments[0], Null) || ReferenceEquals(arguments[0], Undefined))
             {
                 return Construct(arguments);
             }
@@ -114,7 +114,7 @@ namespace Jint.Native.Object
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            return o.Prototype ?? Null.Instance;
+            return o.Prototype ?? Null;
         }
 
         public JsValue GetOwnPropertyDescriptor(JsValue thisObject, JsValue[] arguments)
@@ -172,7 +172,7 @@ namespace Jint.Native.Object
             var oArg = arguments.At(0);
 
             var o = oArg.TryCast<ObjectInstance>();
-            if (o == null && oArg != Null.Instance)
+            if (o == null && oArg != Null)
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -181,7 +181,7 @@ namespace Jint.Native.Object
             obj.Prototype = o;
 
             var properties = arguments.At(1);
-            if (properties != Undefined.Instance)
+            if (properties != Undefined)
             {
                 DefineProperties(thisObject, new [] {obj, properties});
             }

+ 246 - 33
Jint/Native/Object/ObjectInstance.cs

@@ -1,18 +1,27 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Dynamic;
+using Jint.Native.Array;
+using Jint.Native.Boolean;
+using Jint.Native.Date;
+using Jint.Native.Function;
+using Jint.Native.Number;
+using Jint.Native.RegExp;
+using Jint.Native.String;
 using Jint.Runtime;
 using Jint.Runtime.Descriptors;
 using Jint.Runtime.Descriptors.Specialized;
+using Jint.Runtime.Interop;
 
 namespace Jint.Native.Object
 {
-    public class ObjectInstance
+    public class ObjectInstance : JsValue, IEquatable<ObjectInstance>
     {
         private const string PropertyNamePrototype = "prototype";
         private const string PropertyNameConstructor = "constructor";
         private const string PropertyNameLength = "length";
 
-        private JsValue _jsValue;
-
         private Dictionary<string, IPropertyDescriptor> _intrinsicProperties;
         private MruPropertyCache2<string, IPropertyDescriptor> _properties;
 
@@ -27,14 +36,6 @@ namespace Jint.Native.Object
 
         public Engine Engine { get; set; }
 
-        /// <summary>
-        /// Caches the constructed JS.
-        /// </summary>
-        internal JsValue JsValue
-        {
-            get { return _jsValue = _jsValue ?? new JsValue(this); }
-        }
-
         protected bool TryGetIntrinsicValue(JsSymbol symbol, out JsValue value)
         {
             IPropertyDescriptor descriptor;
@@ -47,7 +48,7 @@ namespace Jint.Native.Object
 
             if (Prototype == null)
             {
-                value = JsValue.Undefined;
+                value = Undefined;
                 return false;
             }
 
@@ -227,20 +228,20 @@ namespace Jint.Native.Object
 
             if (desc == PropertyDescriptor.Undefined)
             {
-                return JsValue.Undefined;
+                return Undefined;
             }
 
             if (desc.IsDataDescriptor())
             {
                 var val = desc.Value;
-                return val != null ? val : Undefined.Instance;
+                return val != null ? val : Undefined;
             }
 
-            var getter = desc.Get != null ? desc.Get : Undefined.Instance;
+            var getter = desc.Get != null ? desc.Get : Undefined;
 
             if (getter.IsUndefined())
             {
-                return Undefined.Instance;
+                return JsValue.Undefined;
             }
 
             // if getter is not undefined it must be ICallable
@@ -337,7 +338,7 @@ namespace Jint.Native.Object
 
         public bool TryGetValue(string propertyName, out JsValue value)
         {
-            value = JsValue.Undefined;
+            value = Undefined;
             var desc = GetOwnProperty(propertyName);
             if (desc != null && desc != PropertyDescriptor.Undefined)
             {
@@ -352,11 +353,11 @@ namespace Jint.Native.Object
                     return true;
                 }
 
-                var getter = desc.Get != null ? desc.Get : Undefined.Instance;
+                var getter = desc.Get != null ? desc.Get : Undefined;
 
                 if (getter.IsUndefined())
                 {
-                    value = Undefined.Instance;
+                    value = Undefined;
                     return false;
                 }
 
@@ -413,7 +414,7 @@ namespace Jint.Native.Object
             if (desc.IsAccessorDescriptor())
             {
                 var setter = desc.Set.TryCast<ICallable>();
-                setter.Call(JsValue, new[] {value});
+                setter.Call(this, new[] {value});
             }
             else
             {
@@ -541,7 +542,7 @@ namespace Jint.Native.Object
                 var toString = Get("toString").TryCast<ICallable>();
                 if (toString != null)
                 {
-                    var str = toString.Call(JsValue, Arguments.Empty);
+                    var str = toString.Call(this, Arguments.Empty);
                     if (str.IsPrimitive())
                     {
                         return str;
@@ -551,7 +552,7 @@ namespace Jint.Native.Object
                 var valueOf = Get("valueOf").TryCast<ICallable>();
                 if (valueOf != null)
                 {
-                    var val = valueOf.Call(JsValue, Arguments.Empty);
+                    var val = valueOf.Call(this, Arguments.Empty);
                     if (val.IsPrimitive())
                     {
                         return val;
@@ -566,7 +567,7 @@ namespace Jint.Native.Object
                 var valueOf = Get("valueOf").TryCast<ICallable>();
                 if (valueOf != null)
                 {
-                    var val = valueOf.Call(JsValue, Arguments.Empty);
+                    var val = valueOf.Call(this, Arguments.Empty);
                     if (val.IsPrimitive())
                     {
                         return val;
@@ -576,7 +577,7 @@ namespace Jint.Native.Object
                 var toString = Get("toString").TryCast<ICallable>();
                 if (toString != null)
                 {
-                    var str = toString.Call(JsValue, Arguments.Empty);
+                    var str = toString.Call(this, Arguments.Empty);
                     if (str.IsPrimitive())
                     {
                         return str;
@@ -625,17 +626,17 @@ namespace Jint.Native.Object
                         IPropertyDescriptor propertyDescriptor;
                         if (desc.Configurable.GetValueOrDefault() && desc.Enumerable.GetValueOrDefault() && desc.Writable.GetValueOrDefault())
                         {
-                            propertyDescriptor = new ConfigurableEnumerableWritablePropertyDescriptor(desc.Value != null ? desc.Value : JsValue.Undefined);
+                            propertyDescriptor = new ConfigurableEnumerableWritablePropertyDescriptor(desc.Value != null ? desc.Value : Undefined);
                         }
                         else if (!desc.Configurable.GetValueOrDefault() && !desc.Enumerable.GetValueOrDefault() && !desc.Writable.GetValueOrDefault())
                         {
-                            propertyDescriptor = new AllForbiddenPropertyDescriptor(desc.Value != null ? desc.Value : JsValue.Undefined);
+                            propertyDescriptor = new AllForbiddenPropertyDescriptor(desc.Value != null ? desc.Value : Undefined);
                         }
                         else
                         {
                             propertyDescriptor = new PropertyDescriptor(desc)
                             {
-                                Value = desc.Value != null ? desc.Value : JsValue.Undefined,
+                                Value = desc.Value != null ? desc.Value : Undefined,
                                 Writable = desc.Writable.HasValue ? desc.Writable.Value : false,
                                 Enumerable = desc.Enumerable.HasValue ? desc.Enumerable.Value : false,
                                 Configurable = desc.Configurable.HasValue ? desc.Configurable.Value : false
@@ -722,8 +723,8 @@ namespace Jint.Native.Object
                     if (current.IsDataDescriptor())
                     {
                         SetOwnProperty(propertyName, current = new PropertyDescriptor(
-                            get: Undefined.Instance,
-                            set: Undefined.Instance,
+                            get: JsValue.Undefined,
+                            set: JsValue.Undefined,
                             enumerable: current.Enumerable,
                             configurable: current.Configurable
                         ));
@@ -731,7 +732,7 @@ namespace Jint.Native.Object
                     else
                     {
                         SetOwnProperty(propertyName, current = new PropertyDescriptor(
-                            value: Undefined.Instance,
+                            value: JsValue.Undefined,
                             writable: null,
                             enumerable: current.Enumerable,
                             configurable: current.Configurable
@@ -770,9 +771,9 @@ namespace Jint.Native.Object
                 {
                     if (!current.Configurable.HasValue || !current.Configurable.Value)
                     {
-                        if ((desc.Set != null && !ExpressionInterpreter.SameValue(desc.Set, current.Set != null ? current.Set : Undefined.Instance))
+                        if ((desc.Set != null && !ExpressionInterpreter.SameValue(desc.Set, current.Set != null ? current.Set : Undefined))
                             ||
-                            (desc.Get != null && !ExpressionInterpreter.SameValue(desc.Get, current.Get != null ? current.Get : Undefined.Instance)))
+                            (desc.Get != null && !ExpressionInterpreter.SameValue(desc.Get, current.Get != null ? current.Get : Undefined)))
                         {
                             if (throwOnError)
                             {
@@ -862,5 +863,217 @@ namespace Jint.Native.Object
         }
 
         protected uint GetLengthValue() => TypeConverter.ToUint32(_length.Value);
+
+                public override Types Type => Types.Object;
+
+        [Pure]
+        public override bool IsArray()
+        {
+            return this is ArrayInstance;
+        }
+
+        [Pure]
+        public override bool IsDate()
+        {
+            return this is DateInstance;
+        }
+
+        [Pure]
+        public override bool IsRegExp()
+        {
+            return this is RegExpInstance;
+        }
+
+        [Pure]
+        public override ObjectInstance AsObject()
+        {
+            return this;
+        }
+
+        [Pure]
+        public override TInstance AsInstance<TInstance>()
+        {
+            return this as TInstance;
+        }
+
+        [Pure]
+        public override ArrayInstance AsArray()
+        {
+            if (!IsArray())
+            {
+                throw new ArgumentException("The value is not an array");
+            }
+
+            return this as ArrayInstance;
+        }
+
+        [Pure]
+        public override DateInstance AsDate()
+        {
+            if (!IsDate())
+            {
+                throw new ArgumentException("The value is not a date");
+            }
+
+            return this as DateInstance;
+        }
+
+        [Pure]
+        public override RegExpInstance AsRegExp()
+        {
+            if (!IsRegExp())
+            {
+                throw new ArgumentException("The value is not a regex");
+            }
+
+            return this as RegExpInstance;
+        }
+
+        public override bool Is<T>()
+        {
+            return this is T;
+        }
+
+        public override T As<T>()
+        {
+            return this as T;
+        }
+
+        public override object ToObject()
+        {
+            if (this is IObjectWrapper wrapper)
+            {
+                return wrapper.Target;
+            }
+
+            switch (Class)
+            {
+                case "Array":
+                    if (this is ArrayInstance arrayInstance)
+                    {
+                        var len = TypeConverter.ToInt32(arrayInstance.Get("length"));
+                        var result = new object[len];
+                        for (var k = 0; k < len; k++)
+                        {
+                            var pk = TypeConverter.ToString(k);
+                            var kpresent = arrayInstance.HasProperty(pk);
+                            if (kpresent)
+                            {
+                                var kvalue = arrayInstance.Get(pk);
+                                result[k] = kvalue.ToObject();
+                            }
+                            else
+                            {
+                                result[k] = null;
+                            }
+                        }
+
+                        return result;
+                    }
+
+                    break;
+
+                case "String":
+                    if (this is StringInstance stringInstance)
+                    {
+                        return stringInstance.PrimitiveValue.AsString();
+                    }
+
+                    break;
+
+                case "Date":
+                    if (this is DateInstance dateInstance)
+                    {
+                        return dateInstance.ToDateTime();
+                    }
+
+                    break;
+
+                case "Boolean":
+                    if (this is BooleanInstance booleanInstance)
+                    {
+                        return booleanInstance.PrimitiveValue.AsBoolean();
+                    }
+
+                    break;
+
+                case "Function":
+                    if (this is FunctionInstance function)
+                    {
+                        return (Func<JsValue, JsValue[], JsValue>) function.Call;
+                    }
+
+                    break;
+
+                case "Number":
+                    if (this is NumberInstance numberInstance)
+                    {
+                        return numberInstance.NumberData.AsNumber();
+                    }
+
+                    break;
+
+                case "RegExp":
+                    if (this is RegExpInstance regeExpInstance)
+                    {
+                        return regeExpInstance.Value;
+                    }
+
+                    break;
+
+                case "Arguments":
+                case "Object":
+#if __IOS__
+                                IDictionary<string, object> o = new Dictionary<string, object>();
+#else
+                    IDictionary<string, object> o = new ExpandoObject();
+#endif
+
+                    foreach (var p in GetOwnProperties())
+                    {
+                        if (!p.Value.Enumerable.HasValue || p.Value.Enumerable.Value == false)
+                        {
+                            continue;
+                        }
+
+                        o.Add(p.Key, Get(p.Key).ToObject());
+                    }
+
+                    return o;
+            }
+
+
+            return this;
+        }
+
+        public override bool Equals(JsValue obj)
+        {
+            if (ReferenceEquals(null, obj))
+            {
+                return false;
+            }
+
+            if (!(obj is ObjectInstance s))
+            {
+                return false;
+            }
+
+            return Equals(s);
+        }
+
+        public bool Equals(ObjectInstance other)
+        {
+            if (ReferenceEquals(null, other))
+            {
+                return false;
+            }
+
+            if (ReferenceEquals(this, other))
+            {
+                return true;
+            }
+
+            return false;
+        }
     }
 }

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

@@ -94,12 +94,12 @@ namespace Jint.Native.Object
         /// <returns></returns>
         public JsValue ToObjectString(JsValue thisObject, JsValue[] arguments)
         {
-            if (thisObject == Undefined.Instance)
+            if (ReferenceEquals(thisObject, Undefined))
             {
                 return "[object Undefined]";
             }
 
-            if (thisObject == Null.Instance)
+            if (ReferenceEquals(thisObject, Null))
             {
                 return "[object Null]";
             }

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

@@ -41,7 +41,9 @@ namespace Jint.Native.RegExp
             var pattern = arguments.At(0);
             var flags = arguments.At(1);
 
-            if (pattern != Undefined.Instance && flags == Undefined.Instance && TypeConverter.ToObject(Engine, pattern).Class == "Regex")
+            if (!ReferenceEquals(pattern, Undefined)
+                && ReferenceEquals(flags, Undefined)
+                && TypeConverter.ToObject(Engine, pattern).Class == "Regex")
             {
                 return pattern;
             }
@@ -64,27 +66,26 @@ namespace Jint.Native.RegExp
             var flags = arguments.At(1);
 
             var r = pattern.TryCast<RegExpInstance>();
-            if (flags == Undefined.Instance && r != null)
+            if (ReferenceEquals(flags, Undefined) && r != null)
             {
                 return r;
             }
-            else if (flags != Undefined.Instance && r != null)
+            else if (flags != Undefined && r != null)
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
             else
             {
-                if (pattern == Undefined.Instance)
+                if (ReferenceEquals(pattern, Undefined))
                 {
                     p = "";
-
                 }
                 else
                 {
                     p = TypeConverter.ToString(pattern);
                 }
 
-                f = flags != Undefined.Instance ? TypeConverter.ToString(flags) : "";
+                f = !ReferenceEquals(flags, Undefined) ? TypeConverter.ToString(flags) : "";
             }
 
             r = new RegExpInstance(Engine);

+ 4 - 4
Jint/Native/RegExp/RegExpPrototype.cs

@@ -57,7 +57,7 @@ namespace Jint.Native.RegExp
             }
 
             var match = Exec(r, arguments);
-            return match != Null.Instance;
+            return match != Null;
         }
 
         internal JsValue Exec(JsValue thisObj, JsValue[] arguments)
@@ -91,7 +91,7 @@ namespace Jint.Native.RegExp
             if (i < 0 || i > length)
             {
                 R.Put("lastIndex", (double) 0, true);
-                return Null.Instance;
+                return Null;
             }
 
             r = R.Match(s, i);
@@ -99,7 +99,7 @@ namespace Jint.Native.RegExp
             if (!r.Success)
             {
                 R.Put("lastIndex", (double) 0, true);
-                return Null.Instance;
+                return Null;
             }
 
             var e = r.Index + r.Length;
@@ -116,7 +116,7 @@ namespace Jint.Native.RegExp
             for (uint k = 0; k < n; k++)
             {
                 var group = r.Groups[(int) k];
-                var value = group.Success ? group.Value : Undefined.Instance;
+                var value = group.Success ? group.Value : Undefined;
                 a.SetIndexValue(k, value, throwOnError: true);
             }
 

+ 0 - 43
Jint/Native/String/StringExecutionContext.cs

@@ -1,43 +0,0 @@
-using System.Collections.Generic;
-using System.Text;
-using System.Threading;
-
-namespace Jint.Native.String
-{
-    /// <summary>
-    /// Helper to cache common data structures when manipulating strings.
-    /// </summary>
-    internal class StringExecutionContext
-    {
-        private static readonly ThreadLocal<StringExecutionContext> _executionContext = new ThreadLocal<StringExecutionContext>(() => new StringExecutionContext());
-
-        private StringBuilder _stringBuilder;
-        private List<string> _splitSegmentList;
-        private string[] _splitArray1;
-        private JsValue[] _callArray3;
-
-        private StringExecutionContext()
-        {
-        }
-
-        public StringBuilder GetStringBuilder(int capacity)
-        {
-            if (_stringBuilder == null)
-            {
-                _stringBuilder = new StringBuilder(capacity);
-            }
-            else
-            {
-                _stringBuilder.EnsureCapacity(capacity);
-            }
-
-            return _stringBuilder;
-        }
-
-        public List<string> SplitSegmentList => _splitSegmentList = _splitSegmentList ?? new List<string>();
-        public string[] SplitArray1 => _splitArray1 = _splitArray1 ?? new string[1];
-        public JsValue[] CallArray3 => _callArray3 = _callArray3 ?? new JsValue[3];
-
-        public static StringExecutionContext Current => _executionContext.Value;
-    }
-}

+ 96 - 32
Jint/Native/String/StringPrototype.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.CompilerServices;
+using System.Text;
 using Jint.Native.Array;
 using Jint.Native.Function;
 using Jint.Native.Object;
@@ -17,6 +18,11 @@ namespace Jint.Native.String
     /// </summary>
     public sealed class StringPrototype : StringInstance
     {
+        private StringBuilder _stringBuilder;
+        private List<string> _splitSegmentList;
+        private JsValue[] _callArray3;
+        private string[] _splitArray1;
+
         private StringPrototype(Engine engine)
             : base(engine)
         {
@@ -61,6 +67,20 @@ namespace Jint.Native.String
             FastAddProperty("padEnd", new ClrFunctionInstance(Engine, PadEnd), true, false, true);
         }
 
+        private StringBuilder GetStringBuilder(int capacity)
+        {
+            if (_stringBuilder == null)
+            {
+                _stringBuilder = new StringBuilder(capacity);
+            }
+            else
+            {
+                _stringBuilder.EnsureCapacity(capacity);
+            }
+
+            return _stringBuilder;
+        }
+
         private JsValue ToStringString(JsValue thisObj, JsValue[] arguments)
         {
             var s = TypeConverter.ToObject(Engine, thisObj) as StringInstance;
@@ -201,13 +221,25 @@ namespace Jint.Native.String
             var len = s.Length;
             var intStart = ToIntegerSupportInfinity(start);
 
-            var intEnd = arguments.At(1) == Undefined.Instance ? len : ToIntegerSupportInfinity(end);
+            var intEnd = ReferenceEquals(arguments.At(1), Undefined) ? len : ToIntegerSupportInfinity(end);
             var finalStart = System.Math.Min(len, System.Math.Max(intStart, 0));
             var finalEnd = System.Math.Min(len, System.Math.Max(intEnd, 0));
             // Swap value if finalStart < finalEnd
             var from = System.Math.Min(finalStart, finalEnd);
             var to = System.Math.Max(finalStart, finalEnd);
-            return s.Substring(from, to - from);
+            var length = to - from;
+
+            if (length == 0)
+            {
+                return string.Empty;
+            }
+
+            if (length == 1)
+            {
+                return TypeConverter.ToString(s[from]);
+            }
+
+            return s.Substring(from, length);
         }
 
         private JsValue Substr(JsValue thisObj, JsValue[] arguments)
@@ -225,7 +257,13 @@ namespace Jint.Native.String
                 return "";
             }
 
-            return s.Substring(TypeConverter.ToInt32(start), TypeConverter.ToInt32(length));
+            var startIndex = TypeConverter.ToInt32(start);
+            var l = TypeConverter.ToInt32(length);
+            if (l == 1)
+            {
+                return TypeConverter.ToString(s[startIndex]);
+            }
+            return s.Substring(startIndex, l);
         }
 
         private JsValue Split(JsValue thisObj, JsValue[] arguments)
@@ -237,7 +275,7 @@ namespace Jint.Native.String
 
             // Coerce into a number, true will become 1
             var l = arguments.At(1);
-            var limit = l == Undefined.Instance ? uint.MaxValue : TypeConverter.ToUint32(l);
+            var limit = ReferenceEquals(l, Undefined) ? uint.MaxValue : TypeConverter.ToUint32(l);
             var len = s.Length;
 
             if (limit == 0)
@@ -245,11 +283,11 @@ namespace Jint.Native.String
                 return Engine.Array.Construct(Arguments.Empty);
             }
 
-            if (separator == Null.Instance)
+            if (ReferenceEquals(separator, Null))
             {
-                separator = Null.Text;
+                separator = Native.Null.Text;
             }
-            else if (separator == Undefined.Instance)
+            else if (ReferenceEquals(separator, Undefined))
             {
                 return (ArrayInstance)Engine.Array.Construct(Arguments.From(s));
             }
@@ -300,7 +338,7 @@ namespace Jint.Native.String
                     for (int i = 1; i < match.Groups.Count; i++)
                     {
                         var group = match.Groups[i];
-                        var item = Undefined.Instance;
+                        var item = Undefined;
                         if (group.Captures.Count > 0)
                         {
                             item = match.Groups[i].Value;
@@ -325,7 +363,7 @@ namespace Jint.Native.String
             }
             else
             {
-                var segments = StringExecutionContext.Current.SplitSegmentList;
+                var segments = _splitSegmentList = _splitSegmentList ?? new List<string>();
                 segments.Clear();
                 var sep = TypeConverter.ToString(separator);
 
@@ -342,7 +380,7 @@ namespace Jint.Native.String
                 }
                 else
                 {
-                    var array = StringExecutionContext.Current.SplitArray1;
+                    var array = _splitArray1 = _splitArray1 ?? new string[1];
                     array[0] = sep;
                     segments.AddRange(s.Split(array, StringSplitOptions.None));
                 }
@@ -360,8 +398,6 @@ namespace Jint.Native.String
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
 
-            var s = TypeConverter.ToString(thisObj);
-
             var start = TypeConverter.ToNumber(arguments.At(0));
             if (double.NegativeInfinity.Equals(start))
             {
@@ -372,6 +408,7 @@ namespace Jint.Native.String
                 return string.Empty;
             }
 
+            var s = TypeConverter.ToString(thisObj);
             var end = TypeConverter.ToNumber(arguments.At(1));
             if (double.PositiveInfinity.Equals(end))
             {
@@ -380,11 +417,21 @@ namespace Jint.Native.String
 
             var len = s.Length;
             var intStart = (int)TypeConverter.ToInteger(start);
-            var intEnd = arguments.At(1) == Undefined.Instance ? len : (int)TypeConverter.ToInteger(end);
+            var intEnd = ReferenceEquals(arguments.At(1), Undefined) ? len : (int)TypeConverter.ToInteger(end);
             var from = intStart < 0 ? System.Math.Max(len + intStart, 0) : System.Math.Min(intStart, len);
             var to = intEnd < 0 ? System.Math.Max(len + intEnd, 0) : System.Math.Min(intEnd, len);
             var span = System.Math.Max(to - from, 0);
 
+            if (span == 0)
+            {
+                return string.Empty;
+            }
+
+            if (span == 1)
+            {
+                return TypeConverter.ToString(s[from]);
+            }
+
             return s.Substring(from, span);
         }
 
@@ -402,7 +449,7 @@ namespace Jint.Native.String
             }
             else if (regex.IsNull())
             {
-                regex = Null.Text;
+                regex = Native.Null.Text;
             }
 
             var rx = TypeConverter.ToObject(Engine, regex) as RegExpInstance ?? (RegExpInstance)Engine.RegExp.Construct(new[] { regex });
@@ -446,7 +493,7 @@ namespace Jint.Native.String
                     // $`	Inserts the portion of the string that precedes the matched substring.
                     // $'	Inserts the portion of the string that follows the matched substring.
                     // $n or $nn	Where n or nn are decimal digits, inserts the nth parenthesized submatch string, provided the first argument was a RegExp object.
-                    var replacementBuilder = StringExecutionContext.Current.GetStringBuilder(0);
+                    var replacementBuilder = GetStringBuilder(0);
                     replacementBuilder.Clear();
                     for (int i = 0; i < replaceString.Length; i++)
                     {
@@ -509,11 +556,11 @@ namespace Jint.Native.String
 
             if (searchValue.IsNull())
             {
-                searchValue = Null.Text;
+                searchValue = Native.Null.Text;
             }
             if (searchValue.IsUndefined())
             {
-                searchValue = Undefined.Text;
+                searchValue = Native.Undefined.Text;
             }
 
             var rx = TypeConverter.ToObject(Engine, searchValue) as RegExpInstance;
@@ -532,7 +579,7 @@ namespace Jint.Native.String
                     args[match.Groups.Count] = match.Index;
                     args[match.Groups.Count + 1] = thisString;
 
-                    var v = TypeConverter.ToString(replaceFunction.Call(Undefined.Instance, args));
+                    var v = TypeConverter.ToString(replaceFunction.Call(Undefined, args));
                     return v;
                 }, rx.Global == true ? -1 : 1);
 
@@ -554,15 +601,15 @@ namespace Jint.Native.String
                     return thisString;
                 int end = start + substr.Length;
 
-                var args = StringExecutionContext.Current.CallArray3;
+                var args = _callArray3 = _callArray3 ?? new JsValue[3];
                 args[0] = substr;
                 args[1] = start;
                 args[2] = thisString;
 
-                var replaceString = TypeConverter.ToString(replaceFunction.Call(Undefined.Instance, args));
+                var replaceString = TypeConverter.ToString(replaceFunction.Call(Undefined, args));
 
                 // Replace only the first match.
-                var result = StringExecutionContext.Current.GetStringBuilder(thisString.Length + (substr.Length - substr.Length));
+                var result = GetStringBuilder(thisString.Length + (substr.Length - substr.Length));
                 result.Clear();
                 result.Append(thisString, 0, start);
                 result.Append(replaceString);
@@ -617,7 +664,7 @@ namespace Jint.Native.String
                 }
                 if (n == 0)
                 {
-                    return Null.Instance;
+                    return Null;
                 }
                 return a;
             }
@@ -641,7 +688,7 @@ namespace Jint.Native.String
             var s = TypeConverter.ToString(thisObj);
             var searchStr = TypeConverter.ToString(arguments.At(0));
             double numPos = double.NaN;
-            if (arguments.Length > 1 && arguments[1] != Undefined.Instance)
+            if (arguments.Length > 1 && arguments[1] != Undefined)
             {
                 numPos = TypeConverter.ToNumber(arguments[1]);
             }
@@ -688,7 +735,7 @@ namespace Jint.Native.String
             var s = TypeConverter.ToString(thisObj);
             var searchStr = TypeConverter.ToString(arguments.At(0));
             double pos = 0;
-            if (arguments.Length > 1 && arguments[1] != Undefined.Instance)
+            if (arguments.Length > 1 && arguments[1] != Undefined)
             {
                 pos = TypeConverter.ToInteger(arguments[1]);
             }
@@ -710,16 +757,33 @@ namespace Jint.Native.String
         {
             TypeConverter.CheckObjectCoercible(Engine, thisObj);
 
-            var s = TypeConverter.ToString(thisObj);
-            var sb = StringExecutionContext.Current.GetStringBuilder(0);
-            sb.Clear();
-            sb.Append(s);
+            // try to hint capacity if possible
+            int capacity = 0;
+            for (int i = 0; i < arguments.Length; ++i)
+            {
+                if (arguments[i].Type == Types.String)
+                {
+                    capacity += arguments[i].AsString().Length;
+                }
+            }
+
+            var value = TypeConverter.ToString(thisObj);
+            capacity += value.Length;
+            if (!(thisObj is JsString jsString))
+            {
+                jsString = new JsString.ConcatenatedString(value, capacity);
+            }
+            else
+            {
+                jsString = jsString.EnsureCapacity(capacity);
+            }
+
             for (int i = 0; i < arguments.Length; i++)
             {
-                sb.Append(TypeConverter.ToString(arguments[i]));
+                jsString = jsString.Append(arguments[i]);
             }
 
-            return sb.ToString();
+            return jsString;
         }
 
         private JsValue CharCodeAt(JsValue thisObj, JsValue[] arguments)
@@ -823,9 +887,9 @@ namespace Jint.Native.String
             var s = TypeConverter.ToString(thisObj);
 
             var searchString = arguments.At(0);
-            if (searchString == Null.Instance)
+            if (ReferenceEquals(searchString, Null))
             {
-                searchString = Null.Text;
+                searchString = Native.Null.Text;
             }
             else
             {

+ 3 - 3
Jint/Native/Symbol/SymbolConstructor.cs

@@ -60,8 +60,8 @@ namespace Jint.Native.Symbol
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
             var description = arguments.At(0);
-            var descString = description == Undefined.Instance
-                ? Undefined.Instance
+            var descString = ReferenceEquals(description, Undefined)
+                ? Undefined
                 : TypeConverter.ToString(description);
 
             var value = new JsSymbol(description.AsString());
@@ -102,7 +102,7 @@ namespace Jint.Native.Symbol
             JsSymbol symbol;
             if (!Engine.GlobalSymbolRegistry.TryGetValue(sym.AsSymbol(), out symbol))
             {
-                return Undefined.Instance;
+                return Undefined;
             }
 
             return sym.AsSymbol();

+ 2 - 2
Jint/Native/Symbol/SymbolPrototype.cs

@@ -30,10 +30,10 @@ namespace Jint.Native.Symbol
         {
             FastAddProperty("toString", new ClrFunctionInstance(Engine, ToSymbolString), true, false, true);
             FastAddProperty("valueOf", new ClrFunctionInstance(Engine, ValueOf), true, false, true);
-            FastAddProperty("toStringTag", new JsValue("Symbol"), false, false, true);
+            FastAddProperty("toStringTag", new JsString("Symbol"), false, false, true);
 
             SetIntrinsicValue(GlobalSymbolRegistry.ToPrimitive, new ClrFunctionInstance(Engine, ToPrimitive), false, false, true);
-            SetIntrinsicValue(GlobalSymbolRegistry.ToStringTag, new JsValue("Symbol"), false, false, true);
+            SetIntrinsicValue(GlobalSymbolRegistry.ToStringTag, new JsString("Symbol"), false, false, true);
         }
 
         public string SymbolDescriptiveString(JsSymbol sym)

+ 2 - 2
Jint/Native/Undefined.cs

@@ -2,7 +2,7 @@
 {
     public static class Undefined
     {
-        public readonly static JsValue Instance = JsValue.Undefined;
-        public readonly static string Text = "undefined";
+        public static readonly JsValue Instance = JsValue.Undefined;
+        public const string Text = "undefined";
     }
 }

+ 1 - 1
Jint/Runtime/Descriptors/PropertyDescriptor.cs

@@ -135,7 +135,7 @@ namespace Jint.Runtime.Descriptors
 
         public static JsValue FromPropertyDescriptor(Engine engine, IPropertyDescriptor desc)
         {
-            if (desc == Undefined)
+            if (ReferenceEquals(desc, Undefined))
             {
                 return Native.Undefined.Instance;
             }

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

@@ -34,7 +34,7 @@ namespace Jint.Runtime.Environments
         {
             var binding = new Binding
             {
-                Value = Undefined.Instance,
+                Value = Undefined,
                 CanBeDeleted =  canBeDeleted,
                 Mutable = true
             };
@@ -69,14 +69,14 @@ namespace Jint.Runtime.Environments
         {
             var binding = name == BindingNameArguments ? _argumentsBinding : _bindings[name];
 
-            if (!binding.Mutable && binding.Value == Undefined.Instance)
+            if (!binding.Mutable && ReferenceEquals(binding.Value, Undefined))
             {
                 if (strict)
                 {
                     throw new JavaScriptException(_engine.ReferenceError, "Can't access anm uninitiazed immutable binding.");
                 }
 
-                return Undefined.Instance;
+                return Undefined;
             }
 
             return binding.Value;
@@ -118,7 +118,7 @@ namespace Jint.Runtime.Environments
 
         public override JsValue ImplicitThisValue()
         {
-            return Undefined.Instance;
+            return Undefined;
         }
 
         /// <summary>
@@ -129,7 +129,7 @@ namespace Jint.Runtime.Environments
         {
             var binding = new Binding
             {
-                Value = Undefined.Instance,
+                Value = Undefined,
                 Mutable = false,
                 CanBeDeleted = false
             };

+ 5 - 5
Jint/Runtime/Environments/ObjectEnvironmentRecord.cs

@@ -37,8 +37,8 @@ namespace Jint.Runtime.Environments
         public override void CreateMutableBinding(string name, bool configurable = true)
         {
             var propertyDescriptor = configurable
-                ? (IPropertyDescriptor) new ConfigurableEnumerableWritablePropertyDescriptor(Undefined.Instance)
-                : new NonConfigurablePropertyDescriptor(Undefined.Instance);
+                ? (IPropertyDescriptor) new ConfigurableEnumerableWritablePropertyDescriptor(Undefined)
+                : new NonConfigurablePropertyDescriptor(Undefined);
 
             _bindingObject.SetOwnProperty(name, propertyDescriptor);
         }
@@ -56,7 +56,7 @@ namespace Jint.Runtime.Environments
             {
                 if(!strict)
                 {
-                    return Undefined.Instance;
+                    return Undefined;
                 }
 
                 throw new JavaScriptException(_engine.ReferenceError);
@@ -74,10 +74,10 @@ namespace Jint.Runtime.Environments
         {
             if (_provideThis)
             {
-                return _bindingObject.JsValue;
+                return _bindingObject;
             }
 
-            return Undefined.Instance;
+            return Undefined;
         }
 
         public override string[] GetAllBindingNames()

+ 30 - 19
Jint/Runtime/ExpressionIntepreter.cs

@@ -74,7 +74,12 @@ namespace Jint.Runtime
                     var rprim = TypeConverter.ToPrimitive(rval);
                     if (lprim.IsString() || rprim.IsString())
                     {
-                        lval = TypeConverter.ToString(lprim) + TypeConverter.ToString(rprim);
+                        var jsString = lprim as JsString;
+                        if (jsString == null)
+                        {
+                            jsString = new JsString.ConcatenatedString(TypeConverter.ToString(lprim));
+                        }
+                        lval = jsString.Append(rprim);
                     }
                     else
                     {
@@ -87,7 +92,7 @@ namespace Jint.Runtime
                     break;
 
                 case AssignmentOperator.TimesAssign:
-                    if (lval == Undefined.Instance || rval == Undefined.Instance)
+                    if (ReferenceEquals(lval, Undefined.Instance) || ReferenceEquals(rval, Undefined.Instance))
                     {
                         lval = Undefined.Instance;
                     }
@@ -102,7 +107,7 @@ namespace Jint.Runtime
                     break;
 
                 case AssignmentOperator.ModuloAssign:
-                    if (lval == Undefined.Instance || rval == Undefined.Instance)
+                    if (ReferenceEquals(lval, Undefined.Instance) || ReferenceEquals(rval, Undefined.Instance))
                     {
                         lval = Undefined.Instance;
                     }
@@ -148,7 +153,7 @@ namespace Jint.Runtime
 
         private JsValue Divide(JsValue lval, JsValue rval)
         {
-            if (lval == Undefined.Instance || rval == Undefined.Instance)
+            if (ReferenceEquals(lval, Undefined.Instance) || ReferenceEquals(rval, Undefined.Instance))
             {
                 return Undefined.Instance;
             }
@@ -226,7 +231,7 @@ namespace Jint.Runtime
                     break;
 
                 case BinaryOperator.Times:
-                    if (left == Undefined.Instance || right == Undefined.Instance)
+                    if (ReferenceEquals(left, Undefined.Instance) || ReferenceEquals(right, Undefined.Instance))
                     {
                         value = Undefined.Instance;
                     }
@@ -241,7 +246,7 @@ namespace Jint.Runtime
                     break;
 
                 case BinaryOperator.Modulo:
-                    if (left == Undefined.Instance || right == Undefined.Instance)
+                    if (ReferenceEquals(left, Undefined.Instance) || ReferenceEquals(right, Undefined.Instance))
                     {
                         value = Undefined.Instance;
                     }
@@ -261,7 +266,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.Greater:
                     value = Compare(right, left, false);
-                    if (value == Undefined.Instance)
+                    if (ReferenceEquals(value, Undefined.Instance))
                     {
                         value = false;
                     }
@@ -269,7 +274,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.GreaterOrEqual:
                     value = Compare(left, right);
-                    if (value == Undefined.Instance || value.AsBoolean())
+                    if (ReferenceEquals(value, Undefined.Instance) || value.AsBoolean())
                     {
                         value = false;
                     }
@@ -281,7 +286,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.Less:
                     value = Compare(left, right);
-                    if (value == Undefined.Instance)
+                    if (ReferenceEquals(value, Undefined.Instance))
                     {
                         value = false;
                     }
@@ -289,7 +294,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.LessOrEqual:
                     value = Compare(right, left, false);
-                    if (value == Undefined.Instance || value.AsBoolean())
+                    if (ReferenceEquals(value, Undefined.Instance) || value.AsBoolean())
                     {
                         value = false;
                     }
@@ -378,7 +383,7 @@ namespace Jint.Runtime
             }
         }
 
-        public static bool Equal(JsValue x, JsValue y)
+        private static bool Equal(JsValue x, JsValue y)
         {
             var typex = x.Type;
             var typey = y.Type;
@@ -388,12 +393,12 @@ namespace Jint.Runtime
 				return StrictlyEqual(x, y);
             }
 
-            if (x == Null.Instance && y == Undefined.Instance)
+            if (ReferenceEquals(x, Null.Instance) && ReferenceEquals(y, Undefined.Instance))
             {
                 return true;
             }
 
-            if (x == Undefined.Instance && y == Null.Instance)
+            if (ReferenceEquals(x, Undefined.Instance) && ReferenceEquals(y, Null.Instance))
             {
                 return true;
             }
@@ -614,7 +619,7 @@ namespace Jint.Runtime
             switch (literal.TokenType)
             {
                 case TokenType.BooleanLiteral:
-                    return literal.BooleanValue ? JsValue.True : JsValue.False;
+                    return literal.BooleanValue ? JsBoolean.True : JsBoolean.False;
                 case TokenType.NullLiteral:
                     return JsValue.Null;
                 case TokenType.NumericLiteral:
@@ -752,7 +757,7 @@ namespace Jint.Runtime
             if (!memberExpression.Computed) // index accessor ?
             {
                 // we can take fast path without querying the engine again
-                propertyNameString = memberExpression.Property.As<Identifier>().Name;
+                propertyNameString = ((Identifier) memberExpression.Property).Name;
             }
             else
             {
@@ -847,7 +852,7 @@ namespace Jint.Runtime
                 }
             }
 
-            if (func == Undefined.Instance)
+            if (ReferenceEquals(func, Undefined.Instance))
             {
                 throw new JavaScriptException(_engine.TypeError, r == null ? "" : string.Format("Object has no method '{0}'", r.GetReferencedName()));
             }
@@ -1064,11 +1069,11 @@ namespace Jint.Runtime
                         }
                     }
                     var v = _engine.GetValue(value);
-                    if (v == Undefined.Instance)
+                    if (ReferenceEquals(v, Undefined.Instance))
                     {
                         return "undefined";
                     }
-                    if (v == Null.Instance)
+                    if (ReferenceEquals(v, Null.Instance))
                     {
                         return "object";
                     }
@@ -1094,6 +1099,12 @@ namespace Jint.Runtime
         {
             allLiteral = true;
             var count = expressionArguments.Count;
+
+            if (count == 0)
+            {
+                return Array.Empty<JsValue>();
+            }
+
             var arguments = new JsValue[count];
             for (var i = 0; i < count; i++)
             {
@@ -1105,4 +1116,4 @@ namespace Jint.Runtime
             return arguments;
         }
     }
-}
+}

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

@@ -37,7 +37,7 @@ namespace Jint.Runtime.Interop
                 {
                     var parameterType = method.GetParameters()[i].ParameterType;
 
-                    if (parameterType == typeof(JsValue))
+                    if (typeof(JsValue).IsAssignableFrom(parameterType))
                     {
                         parameters[i] = arguments[i];
                     }
@@ -137,7 +137,7 @@ namespace Jint.Runtime.Interop
                     newArgumentsCollection[j] = jsArguments[j];
                 }
 
-                newArgumentsCollection[nonParamsArgumentsCount] = new JsValue(jsArray);
+                newArgumentsCollection[nonParamsArgumentsCount] = jsArray;
                 return newArgumentsCollection;
             }
 

+ 7 - 5
Jint/Runtime/Interop/NamespaceReference.cs

@@ -50,24 +50,26 @@ namespace Jint.Runtime.Interop
             for (int i = 0; i < arguments.Length; i++)
             {
                 var genericTypeReference = arguments.At(i);
-                if (genericTypeReference == Undefined.Instance || !genericTypeReference.IsObject() || genericTypeReference.AsObject().Class != "TypeReference")
+                if (ReferenceEquals(genericTypeReference, Undefined)
+                    || !genericTypeReference.IsObject() 
+                    || genericTypeReference.AsObject().Class != "TypeReference")
                 {
                     throw new JavaScriptException(Engine.TypeError, "Invalid generic type parameter on " + _path + ", if this is not a generic type / method, are you missing a lookup assembly?");
                 }
 
-                genericTypes[i] = arguments.At(i).As<TypeReference>().Type;
+                genericTypes[i] = arguments.At(i).As<TypeReference>().ReferenceType;
             }
 
             var typeReference = GetPath(_path + "`" + arguments.Length.ToString(CultureInfo.InvariantCulture)).As<TypeReference>();
 
             if (typeReference == null)
             {
-                return Undefined.Instance;
+                return Undefined;
             }
 
             try
             {
-                var genericType = typeReference.Type.MakeGenericType(genericTypes);
+                var genericType = typeReference.ReferenceType.MakeGenericType(genericTypes);
 
                 return TypeReference.CreateTypeReference(Engine, genericType);
             }
@@ -141,7 +143,7 @@ namespace Jint.Runtime.Interop
             }
 
             // search for type in mscorlib
-            type = Type.GetType(path);
+            type = System.Type.GetType(path);
             if (type != null)
             {
                 Engine.TypeCache.Add(path, type);

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

@@ -20,8 +20,8 @@ namespace Jint.Runtime.Interop
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
             _setter(thisObject, arguments[0]);
-            
-            return Null.Instance;
+
+            return Null;
         }
     }
 }

+ 15 - 24
Jint/Runtime/Interop/TypeReference.cs

@@ -17,13 +17,13 @@ namespace Jint.Runtime.Interop
         {
         }
 
-        public Type Type { get; set; }
+        public Type ReferenceType { get; set; }
 
         public static TypeReference CreateTypeReference(Engine engine, Type type)
         {
             var obj = new TypeReference(engine);
             obj.Extensible = false;
-            obj.Type = type;
+            obj.ReferenceType = type;
 
             // The value of the [[Prototype]] internal property of the TypeReference constructor is the Function prototype object
             obj.Prototype = engine.Function.PrototypeObject;
@@ -44,15 +44,15 @@ namespace Jint.Runtime.Interop
 
         public ObjectInstance Construct(JsValue[] arguments)
         {
-            if (arguments.Length == 0 && Type.IsValueType())
+            if (arguments.Length == 0 && ReferenceType.IsValueType())
             {
-                var instance = Activator.CreateInstance(Type);
+                var instance = Activator.CreateInstance(ReferenceType);
                 var result = TypeConverter.ToObject(Engine, JsValue.FromObject(Engine, instance));
 
                 return result;
             }
 
-            var constructors = Type.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
+            var constructors = ReferenceType.GetConstructors(BindingFlags.Public | BindingFlags.Instance);
 
             var methods = TypeConverter.FindBestMatch(Engine, constructors, arguments).ToList();
 
@@ -65,7 +65,7 @@ namespace Jint.Runtime.Interop
                     {
                         var parameterType = method.GetParameters()[i].ParameterType;
 
-                        if (parameterType == typeof(JsValue))
+                        if (typeof(JsValue).IsAssignableFrom(parameterType))
                         {
                             parameters[i] = arguments[i];
                         }
@@ -105,7 +105,7 @@ namespace Jint.Runtime.Interop
                 return base.HasInstance(v);
             }
 
-            return wrapper.Target.GetType() == this.Type;
+            return wrapper.Target.GetType() == ReferenceType;
         }
 
         public override bool DefineOwnProperty(string propertyName, IPropertyDescriptor desc, bool throwOnError)
@@ -161,10 +161,10 @@ namespace Jint.Runtime.Interop
         {
             // todo: cache members locally
 
-            if (Type.IsEnum())
+            if (ReferenceType.IsEnum())
             {
-                Array enumValues = Enum.GetValues(Type);
-                Array enumNames = Enum.GetNames(Type);
+                Array enumValues = Enum.GetValues(ReferenceType);
+                Array enumNames = Enum.GetNames(ReferenceType);
 
                 for (int i = 0; i < enumValues.Length; i++)
                 {
@@ -176,19 +176,19 @@ namespace Jint.Runtime.Interop
                 return PropertyDescriptor.Undefined;
             }
 
-            var propertyInfo = Type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static);
+            var propertyInfo = ReferenceType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static);
             if (propertyInfo != null)
             {
                 return new PropertyInfoDescriptor(Engine, propertyInfo, Type);
             }
 
-            var fieldInfo = Type.GetField(propertyName, BindingFlags.Public | BindingFlags.Static);
+            var fieldInfo = ReferenceType.GetField(propertyName, BindingFlags.Public | BindingFlags.Static);
             if (fieldInfo != null)
             {
                 return new FieldInfoDescriptor(Engine, fieldInfo, Type);
             }
 
-            var methodInfo = Type
+            var methodInfo = ReferenceType
                 .GetMethods(BindingFlags.Public | BindingFlags.Static)
                 .Where(mi => mi.Name == propertyName)
                 .ToArray();
@@ -201,17 +201,8 @@ namespace Jint.Runtime.Interop
             return new AllForbiddenPropertyDescriptor(new MethodInfoFunctionInstance(Engine, methodInfo));
         }
 
-        public object Target
-        {
-            get
-            {
-                return Type;
-            }
-        }
+        public object Target => ReferenceType;
 
-        public override string Class
-        {
-            get { return "TypeReference"; }
-        }
+        public override string Class => "TypeReference";
     }
 }

+ 2 - 2
Jint/Runtime/JavaScriptException.cs

@@ -78,7 +78,7 @@ namespace Jint.Runtime
             }
             if (error.IsString())
                 return error.AsString();
-            
+
             return error.ToString();
         }
 
@@ -110,7 +110,7 @@ namespace Jint.Runtime
                 if (value != null && Error.IsObject())
                 {
                     Error.AsObject()
-                        .FastAddProperty("callstack", new JsValue(value), false, false, false);
+                        .FastAddProperty("callstack", new JsString(value), false, false, false);
                 }
             }
         }

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

@@ -48,7 +48,7 @@ namespace Jint.Runtime.References
         public bool IsPropertyReference()
         {
             // http://www.ecma-international.org/ecma-262/5.1/#sec-8.7
-            return (_baseValue.IsObject() && !_baseValue.Is<EnvironmentRecord>()) || HasPrimitiveBase();
+            return HasPrimitiveBase() || (_baseValue.IsObject() && !(_baseValue is EnvironmentRecord));
         }
     }
 }

+ 1 - 2
Jint/Runtime/StatementInterpreter.cs

@@ -215,12 +215,11 @@ namespace Jint.Runtime
             var varRef = _engine.EvaluateExpression(identifier) as Reference;
             var exprRef = _engine.EvaluateExpression(forInStatement.Right);
             var experValue = _engine.GetValue(exprRef);
-            if (experValue == Undefined.Instance || experValue == Null.Instance)
+            if (ReferenceEquals(experValue, Undefined.Instance) || ReferenceEquals(experValue,  Null.Instance))
             {
                 return Completion.Empty;
             }
 
-
             var obj = TypeConverter.ToObject(_engine, experValue);
             JsValue v = Null.Instance;
 

+ 10 - 10
Jint/Runtime/TypeConverter.cs

@@ -57,7 +57,7 @@ namespace Jint.Runtime
         /// <returns></returns>
         public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.None)
         {
-            if (input == Null.Instance || input == Undefined.Instance)
+            if (ReferenceEquals(input, Null.Instance) || ReferenceEquals(input, Undefined.Instance))
             {
                 return input;
             }
@@ -88,7 +88,7 @@ namespace Jint.Runtime
                 return o.AsBoolean();
             }
 
-            if (o == Undefined.Instance || o == Null.Instance)
+            if (ReferenceEquals(o, Undefined.Instance) || ReferenceEquals(o, Null.Instance))
             {
                 return false;
             }
@@ -140,12 +140,12 @@ namespace Jint.Runtime
                 }
             }
 
-            if (o == Undefined.Instance)
+            if (ReferenceEquals(o, Undefined.Instance))
             {
                 return double.NaN;
             }
 
-            if (o == Null.Instance)
+            if (ReferenceEquals(o, Null.Instance))
             {
                 return 0;
             }
@@ -365,17 +365,17 @@ namespace Jint.Runtime
                     {
                         // TODO: throw a TypeError
                         // NB: But it requires an Engine reference
-                        throw new JavaScriptException(new JsValue("TypeError"));
+                        throw new JavaScriptException(new JsString("TypeError"));
                     }
                 }
             }
 
-            if (o == Undefined.Instance)
+            if (ReferenceEquals(o, Undefined.Instance))
             {
                 return Undefined.Text;
             }
 
-            if (o == Null.Instance)
+            if (ReferenceEquals(o, Null.Instance))
             {
                 return Null.Text;
             }
@@ -405,12 +405,12 @@ namespace Jint.Runtime
                 return value.AsObject();
             }
 
-            if (value == Undefined.Instance)
+            if (ReferenceEquals(value, Undefined.Instance))
             {
                 throw new JavaScriptException(engine.TypeError);
             }
 
-            if (value == Null.Instance)
+            if (ReferenceEquals(value, Null.Instance))
             {
                 throw new JavaScriptException(engine.TypeError);
             }
@@ -475,7 +475,7 @@ namespace Jint.Runtime
 
         public static void CheckObjectCoercible(Engine engine, JsValue o)
         {
-            if (o == Undefined.Instance || o == Null.Instance)
+            if (ReferenceEquals(o, Undefined.Instance) || ReferenceEquals(o, Null.Instance))
             {
                 throw new JavaScriptException(engine.TypeError);
             }