Browse Source

Make execution context struct and other performance tweaks (#502)

#451
Marko Lahma 7 years ago
parent
commit
1cf16df954
60 changed files with 570 additions and 486 deletions
  1. 1 1
      Jint.Benchmark/UncacheableExpressionsBenchmark.cs
  2. 101 43
      Jint/Engine.cs
  3. 65 0
      Jint/JsValueExtensions.cs
  4. 2 4
      Jint/Native/Argument/ArgumentsInstance.cs
  5. 1 1
      Jint/Native/Array/ArrayConstructor.cs
  6. 13 11
      Jint/Native/Array/ArrayInstance.cs
  7. 11 9
      Jint/Native/Array/ArrayPrototype.cs
  8. 1 3
      Jint/Native/Boolean/BooleanInstance.cs
  9. 1 1
      Jint/Native/Boolean/BooleanPrototype.cs
  10. 2 2
      Jint/Native/Date/DateConstructor.cs
  11. 1 9
      Jint/Native/Date/DateInstance.cs
  12. 2 2
      Jint/Native/Date/DatePrototype.cs
  13. 3 2
      Jint/Native/Error/ErrorConstructor.cs
  14. 1 9
      Jint/Native/Error/ErrorInstance.cs
  15. 1 1
      Jint/Native/Error/ErrorPrototype.cs
  16. 0 3
      Jint/Native/Function/EvalFunctionInstance.cs
  17. 1 1
      Jint/Native/Function/FunctionConstructor.cs
  18. 12 6
      Jint/Native/Function/FunctionInstance.cs
  19. 2 2
      Jint/Native/Function/FunctionPrototype.cs
  20. 14 15
      Jint/Native/Function/ScriptFunctionInstance.cs
  21. 0 3
      Jint/Native/Function/ThrowTypeError.cs
  22. 2 2
      Jint/Native/Global/GlobalObject.cs
  23. 0 7
      Jint/Native/JsBoolean.cs
  24. 0 7
      Jint/Native/JsNumber.cs
  25. 4 17
      Jint/Native/JsString.cs
  26. 1 11
      Jint/Native/JsSymbol.cs
  27. 7 32
      Jint/Native/JsValue.cs
  28. 2 12
      Jint/Native/Json/JsonInstance.cs
  29. 1 1
      Jint/Native/Json/JsonParser.cs
  30. 15 17
      Jint/Native/Json/JsonSerializer.cs
  31. 2 10
      Jint/Native/Math/MathInstance.cs
  32. 1 4
      Jint/Native/Number/Dtoa/FastDtoaBuilder.cs
  33. 1 3
      Jint/Native/Number/NumberInstance.cs
  34. 2 2
      Jint/Native/Number/NumberPrototype.cs
  35. 4 7
      Jint/Native/Object/ObjectConstructor.cs
  36. 19 10
      Jint/Native/Object/ObjectInstance.cs
  37. 2 2
      Jint/Native/Object/ObjectPrototype.cs
  38. 7 7
      Jint/Native/RegExp/RegExpConstructor.cs
  39. 1 10
      Jint/Native/RegExp/RegExpInstance.cs
  40. 2 2
      Jint/Native/RegExp/RegExpPrototype.cs
  41. 3 5
      Jint/Native/String/StringInstance.cs
  42. 26 20
      Jint/Native/String/StringPrototype.cs
  43. 2 3
      Jint/Native/Symbol/SymbolConstructor.cs
  44. 1 3
      Jint/Native/Symbol/SymbolInstance.cs
  45. 27 19
      Jint/Pooling/JsValueArrayPool.cs
  46. 1 7
      Jint/Runtime/Completion.cs
  47. 3 2
      Jint/Runtime/Debugger/DebugHandler.cs
  48. 4 4
      Jint/Runtime/Descriptors/PropertyDescriptor.cs
  49. 3 6
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  50. 15 4
      Jint/Runtime/Environments/ExecutionContext.cs
  51. 20 8
      Jint/Runtime/Environments/LexicalEnvironment.cs
  52. 0 2
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  53. 19 22
      Jint/Runtime/ExpressionIntepreter.cs
  54. 1 1
      Jint/Runtime/Interop/NamespaceReference.cs
  55. 1 1
      Jint/Runtime/Interop/ObjectWrapper.cs
  56. 1 3
      Jint/Runtime/Interop/TypeReference.cs
  57. 4 4
      Jint/Runtime/JavaScriptException.cs
  58. 64 0
      Jint/Runtime/RefStack.cs
  59. 14 13
      Jint/Runtime/StatementInterpreter.cs
  60. 53 68
      Jint/Runtime/TypeConverter.cs

+ 1 - 1
Jint.Benchmark/UncacheableExpressionsBenchmark.cs

@@ -23,7 +23,7 @@ namespace Jint.Benchmark
         {
             public Config()
             {
-                Add(Job.ShortRun);
+                Add(Job.MediumRun.WithLaunchCount(1));
                 Add(MemoryDiagnoser.Default);
             }
         }

+ 101 - 43
Jint/Engine.cs

@@ -39,7 +39,7 @@ namespace Jint
 
         private readonly ExpressionInterpreter _expressions;
         private readonly StatementInterpreter _statements;
-        private readonly Stack<ExecutionContext> _executionContexts;
+        private readonly ExecutionContextStack _executionContexts;
         private JsValue _completionValue = JsValue.Undefined;
         private int _statementsCount;
         private long _timeoutTicks;
@@ -85,7 +85,7 @@ namespace Jint
 
         public Engine(Action<Options> options)
         {
-            _executionContexts = new Stack<ExecutionContext>();
+            _executionContexts = new ExecutionContextStack();
 
             Global = GlobalObject.CreateGlobalObject(this);
 
@@ -213,7 +213,11 @@ namespace Jint
         public ErrorConstructor ReferenceError { get; }
         public ErrorConstructor UriError { get; }
 
-        public ExecutionContext ExecutionContext => _executionContexts.Peek();
+        public ref readonly ExecutionContext ExecutionContext
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get { return ref _executionContexts.Peek(); }
+        }
 
         public GlobalSymbolRegistry GlobalSymbolRegistry { get; }
 
@@ -247,17 +251,17 @@ namespace Jint
         }
         #endregion
 
-        public ExecutionContext EnterExecutionContext(LexicalEnvironment lexicalEnvironment, LexicalEnvironment variableEnvironment, JsValue thisBinding)
+        public void EnterExecutionContext(
+            LexicalEnvironment lexicalEnvironment,
+            LexicalEnvironment variableEnvironment,
+            JsValue thisBinding)
         {
-            var executionContext = new ExecutionContext
-                {
-                    LexicalEnvironment = lexicalEnvironment,
-                    VariableEnvironment = variableEnvironment,
-                    ThisBinding = thisBinding
-                };
-            _executionContexts.Push(executionContext);
+            var context = new ExecutionContext(
+                lexicalEnvironment,
+                variableEnvironment,
+                thisBinding);
 
-            return executionContext;
+            _executionContexts.Push(context);
         }
 
         public Engine SetValue(string name, Delegate value)
@@ -376,12 +380,12 @@ namespace Jint
         {
             if (_maxStatements > 0 && _statementsCount++ > _maxStatements)
             {
-                throw new StatementsCountOverflowException();
+                ThrowStatementsCountOverflowException();
             }
 
             if (_timeoutTicks > 0 && _timeoutTicks < DateTime.UtcNow.Ticks)
             {
-                throw new TimeoutException();
+                ThrowTimeoutException();
             }
 
             _lastSyntaxNode = statement;
@@ -433,7 +437,7 @@ namespace Jint
                     return _statements.ExecuteSwitchStatement((SwitchStatement) statement);
 
                 case Nodes.FunctionDeclaration:
-                    return Completion.Empty;
+                    return new Completion(CompletionType.Normal, null, null);
 
                 case Nodes.ThrowStatement:
                     return _statements.ExecuteThrowStatement((ThrowStatement) statement);
@@ -454,7 +458,8 @@ namespace Jint
                     return _statements.ExecuteProgram((Program) statement);
 
                 default:
-                    throw new ArgumentOutOfRangeException();
+                    ThrowArgumentOutOfRange();
+                    return new Completion(CompletionType.Normal, null, null);
             }
         }
 
@@ -513,30 +518,50 @@ namespace Jint
                     return _expressions.EvaluateUnaryExpression((UnaryExpression) expression);
 
                 default:
-                    throw new ArgumentOutOfRangeException();
+                    ThrowArgumentOutOfRange();
+                    return null;
             }
         }
 
-
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-8.7.1
         /// </summary>
-        /// <param name="value"></param>
-        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue GetValue(object value)
         {
-            return GetValue(value, false);
+            if (value is JsValue jsValue)
+            {
+                return jsValue;
+            }
+
+            return GetValueFromReference(value, false);
+        }
+        
+        
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal JsValue GetValue(in Completion completion)
+        {
+            var value = completion.Value;
+            if (value is JsValue jsValue)
+            {
+                return jsValue;
+            }
+            return GetValueFromReference(completion.Value, false);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         internal JsValue GetValue(object value, bool returnReferenceToPool)
         {
-            var jsValue = value as JsValue;
-            if (!ReferenceEquals(jsValue, null))
+            if (value is JsValue jsValue)
             {
                 return jsValue;
             }
 
+            return GetValueFromReference(value, returnReferenceToPool);
+        }
+       
+        internal JsValue GetValueFromReference(object value, bool returnReferenceToPool)
+        {
             var reference = value as Reference;
             if (reference == null)
             {
@@ -592,7 +617,7 @@ namespace Jint
                     }
 
                     var getter = desc.Get;
-                    if (ReferenceEquals(getter, Undefined.Instance))
+                    if (getter.IsUndefined())
                     {
                         return Undefined.Instance;
                     }
@@ -629,7 +654,7 @@ namespace Jint
             {
                 if (reference.IsStrict())
                 {
-                    throw new JavaScriptException(ReferenceError);
+                    ThrowReferenceError();
                 }
 
                 Global.Put(reference.GetReferencedName(), value, false);
@@ -649,11 +674,10 @@ namespace Jint
             else
             {
                 var baseValue = reference.GetBase();
-                var record = baseValue as EnvironmentRecord;
-
-                if (ReferenceEquals(record, null))
+                if (!(baseValue is EnvironmentRecord record))
                 {
-                    throw new ArgumentNullException();
+                    ThrowArgumentNullException();
+                    return;
                 }
 
                 record.SetMutableBinding(reference.GetReferencedName(), value, reference.IsStrict());
@@ -663,10 +687,6 @@ namespace Jint
         /// <summary>
         /// Used by PutValue when the reference has a primitive base value
         /// </summary>
-        /// <param name="b"></param>
-        /// <param name="name"></param>
-        /// <param name="value"></param>
-        /// <param name="throwOnError"></param>
         public void PutPrimitiveBase(JsValue b, string name, JsValue value, bool throwOnError)
         {
             var o = TypeConverter.ToObject(this, b);
@@ -674,9 +694,8 @@ namespace Jint
             {
                 if (throwOnError)
                 {
-                    throw new JavaScriptException(TypeError);
+                    ThrowTypeError();
                 }
-
                 return;
             }
 
@@ -686,9 +705,8 @@ namespace Jint
             {
                 if (throwOnError)
                 {
-                    throw new JavaScriptException(TypeError);
+                    ThrowTypeError();
                 }
-
                 return;
             }
 
@@ -703,7 +721,7 @@ namespace Jint
             {
                 if (throwOnError)
                 {
-                    throw new JavaScriptException(TypeError);
+                    ThrowTypeError();
                 }
             }
         }
@@ -796,15 +814,11 @@ namespace Jint
         /// <param name="propertyName">The name of the property to return.</param>
         public JsValue GetValue(JsValue scope, string propertyName)
         {
-            if (string.IsNullOrEmpty(propertyName))
-            {
-                throw new ArgumentException("propertyName");
-            }
+            AssertNotNullOrEmpty(nameof(propertyName), propertyName);
 
             var reference = ReferencePool.Rent(scope, propertyName, _isStrict);
             var jsValue = GetValue(reference);
             ReferencePool.Return(reference);
-
             return jsValue;
         }
 
@@ -921,5 +935,49 @@ namespace Jint
 
             return canReleaseArgumentsInstance;
         }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal void UpdateLexicalEnvironment(LexicalEnvironment newEnv)
+        {
+            _executionContexts.ReplaceTopLexicalEnvironment(newEnv);
+        }
+
+        private static void ThrowTimeoutException()
+        {
+            throw new TimeoutException();
+        }
+
+        private static void ThrowStatementsCountOverflowException()
+        {
+            throw new StatementsCountOverflowException();
+        }
+
+        private static void ThrowArgumentOutOfRange()
+        {
+            throw new ArgumentOutOfRangeException();
+        }
+        
+        private static void ThrowArgumentNullException()
+        {
+            throw new ArgumentNullException();
+        }
+        
+        private void ThrowReferenceError()
+        {
+            throw new JavaScriptException(ReferenceError);
+        }
+        
+        private static void AssertNotNullOrEmpty(string propertyname, string propertyValue)
+        {
+            if (string.IsNullOrEmpty(propertyValue))
+            {
+                throw new ArgumentException(propertyname);
+            }
+        }
+        
+        private void ThrowTypeError()
+        {
+            throw new JavaScriptException(TypeError);
+        }
     }
 }

+ 65 - 0
Jint/JsValueExtensions.cs

@@ -0,0 +1,65 @@
+using System;
+using System.Runtime.CompilerServices;
+using Jint.Native;
+using Jint.Runtime;
+
+namespace Jint
+{
+    public static class JsValueExtensions
+    {
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static bool AsBoolean(this JsValue value)
+        {
+            if (value._type != Types.Boolean)
+            {
+                ThrowWrongTypeException("The value is not a boolean");
+            }
+
+            return ((JsBoolean) value)._value;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static double AsNumber(this JsValue value)
+        {
+            if (value._type != Types.Number)
+            {
+                ThrowWrongTypeException("The value is not a number");
+            }
+
+            return ((JsNumber) value)._value;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static string AsString(this JsValue value)
+        {
+            if (value._type != Types.String)
+            {
+                ThrowWrongTypeException("The value is not a string");
+            }
+
+            return AsStringWithoutTypeCheck(value);
+        }
+        
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static string AsStringWithoutTypeCheck(this JsValue value)
+        {
+            return value.ToString();
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static string AsSymbol(this JsValue value)
+        {
+            if (value._type != Types.Symbol)
+            {
+                ThrowWrongTypeException("The value is not a symbol");
+            }
+
+            return ((JsSymbol) value)._value;
+        }
+        
+        private static void ThrowWrongTypeException(string message)
+        {
+            throw new ArgumentException(message);
+        }
+    }
+}

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

@@ -25,7 +25,7 @@ namespace Jint.Native.Argument
 
         private bool _initialized;
 
-        internal ArgumentsInstance(Engine engine) : base(engine)
+        internal ArgumentsInstance(Engine engine) : base(engine, objectClass: "Arguments")
         {
         }
 
@@ -101,8 +101,6 @@ namespace Jint.Native.Argument
 
         public ObjectInstance ParameterMap { get; set; }
 
-        public override string Class => "Arguments";
-
         public override PropertyDescriptor GetOwnProperty(string propertyName)
         {
             EnsureInitialized();
@@ -194,7 +192,7 @@ namespace Jint.Native.Argument
                     else
                     {
                         var descValue = desc.Value;
-                        if (!ReferenceEquals(descValue, null) && !ReferenceEquals(descValue, Undefined))
+                        if (!ReferenceEquals(descValue, null) && !descValue.IsUndefined())
                         {
                             map.Put(propertyName, descValue, throwOnError);
                         }

+ 1 - 1
Jint/Native/Array/ArrayConstructor.cs

@@ -61,7 +61,7 @@ namespace Jint.Native.Array
             var capacity = arguments.Length > 0 ? (uint) arguments.Length : 0;
             if (arguments.Length == 1 && arguments[0].Type == Types.Number)
             {
-                var number = arguments[0].AsNumber();
+                var number = ((JsNumber) arguments[0])._value;
                 if (number > 0)
                 {
                     capacity = (uint) number;

+ 13 - 11
Jint/Native/Array/ArrayInstance.cs

@@ -13,8 +13,6 @@ namespace Jint.Native.Array
 {
     public class ArrayInstance : ObjectInstance, IEnumerable<JsValue>
     {
-        private readonly Engine _engine;
-
         private const string PropertyNameLength = "length";
         private const int PropertyNameLengthLength = 6;
 
@@ -26,9 +24,8 @@ namespace Jint.Native.Array
         private PropertyDescriptor[] _dense;
         private Dictionary<uint, PropertyDescriptor> _sparse;
 
-        public ArrayInstance(Engine engine, uint capacity = 0) : base(engine)
+        public ArrayInstance(Engine engine, uint capacity = 0) : base(engine, objectClass: "Array")
         {
-            _engine = engine;
             if (capacity < MaxDenseArrayLength)
             {
                 _dense = capacity > 0 ? new PropertyDescriptor[capacity] : System.Array.Empty<PropertyDescriptor>();
@@ -39,9 +36,8 @@ namespace Jint.Native.Array
             }
         }
 
-        public ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine)
+        public ArrayInstance(Engine engine, PropertyDescriptor[] items) : base(engine, objectClass: "Array")
         {
-            _engine = engine;
             int length = 0;
             if (items == null || items.Length == 0)
             {
@@ -57,16 +53,13 @@ namespace Jint.Native.Array
             _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
 
-        public ArrayInstance(Engine engine, Dictionary<uint, PropertyDescriptor> items) : base(engine)
+        public ArrayInstance(Engine engine, Dictionary<uint, PropertyDescriptor> items) : base(engine, objectClass: "Array")
         {
-            _engine = engine;
             _sparse = items;
             var length = items?.Count ?? 0;
             _length = new PropertyDescriptor(length, PropertyFlag.OnlyWritable);
         }
 
-        public override string Class => "Array";
-
         /// Implementation from ObjectInstance official specs as the one
         /// in ObjectInstance is optimized for the general case and wouldn't work
         /// for arrays
@@ -457,8 +450,17 @@ namespace Jint.Native.Array
                 return uint.MaxValue;
             }
 
-            ulong result = (uint) d;
+            if (p.Length > 1)
+            {
+                return StringAsIndex(d, p);
+            }
+            
+            return (uint) d;
+        }
 
+        private static uint StringAsIndex(int d, string p)
+        {
+            ulong result = (uint) d;
             for (int i = 1; i < p.Length; i++)
             {
                 d = p[i] - '0';

+ 11 - 9
Jint/Native/Array/ArrayPrototype.cs

@@ -509,24 +509,26 @@ namespace Jint.Native.Array
 
             var compareArg = arguments.At(0);
             ICallable compareFn = null;
-            if (!ReferenceEquals(compareArg, Undefined))
+            if (!compareArg.IsUndefined())
             {
                 compareFn = compareArg.TryCast<ICallable>(x => throw new JavaScriptException(Engine.TypeError, "The sort argument must be a function"));
             }
 
             int Comparer(JsValue x, JsValue y)
             {
-                if (ReferenceEquals(x, Undefined) && ReferenceEquals(y, Undefined))
+                var xUndefined = x.IsUndefined();
+                var yUndefined = y.IsUndefined();
+                if (xUndefined && yUndefined)
                 {
                     return 0;
                 }
 
-                if (ReferenceEquals(x, Undefined))
+                if (xUndefined)
                 {
                     return 1;
                 }
 
-                if (ReferenceEquals(y, Undefined))
+                if (yUndefined)
                 {
                     return -1;
                 }
@@ -598,7 +600,7 @@ namespace Jint.Native.Array
             }
 
             uint final;
-            if (ReferenceEquals(end, Undefined))
+            if (end.IsUndefined())
             {
                 final = TypeConverter.ToUint32(len);
             }
@@ -700,7 +702,7 @@ namespace Jint.Native.Array
             var separator = arguments.At(0);
             var o = ArrayOperations.For(Engine, thisObj);
             var len = o.GetLength();
-            if (ReferenceEquals(separator, Undefined))
+            if (separator.IsUndefined())
             {
                 separator = ",";
             }
@@ -715,7 +717,7 @@ namespace Jint.Native.Array
 
             string StringFromJsValue(JsValue value)
             {
-                return ReferenceEquals(value, Undefined) || ReferenceEquals(value, Null)
+                return value.IsUndefined() || value.IsNull()
                     ? ""
                     : TypeConverter.ToString(value);
             }
@@ -749,7 +751,7 @@ namespace Jint.Native.Array
             }
 
             JsValue r;
-            if (!array.TryGetValue(0, out var firstElement) || ReferenceEquals(firstElement, Null) || ReferenceEquals(firstElement, Undefined))
+            if (!array.TryGetValue(0, out var firstElement) || firstElement.IsNull() || firstElement.IsUndefined())
             {
                 r = "";
             }
@@ -764,7 +766,7 @@ namespace Jint.Native.Array
             for (uint k = 1; k < len; k++)
             {
                 string s = r + separator;
-                if (!array.TryGetValue(k, out var nextElement) || ReferenceEquals(nextElement, Null))
+                if (!array.TryGetValue(k, out var nextElement) || nextElement.IsNull())
                 {
                     r = "";
                 }

+ 1 - 3
Jint/Native/Boolean/BooleanInstance.cs

@@ -6,12 +6,10 @@ namespace Jint.Native.Boolean
     public class BooleanInstance : ObjectInstance, IPrimitiveInstance
     {
         public BooleanInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "Boolean")
         {
         }
 
-        public override string Class => "Boolean";
-
         Types IPrimitiveInstance.Type => Types.Boolean;
 
         JsValue IPrimitiveInstance.PrimitiveValue => PrimitiveValue;

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

@@ -51,7 +51,7 @@ namespace Jint.Native.Boolean
         private JsValue ToBooleanString(JsValue thisObj, JsValue[] arguments)
         {
             var b = ValueOf(thisObj, Arguments.Empty);
-            return b.AsBoolean() ? "true" : "false";
+            return ((JsBoolean) b)._value ? "true" : "false";
         }
     }
 }

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

@@ -105,7 +105,7 @@ namespace Jint.Native.Date
             return TimeClip(ConstructTimeValue(arguments, useUtc: true));
         }
 
-        private JsValue Now(JsValue thisObj, JsValue[] arguments)
+        private static JsValue Now(JsValue thisObj, JsValue[] arguments)
         {
             return System.Math.Floor((DateTime.UtcNow - Epoch).TotalMilliseconds);
         }
@@ -131,7 +131,7 @@ namespace Jint.Native.Date
                 var v = TypeConverter.ToPrimitive(arguments[0]);
                 if (v.IsString())
                 {
-                    return Construct(Parse(Undefined, Arguments.From(v)).AsNumber());
+                    return Construct(((JsNumber) Parse(Undefined, Arguments.From(v)))._value);
                 }
 
                 return Construct(TypeConverter.ToNumber(v));

+ 1 - 9
Jint/Native/Date/DateInstance.cs

@@ -13,18 +13,10 @@ namespace Jint.Native.Date
         internal static readonly double Min = -(new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc) - DateTime.MinValue).TotalMilliseconds;
 
         public DateInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "Date")
         {
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "Date";
-            }
-        }
-
         public DateTime ToDateTime()
         {
             if (double.IsNaN(PrimitiveValue) || PrimitiveValue > Max || PrimitiveValue < Min)

+ 2 - 2
Jint/Native/Date/DatePrototype.cs

@@ -562,9 +562,9 @@ namespace Jint.Native.Date
         {
             var o = TypeConverter.ToObject(Engine, thisObj);
             var tv = TypeConverter.ToPrimitive(o, Types.Number);
-            if (tv.IsNumber() && double.IsInfinity(tv.AsNumber()))
+            if (tv.IsNumber() && double.IsInfinity(((JsNumber) tv)._value))
             {
-                return JsValue.Null;
+                return Null;
             }
 
             var toIso = o.Get("toISOString");

+ 3 - 2
Jint/Native/Error/ErrorConstructor.cs

@@ -47,9 +47,10 @@ namespace Jint.Native.Error
             instance.Prototype = PrototypeObject;
             instance.Extensible = true;
 
-            if (!ReferenceEquals(arguments.At(0), Undefined))
+            var jsValue = arguments.At(0);
+            if (!jsValue.IsUndefined())
             {
-                instance.Put("message", TypeConverter.ToString(arguments.At(0)), false);
+                instance.Put("message", TypeConverter.ToString(jsValue), false);
             }
 
             return instance;

+ 1 - 9
Jint/Native/Error/ErrorInstance.cs

@@ -6,19 +6,11 @@ namespace Jint.Native.Error
     public class ErrorInstance : ObjectInstance
     {
         public ErrorInstance(Engine engine, string name)
-            : base(engine)
+            : base(engine, objectClass: "Error")
         {
             FastAddProperty("name", name, true, false, true);
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "Error";
-            }
-        }
-
         public override string ToString()
         {
             return Engine.Error.PrototypeObject.ToString(this, Arguments.Empty).ToObject().ToString();

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

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

+ 0 - 3
Jint/Native/Function/EvalFunctionInstance.cs

@@ -10,11 +10,8 @@ namespace Jint.Native.Function
     {
         private static readonly ParserOptions ParserOptions = new ParserOptions { AdaptRegexp = true, Tolerant = false };
 
-        private readonly Engine _engine;
-
         public EvalFunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict) : base(engine, parameters, scope, strict)
         {
-            _engine = engine;
             Prototype = Engine.Function.PrototypeObject;
             SetOwnProperty("length", new PropertyDescriptor(1, PropertyFlag.AllForbidden));
         }

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

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

+ 12 - 6
Jint/Native/Function/FunctionInstance.cs

@@ -15,12 +15,20 @@ namespace Jint.Native.Function
         private const string PropertyNameLength = "length";
         private const int PropertyNameLengthLength = 6;
         private PropertyDescriptor _length;
+        
+        protected FunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict)
+            : this(engine, parameters, scope, strict, objectClass: "Function")
+        {
+        }
 
-        private readonly Engine _engine;
-
-        protected FunctionInstance(Engine engine, string[] parameters, LexicalEnvironment scope, bool strict) : base(engine)
+        protected FunctionInstance(
+            Engine engine, 
+            string[] parameters, 
+            LexicalEnvironment scope, 
+            bool strict, 
+            in string objectClass)
+            : base(engine, objectClass)
         {
-            _engine = engine;
             FormalParameters = parameters;
             Scope = scope;
             Strict = strict;
@@ -76,8 +84,6 @@ namespace Jint.Native.Function
             }
         }
 
-        public override string Class => "Function";
-
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-15.3.5.4
         /// </summary>

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

@@ -94,7 +94,7 @@ namespace Jint.Native.Function
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            if (ReferenceEquals(argArray, Null) || ReferenceEquals(argArray, Undefined))
+            if (argArray.IsNull() || argArray.IsUndefined())
             {
                 return func.Call(thisArg, Arguments.Empty);
             }
@@ -105,7 +105,7 @@ namespace Jint.Native.Function
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            var len = argArrayObj.Get("length").AsNumber();
+            var len = ((JsNumber) argArrayObj.Get("length"))._value;
             uint n = TypeConverter.ToUint32(len);
 
             var argList = n < 10 

+ 14 - 15
Jint/Native/Function/ScriptFunctionInstance.cs

@@ -35,14 +35,14 @@ namespace Jint.Native.Function
             _functionDeclaration = functionDeclaration;
 
             Extensible = true;
-            Prototype = engine.Function.PrototypeObject;
+            Prototype = _engine.Function.PrototypeObject;
 
             DefineOwnProperty("length", new PropertyDescriptor(JsNumber.Create(FormalParameters.Length), PropertyFlag.AllForbidden), false);
 
             var proto = new ObjectInstanceWithConstructor(engine, this)
             {
                 Extensible = true,
-                Prototype = Engine.Object.PrototypeObject
+                Prototype = _engine.Object.PrototypeObject
             };
 
             SetOwnProperty("prototype", new PropertyDescriptor(proto, PropertyFlag.OnlyWritable));
@@ -116,7 +116,6 @@ namespace Jint.Native.Function
             base.RemoveOwnProperty(propertyName);
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static string[] GetParameterNames(IFunction functionDeclaration)
         {
             var list = functionDeclaration.Params;
@@ -152,39 +151,39 @@ namespace Jint.Native.Function
                 {
                     thisBinding = thisArg;
                 }
-                else if (ReferenceEquals(thisArg, Undefined) || ReferenceEquals(thisArg, Null))
+                else if (thisArg.IsUndefined() || thisArg.IsNull())
                 {
-                    thisBinding = Engine.Global;
+                    thisBinding = _engine.Global;
                 }
                 else if (!thisArg.IsObject())
                 {
-                    thisBinding = TypeConverter.ToObject(Engine, thisArg);
+                    thisBinding = TypeConverter.ToObject(_engine, thisArg);
                 }
                 else
                 {
                     thisBinding = thisArg;
                 }
 
-                var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(Engine, Scope);
+                var localEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, Scope);
 
-                Engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
+                _engine.EnterExecutionContext(localEnv, localEnv, thisBinding);
 
                 try
                 {
-                    var argumentInstanceRented = Engine.DeclarationBindingInstantiation(
+                    var argumentInstanceRented = _engine.DeclarationBindingInstantiation(
                         DeclarationBindingType.FunctionCode,
                         _functionDeclaration.HoistingScope.FunctionDeclarations,
                         _functionDeclaration.HoistingScope.VariableDeclarations,
                         this,
                         arguments);
 
-                    var result = Engine.ExecuteStatement(_functionDeclaration.Body);
+                    var result = _engine.ExecuteStatement(_functionDeclaration.Body);
                     
                     var value = result.GetValueOrDefault();
                     
                     // we can safely release arguments if they don't escape the scope
                     if (argumentInstanceRented
-                        && Engine.ExecutionContext.LexicalEnvironment?.Record is DeclarativeEnvironmentRecord der
+                        && _engine.ExecutionContext.LexicalEnvironment?.Record is DeclarativeEnvironmentRecord der
                         && !(result.Value is ArgumentsInstance))
                     {
                         der.ReleaseArguments();
@@ -192,7 +191,7 @@ namespace Jint.Native.Function
 
                     if (result.Type == CompletionType.Throw)
                     {
-                        var ex = new JavaScriptException(value).SetCallstack(Engine, result.Location);
+                        var ex = new JavaScriptException(value).SetCallstack(_engine, result.Location);
                         throw ex;
                     }
 
@@ -203,7 +202,7 @@ namespace Jint.Native.Function
                 }
                 finally
                 {
-                    Engine.LeaveExecutionContext();
+                    _engine.LeaveExecutionContext();
                 }
 
                 return Undefined;
@@ -218,10 +217,10 @@ namespace Jint.Native.Function
         public ObjectInstance Construct(JsValue[] arguments)
         {
             var proto = Get("prototype").TryCast<ObjectInstance>();
-            var obj = new ObjectInstance(Engine)
+            var obj = new ObjectInstance(_engine)
             {
                 Extensible = true,
-                Prototype = proto ?? Engine.Object.PrototypeObject
+                Prototype = proto ?? _engine.Object.PrototypeObject
             };
 
             var result = Call(obj, arguments).TryCast<ObjectInstance>();

+ 0 - 3
Jint/Native/Function/ThrowTypeError.cs

@@ -5,11 +5,8 @@ namespace Jint.Native.Function
 {
     public sealed class ThrowTypeError : FunctionInstance
     {
-        private readonly Engine _engine;
-
         public ThrowTypeError(Engine engine): base(engine, System.Array.Empty<string>(), engine.GlobalEnvironment, false)
         {
-            _engine = engine;
             DefineOwnProperty("length", new PropertyDescriptor(0, PropertyFlag.AllForbidden), false);
             Extensible = false;
         }

+ 2 - 2
Jint/Native/Global/GlobalObject.cs

@@ -120,7 +120,7 @@ namespace Jint.Native.Global
 
             try
             {
-                return sign * Parse(s, radix).AsNumber();
+                return sign * Parse(s, radix);
             }
             catch
             {
@@ -129,7 +129,7 @@ namespace Jint.Native.Global
 
         }
 
-        private static JsValue Parse(string number, int radix)
+        private static double Parse(string number, int radix)
         {
             if (number == "")
             {

+ 0 - 7
Jint/Native/JsBoolean.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Diagnostics.Contracts;
 using Jint.Runtime;
 
 namespace Jint.Native
@@ -19,12 +18,6 @@ namespace Jint.Native
             _value = value;
         }
 
-        [Pure]
-        public override bool AsBoolean()
-        {
-            return _value;
-        }
-
         public override object ToObject()
         {
             return _value ? BoxedTrue : BoxedFalse;

+ 0 - 7
Jint/Native/JsNumber.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Diagnostics.Contracts;
 using Jint.Runtime;
 
 namespace Jint.Native
@@ -49,12 +48,6 @@ namespace Jint.Native
             _value = value;
         }
 
-        [Pure]
-        public override double AsNumber()
-        {
-            return _value;
-        }
-
         public override object ToObject()
         {
             return _value;

+ 4 - 17
Jint/Native/JsString.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Diagnostics.Contracts;
 using System.Text;
 using Jint.Runtime;
 
@@ -14,7 +13,7 @@ namespace Jint.Native
         private static readonly JsString Empty = new JsString("");
         private static readonly JsString NullString = new JsString("null");
 
-        private string _value;
+        internal string _value;
 
         static JsString()
         {
@@ -43,17 +42,6 @@ namespace Jint.Native
             _value = value.ToString();
         }
 
-        [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)));
@@ -156,8 +144,7 @@ namespace Jint.Native
                 }
             }
 
-            [Pure]
-            public override string AsString()
+            public override string ToString()
             {
                 if (_dirty)
                 {
@@ -203,13 +190,13 @@ namespace Jint.Native
 
                 if (other.Type == Types.String)
                 {
-                    var otherString = other.AsString();
+                    var otherString = other.AsStringWithoutTypeCheck();
                     if (otherString.Length != _stringBuilder.Length)
                     {
                         return false;
                     }
 
-                    return AsString().Equals(otherString);
+                    return ToString().Equals(otherString);
                 }
 
                 return base.Equals(other);

+ 1 - 11
Jint/Native/JsSymbol.cs

@@ -8,7 +8,7 @@ namespace Jint.Native
     /// </summary>
     public sealed class JsSymbol : JsValue, IEquatable<JsSymbol>
     {
-        private readonly string _value;
+        internal readonly string _value;
 
         public JsSymbol(string value) : base(Types.Symbol)
         {
@@ -20,16 +20,6 @@ namespace Jint.Native
             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))

+ 7 - 32
Jint/Native/JsValue.cs

@@ -19,7 +19,7 @@ namespace Jint.Native
     {
         public static readonly JsValue Undefined = new JsUndefined();
         public static readonly JsValue Null = new JsNull();
-        private readonly Types _type;
+        internal readonly Types _type;
 
         protected JsValue(Types type)
         {
@@ -30,7 +30,7 @@ namespace Jint.Native
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public bool IsPrimitive()
         {
-            return _type != Types.Object && Type != Types.None;
+            return _type != Types.Object && _type != Types.None;
         }
 
         [Pure]
@@ -174,7 +174,7 @@ namespace Jint.Native
             }
 
             // TODO not implemented
-            return Completion.EmptyUndefined;
+            return new Completion(CompletionType.Normal, Native.Undefined.Instance, null);
         }
 
         [Pure]
@@ -183,8 +183,7 @@ namespace Jint.Native
         {
             if (IsObject())
             {
-                var o = this as T;
-                if (o != null)
+                if (this is T o)
                 {
                     return o;
                 }
@@ -213,30 +212,6 @@ namespace Jint.Native
             return null;
         }
 
-        [Pure]
-        public virtual bool AsBoolean()
-        {
-            throw new ArgumentException("The value is not a boolean");
-        }
-
-        [Pure]
-        public virtual string AsString()
-        {
-            throw new ArgumentException("The value is not a string");
-        }
-
-        [Pure]
-        public virtual string AsSymbol()
-        {
-            throw new ArgumentException("The value is not a symbol");
-        }
-
-        [Pure]
-        public virtual double AsNumber()
-        {
-            throw new ArgumentException("The value is not a number");
-        }
-
         // ReSharper disable once ConvertToAutoPropertyWhenPossible // PERF
         public Types Type
         {
@@ -511,13 +486,13 @@ namespace Jint.Native
                         Value = "null";
                         break;
                     case Types.Boolean:
-                        Value = value.AsBoolean() + " (bool)";
+                        Value = ((JsBoolean) value)._value + " (bool)";
                         break;
                     case Types.String:
-                        Value = value.AsString() + " (string)";
+                        Value = value.AsStringWithoutTypeCheck() + " (string)";
                         break;
                     case Types.Number:
-                        Value = value.AsNumber() + " (number)";
+                        Value = ((JsNumber) value)._value + " (number)";
                         break;
                     case Types.Object:
                         Value = value.AsObject().GetType().Name;

+ 2 - 12
Jint/Native/Json/JsonInstance.cs

@@ -7,24 +7,14 @@ namespace Jint.Native.Json
 {
     public sealed class JsonInstance : ObjectInstance
     {
-        private readonly Engine _engine;
         private JsValue _reviver;
 
         private JsonInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "JSON")
         {
-            _engine = engine;
             Extensible = true;
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "JSON";
-            }
-        }
-
         public static JsonInstance CreateJsonObject(Engine engine)
         {
             var json = new JsonInstance(engine);
@@ -144,7 +134,7 @@ namespace Jint.Native.Json
             }
 
             var serializer = new JsonSerializer(_engine);
-            if (ReferenceEquals(value, Undefined) && ReferenceEquals(replacer, Undefined)) {
+            if (value.IsUndefined() && replacer.IsUndefined()) {
                 return Undefined;
             }
 

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

@@ -757,7 +757,7 @@ namespace Jint.Native.Json
             return obj;
         }
 
-        private bool PropertyNameContainsInvalidChar0To31(string s)
+        private static bool PropertyNameContainsInvalidChar0To31(string s)
         {
             const int max = 31;
 

+ 15 - 17
Jint/Native/Json/JsonSerializer.cs

@@ -54,7 +54,7 @@ namespace Jint.Native.Json
                         string item = null;
                         if (v.IsString())
                         {
-                            item = v.AsString();
+                            item = v.AsStringWithoutTypeCheck();
                         }
                         else if (v.IsNumber())
                         {
@@ -94,8 +94,10 @@ namespace Jint.Native.Json
             // defining the gap
             if (space.IsNumber())
             {
-                if (space.AsNumber() > 0) {
-                    _gap = new System.String(' ', (int)System.Math.Min(10, space.AsNumber()));
+                var number = ((JsNumber) space)._value;
+                if (number > 0)
+                {
+                    _gap = new string(' ', (int) System.Math.Min(10, number));
                 }
                 else
                 {
@@ -104,7 +106,7 @@ namespace Jint.Native.Json
             }
             else if (space.IsString())
             {
-                var stringSpace = space.AsString();
+                var stringSpace = space.AsStringWithoutTypeCheck();
                 _gap = stringSpace.Length <= 10 ? stringSpace : stringSpace.Substring(0, 10);
             }
             else
@@ -170,24 +172,20 @@ namespace Jint.Native.Json
                 return "null";
             }
 
-            if (value.IsBoolean() && value.AsBoolean())
-            {
-                return "true";
-            }
-
-            if (value.IsBoolean() && !value.AsBoolean())
+            if (value.IsBoolean())
             {
-                return "false";
+                return ((JsBoolean) value)._value ? "true" : "false";
             }
 
             if (value.IsString())
             {
-                return Quote(value.AsString());
+                return Quote(value.AsStringWithoutTypeCheck());
             }
 
             if (value.IsNumber())
             {
-                if (GlobalObject.IsFinite(Undefined.Instance, Arguments.From(value)).AsBoolean())
+                var isFinite = GlobalObject.IsFinite(Undefined.Instance, Arguments.From(value));
+                if (((JsBoolean) isFinite)._value)
                 {
                     return TypeConverter.ToString(value);
                 }
@@ -210,7 +208,7 @@ namespace Jint.Native.Json
             return JsValue.Undefined;
         }
 
-        private string Quote(string value)
+        private static string Quote(string value)
         {
             var sb = new System.Text.StringBuilder("\"");
 
@@ -266,9 +264,9 @@ namespace Jint.Native.Json
             for (int i = 0; i < len; i++)
             {
                 var strP = Str(TypeConverter.ToString(i), value);
-                if (ReferenceEquals(strP, JsValue.Undefined))
+                if (strP.IsUndefined())
                     strP = "null";
-                partial.Add(strP.AsString());
+                partial.Add(strP.AsStringWithoutTypeCheck());
             }
             if (partial.Count == 0)
             {
@@ -326,7 +324,7 @@ namespace Jint.Native.Json
             foreach (var p in k)
             {
                 var strP = Str(p, value);
-                if (!ReferenceEquals(strP, JsValue.Undefined))
+                if (!strP.IsUndefined())
                 {
                     var member = Quote(p) + ":";
                     if (_gap != "")

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

@@ -8,18 +8,10 @@ namespace Jint.Native.Math
 {
     public sealed class MathInstance : ObjectInstance
     {
-        private static Random _random = new Random();
-        
-        private MathInstance(Engine engine):base(engine)
-        {
-        }
+        private static readonly Random _random = new Random();
 
-        public override string Class
+        private MathInstance(Engine engine) : base(engine, "Math")
         {
-            get
-            {
-                return "Math";
-            }
         }
 
         public static MathInstance CreateMathObject(Engine engine)

+ 1 - 4
Jint/Native/Number/Dtoa/FastDtoaBuilder.cs

@@ -2,8 +2,6 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-using System.Runtime.CompilerServices;
-
 namespace Jint.Native.Number.Dtoa
 {
     internal class FastDtoaBuilder
@@ -131,8 +129,7 @@ namespace Jint.Native.Number.Dtoa
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
         };
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        private void Fill<T>(T[] array, int fromIndex, int toIndex, T val)
+        private static void Fill(char[] array, int fromIndex, int toIndex, char val)
         {
             for (int i = fromIndex; i < toIndex; i++)
             {

+ 1 - 3
Jint/Native/Number/NumberInstance.cs

@@ -10,12 +10,10 @@ namespace Jint.Native.Number
         private static readonly long NegativeZeroBits = BitConverter.DoubleToInt64Bits(-0.0);
 
         public NumberInstance(Engine engine)
-            : base(engine)
+            : base(engine, "Number")
         {
         }
 
-        public override string Class => "Number";
-
         Types IPrimitiveInstance.Type => Types.Number;
 
         JsValue IPrimitiveInstance.PrimitiveValue => NumberData;

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

@@ -137,7 +137,7 @@ namespace Jint.Native.Number
         {
             var x = TypeConverter.ToNumber(thisObj);
 
-            if (ReferenceEquals(arguments.At(0), Undefined))
+            if (arguments.At(0).IsUndefined())
             {
                 return TypeConverter.ToString(x);
             }
@@ -172,7 +172,7 @@ namespace Jint.Native.Number
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            var radix = ReferenceEquals(arguments.At(0), Undefined) 
+            var radix = arguments.At(0).IsUndefined() 
                 ? 10 
                 : (int) TypeConverter.ToInteger(arguments.At(0));
 

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

@@ -11,11 +11,8 @@ namespace Jint.Native.Object
 {
     public sealed class ObjectConstructor : FunctionInstance, IConstructor
     {
-        private readonly Engine _engine;
-
         private ObjectConstructor(Engine engine) : base(engine, null, null, false)
         {
-            _engine = engine;
         }
 
         public static ObjectConstructor CreateObjectConstructor(Engine engine)
@@ -65,7 +62,7 @@ namespace Jint.Native.Object
                 return Construct(arguments);
             }
 
-            if(ReferenceEquals(arguments[0], Null) || ReferenceEquals(arguments[0], Undefined))
+            if(arguments[0].IsNull() || arguments[0].IsUndefined())
             {
                 return Construct(arguments);
             }
@@ -147,7 +144,7 @@ namespace Jint.Native.Object
             var ownProperties = o.GetOwnProperties().ToList();
             if (o is StringInstance s)
             {
-                var length = s.PrimitiveValue.AsString().Length;
+                var length = s.PrimitiveValue.AsStringWithoutTypeCheck().Length;
                 array = Engine.Array.Construct(ownProperties.Count + length);
                 for (var i = 0; i < length; i++)
                 {
@@ -173,7 +170,7 @@ namespace Jint.Native.Object
             var oArg = arguments.At(0);
 
             var o = oArg.TryCast<ObjectInstance>();
-            if (ReferenceEquals(o, null) && !ReferenceEquals(oArg, Null))
+            if (ReferenceEquals(o, null) && !oArg.IsNull())
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
@@ -182,7 +179,7 @@ namespace Jint.Native.Object
             obj.Prototype = o;
 
             var properties = arguments.At(1);
-            if (!ReferenceEquals(properties, Undefined))
+            if (!properties.IsUndefined())
             {
                 DefineProperties(thisObject, new [] {obj, properties});
             }

+ 19 - 10
Jint/Native/Object/ObjectInstance.cs

@@ -20,13 +20,22 @@ namespace Jint.Native.Object
     {
         private MruPropertyCache2<PropertyDescriptor> _intrinsicProperties;
         private MruPropertyCache2<PropertyDescriptor> _properties;
-
-        public ObjectInstance(Engine engine) : base(Types.Object)
+        
+        private readonly string _class;
+        protected readonly Engine _engine;
+        
+        public ObjectInstance(Engine engine) : this(engine, "Object")
+        {
+            _engine = engine;
+        }
+        
+        protected ObjectInstance(Engine engine, in string objectClass) : base(Types.Object)
         {
-            Engine = engine;
+            _engine = engine;
+            _class = objectClass;
         }
 
-        public Engine Engine { get; }
+        public Engine Engine => _engine;
 
         protected bool TryGetIntrinsicValue(JsSymbol symbol, out JsValue value)
         {
@@ -75,7 +84,7 @@ namespace Jint.Native.Object
         /// A String value indicating a specification defined
         /// classification of objects.
         /// </summary>
-        public virtual string Class => "Object";
+        public ref readonly string Class => ref _class;
 
         public virtual IEnumerable<KeyValuePair<string, PropertyDescriptor>> GetOwnProperties()
         {
@@ -148,10 +157,10 @@ namespace Jint.Native.Object
             if (desc.IsDataDescriptor())
             {
                 var val = desc.Value;
-                return !ReferenceEquals(val, null) ? val : Undefined;
+                return val ?? Undefined;
             }
 
-            var getter = !ReferenceEquals(desc.Get, null) ? desc.Get : Undefined;
+            var getter = desc.Get ?? Undefined;
             if (getter.IsUndefined())
             {
                 return Undefined;
@@ -777,7 +786,7 @@ namespace Jint.Native.Object
                 case "String":
                     if (this is StringInstance stringInstance)
                     {
-                        return stringInstance.PrimitiveValue.AsString();
+                        return stringInstance.PrimitiveValue.AsStringWithoutTypeCheck();
                     }
 
                     break;
@@ -793,7 +802,7 @@ namespace Jint.Native.Object
                 case "Boolean":
                     if (this is BooleanInstance booleanInstance)
                     {
-                        return booleanInstance.PrimitiveValue.AsBoolean()
+                        return ((JsBoolean) booleanInstance.PrimitiveValue)._value
                              ? JsBoolean.BoxedTrue
                              : JsBoolean.BoxedFalse;
                     }
@@ -811,7 +820,7 @@ namespace Jint.Native.Object
                 case "Number":
                     if (this is NumberInstance numberInstance)
                     {
-                        return numberInstance.NumberData.AsNumber();
+                        return ((JsNumber) numberInstance.NumberData)._value;
                     }
 
                     break;

+ 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 (ReferenceEquals(thisObject, Undefined))
+            if (thisObject.IsUndefined())
             {
                 return "[object Undefined]";
             }
 
-            if (ReferenceEquals(thisObject, Null))
+            if (thisObject.IsNull())
             {
                 return "[object Null]";
             }

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

@@ -41,8 +41,8 @@ namespace Jint.Native.RegExp
             var pattern = arguments.At(0);
             var flags = arguments.At(1);
 
-            if (!ReferenceEquals(pattern, Undefined)
-                && ReferenceEquals(flags, Undefined)
+            if (!pattern.IsUndefined()
+                && flags.IsUndefined()
                 && TypeConverter.ToObject(Engine, pattern).Class == "Regex")
             {
                 return pattern;
@@ -66,17 +66,17 @@ namespace Jint.Native.RegExp
             var flags = arguments.At(1);
 
             var r = pattern.TryCast<RegExpInstance>();
-            if (ReferenceEquals(flags, Undefined) && !ReferenceEquals(r, null))
+            if (flags.IsUndefined() && !ReferenceEquals(r, null))
             {
                 return r;
             }
 
-            if (!ReferenceEquals(flags, Undefined) && !ReferenceEquals(r, null))
+            if (!flags.IsUndefined() && !ReferenceEquals(r, null))
             {
                 throw new JavaScriptException(Engine.TypeError);
             }
 
-            if (ReferenceEquals(pattern, Undefined))
+            if (pattern.IsUndefined())
             {
                 p = "";
             }
@@ -85,7 +85,7 @@ namespace Jint.Native.RegExp
                 p = TypeConverter.ToString(pattern);
             }
 
-            f = !ReferenceEquals(flags, Undefined) ? TypeConverter.ToString(flags) : "";
+            f = !flags.IsUndefined() ? TypeConverter.ToString(flags) : "";
 
             r = new RegExpInstance(Engine);
             r.Prototype = PrototypeObject;
@@ -164,7 +164,7 @@ namespace Jint.Native.RegExp
             r.SetOwnProperty("lastIndex", new PropertyDescriptor(0, PropertyFlag.OnlyWritable));
         }
 
-        private void AssignFlags(RegExpInstance r, string flags)
+        private static void AssignFlags(RegExpInstance r, string flags)
         {
             for(var i=0; i < flags.Length; i++)
             {

+ 1 - 10
Jint/Native/RegExp/RegExpInstance.cs

@@ -6,20 +6,11 @@ namespace Jint.Native.RegExp
     public class RegExpInstance : ObjectInstance
     {
         public RegExpInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "RegExp")
         {
         }
 
-        public override string Class
-        {
-            get
-            {
-                return "RegExp";
-            }
-        }
-
         public Regex Value { get; set; }
-        //public string Pattern { get; set; }
         public string Source { get; set; }
         public string Flags { get; set; }
         public bool Global { get; set; }

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

@@ -36,7 +36,7 @@ namespace Jint.Native.RegExp
             FastAddProperty("lastIndex", 0, true, false, false);
         }
 
-        private JsValue ToRegExpString(JsValue thisObj, JsValue[] arguments)
+        private static JsValue ToRegExpString(JsValue thisObj, JsValue[] arguments)
         {
             var regExp = thisObj.TryCast<RegExpInstance>();
 
@@ -56,7 +56,7 @@ namespace Jint.Native.RegExp
             }
 
             var match = Exec(r, arguments);
-            return !ReferenceEquals(match, Null);
+            return !match.IsNull();
         }
 
         internal JsValue Exec(JsValue thisObj, JsValue[] arguments)

+ 3 - 5
Jint/Native/String/StringInstance.cs

@@ -13,12 +13,10 @@ namespace Jint.Native.String
         private PropertyDescriptor _length;
 
         public StringInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "String")
         {
         }
 
-        public override string Class => "String";
-
         Types IPrimitiveInstance.Type => Types.String;
 
         JsValue IPrimitiveInstance.PrimitiveValue => PrimitiveValue;
@@ -66,13 +64,13 @@ namespace Jint.Native.String
                 return PropertyDescriptor.Undefined;
 
             var index = (int) dIndex;
-            var len = str.AsString().Length;
+            var len = str.AsStringWithoutTypeCheck().Length;
             if (len <= index || index < 0)
             {
                 return PropertyDescriptor.Undefined;
             }
 
-            var resultStr = TypeConverter.ToString(str.AsString()[index]);
+            var resultStr = TypeConverter.ToString(str.AsStringWithoutTypeCheck()[index]);
             return new PropertyDescriptor(resultStr, PropertyFlag.OnlyEnumerable);
         }
 

+ 26 - 20
Jint/Native/String/StringPrototype.cs

@@ -95,6 +95,11 @@ namespace Jint.Native.String
             if (!IsWhiteSpaceEx(s[s.Length - 1]))
                 return s;
 
+            return TrimEnd(s);
+        }
+
+        private static string TrimEnd(string s)
+        {
             var i = s.Length - 1;
             while (i >= 0)
             {
@@ -103,10 +108,8 @@ namespace Jint.Native.String
                 else
                     break;
             }
-            if (i >= 0)
-                return s.Substring(0, i + 1);
-            else
-                return string.Empty;
+
+            return i >= 0 ? s.Substring(0, i + 1) : string.Empty;
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -118,6 +121,11 @@ namespace Jint.Native.String
             if (!IsWhiteSpaceEx(s[0]))
                 return s;
 
+            return TrimStart(s);
+        }
+
+        private static string TrimStart(string s)
+        {
             var i = 0;
             while (i < s.Length)
             {
@@ -126,10 +134,8 @@ namespace Jint.Native.String
                 else
                     break;
             }
-            if (i >= s.Length)
-                return string.Empty;
-            else
-                return s.Substring(i);
+
+            return i >= s.Length ? string.Empty : s.Substring(i);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -204,7 +210,7 @@ namespace Jint.Native.String
             var len = s.Length;
             var intStart = ToIntegerSupportInfinity(start);
 
-            var intEnd = ReferenceEquals(arguments.At(1), Undefined) ? len : ToIntegerSupportInfinity(end);
+            var intEnd = arguments.At(1).IsUndefined() ? 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
@@ -225,11 +231,11 @@ namespace Jint.Native.String
             return s.Substring(from, length);
         }
 
-        private JsValue Substr(JsValue thisObj, JsValue[] arguments)
+        private static JsValue Substr(JsValue thisObj, JsValue[] arguments)
         {
             var s = TypeConverter.ToString(thisObj);
             var start = TypeConverter.ToInteger(arguments.At(0));
-            var length = ReferenceEquals(arguments.At(1), Undefined)
+            var length = arguments.At(1).IsUndefined()
                 ? double.PositiveInfinity
                 : TypeConverter.ToInteger(arguments.At(1));
 
@@ -258,7 +264,7 @@ namespace Jint.Native.String
 
             // Coerce into a number, true will become 1
             var l = arguments.At(1);
-            var limit = ReferenceEquals(l, Undefined) ? uint.MaxValue : TypeConverter.ToUint32(l);
+            var limit = l.IsUndefined() ? uint.MaxValue : TypeConverter.ToUint32(l);
             var len = s.Length;
 
             if (limit == 0)
@@ -266,11 +272,11 @@ namespace Jint.Native.String
                 return Engine.Array.Construct(Arguments.Empty);
             }
 
-            if (ReferenceEquals(separator, Null))
+            if (separator.IsNull())
             {
                 separator = Native.Null.Text;
             }
-            else if (ReferenceEquals(separator, Undefined))
+            else if (separator.IsUndefined())
             {
                 var jsValues = Engine.JsValueArrayPool.RentArray(1);
                 jsValues[0] = s;
@@ -407,7 +413,7 @@ namespace Jint.Native.String
 
             var len = s.Length;
             var intStart = (int)TypeConverter.ToInteger(start);
-            var intEnd = ReferenceEquals(arguments.At(1), Undefined) ? len : (int)TypeConverter.ToInteger(end);
+            var intEnd = arguments.At(1).IsUndefined() ? 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);
@@ -621,7 +627,7 @@ namespace Jint.Native.String
 
             rx = rx ?? (RegExpInstance) Engine.RegExp.Construct(new[] {regex});
 
-            var global = rx.Get("global").AsBoolean();
+            var global = ((JsBoolean) rx.Get("global"))._value;
             if (!global)
             {
                 return Engine.RegExp.PrototypeObject.Exec(rx, Arguments.From(s));
@@ -642,7 +648,7 @@ namespace Jint.Native.String
                     }
                     else
                     {
-                        var thisIndex = rx.Get("lastIndex").AsNumber();
+                        var thisIndex = ((JsNumber) rx.Get("lastIndex"))._value;
                         if (thisIndex == previousLastIndex)
                         {
                             rx.Put("lastIndex", thisIndex + 1, false);
@@ -681,7 +687,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 && !ReferenceEquals(arguments[1], Undefined))
+            if (arguments.Length > 1 && !arguments[1].IsUndefined())
             {
                 numPos = TypeConverter.ToNumber(arguments[1]);
             }
@@ -728,7 +734,7 @@ namespace Jint.Native.String
             var s = TypeConverter.ToString(thisObj);
             var searchStr = TypeConverter.ToString(arguments.At(0));
             double pos = 0;
-            if (arguments.Length > 1 && !ReferenceEquals(arguments[1], Undefined))
+            if (arguments.Length > 1 && !arguments[1].IsUndefined())
             {
                 pos = TypeConverter.ToInteger(arguments[1]);
             }
@@ -756,7 +762,7 @@ namespace Jint.Native.String
             {
                 if (arguments[i].Type == Types.String)
                 {
-                    capacity += arguments[i].AsString().Length;
+                    capacity += arguments[i].AsStringWithoutTypeCheck().Length;
                 }
             }
 

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

@@ -60,7 +60,7 @@ namespace Jint.Native.Symbol
         public override JsValue Call(JsValue thisObject, JsValue[] arguments)
         {
             var description = arguments.At(0);
-            var descString = ReferenceEquals(description, Undefined)
+            var descString = description.IsUndefined()
                 ? Undefined
                 : TypeConverter.ToString(description);
 
@@ -80,8 +80,7 @@ namespace Jint.Native.Symbol
 
             // 2. ReturnIfAbrupt(stringKey).
 
-            JsSymbol symbol;
-            if (!Engine.GlobalSymbolRegistry.TryGetValue(stringKey, out symbol))
+            if (!Engine.GlobalSymbolRegistry.TryGetValue(stringKey, out var symbol))
             {
                 symbol = new JsSymbol(stringKey);
                 Engine.GlobalSymbolRegistry.Add(stringKey, symbol);

+ 1 - 3
Jint/Native/Symbol/SymbolInstance.cs

@@ -6,12 +6,10 @@ namespace Jint.Native.Symbol
     public class SymbolInstance : ObjectInstance, IPrimitiveInstance
     {
         public SymbolInstance(Engine engine)
-            : base(engine)
+            : base(engine, objectClass: "Symbol")
         {
         }
 
-        public override string Class => "Symbol";
-
         Types IPrimitiveInstance.Type => Types.Symbol;
 
         JsValue IPrimitiveInstance.PrimitiveValue => SymbolData;

+ 27 - 19
Jint/Pooling/JsValueArrayPool.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Runtime.CompilerServices;
 using Jint.Native;
 
 namespace Jint.Pooling
@@ -35,36 +36,43 @@ namespace Jint.Pooling
             return new JsValue[3];
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue[] RentArray(int size)
         {
-            switch (size)
+            if (size == 0)
             {
-                case 0:
-                    return Array.Empty<JsValue>();
-                case 1:
-                    return _poolArray1.Allocate();
-                case 2:
-                    return _poolArray2.Allocate();
-                case 3:
-                    return _poolArray3.Allocate();
+                return Array.Empty<JsValue>();
+            }
+            if (size == 1)
+            {
+                return _poolArray1.Allocate();
+            }
+            if (size == 2)
+            {
+                return _poolArray2.Allocate();
+            }
+            if (size == 3)
+            {
+                return _poolArray3.Allocate();
             }
 
             return new JsValue[size];
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public void ReturnArray(JsValue[] array)
         {
-            switch (array.Length)
+            if (array.Length == 1)
+            {
+                _poolArray1.Free(array);
+            }
+            else if (array.Length == 2)
+            {
+                _poolArray2.Free(array);
+            }
+            else if (array.Length == 3)
             {
-                case 1:
-                    _poolArray1.Free(array);
-                    break;
-                case 2:
-                    _poolArray2.Free(array);
-                    break;
-                case 3:
-                    _poolArray3.Free(array);
-                    break;
+                _poolArray3.Free(array);
             }
         }
     }

+ 1 - 7
Jint/Runtime/Completion.cs

@@ -18,9 +18,6 @@ namespace Jint.Runtime
     /// </summary>
     public readonly struct Completion
     {
-        internal static readonly Completion Empty = new Completion(CompletionType.Normal, null, null);
-        internal static readonly Completion EmptyUndefined = new Completion(CompletionType.Normal, Undefined.Instance, null);
-
         public Completion(CompletionType type, JsValue value, string identifier, Location location = null)
         {
             Type = type;
@@ -30,10 +27,9 @@ namespace Jint.Runtime
         }
 
         public readonly CompletionType Type;
-
         public readonly JsValue Value;
-
         public readonly string Identifier;
+        public readonly Location Location;
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue GetValueOrDefault()
@@ -46,7 +42,5 @@ namespace Jint.Runtime
         {
             return Type != CompletionType.Normal;
         }
-
-        public readonly Location Location;
     }
 }

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

@@ -138,7 +138,8 @@ namespace Jint.Runtime.Debugger
 
             if (!string.IsNullOrEmpty(breakpoint.Condition))
             {
-                return _engine.Execute(breakpoint.Condition).GetCompletionValue().AsBoolean();
+                var completionValue = _engine.Execute(breakpoint.Condition).GetCompletionValue();
+                return ((JsBoolean) completionValue)._value;
             }
 
             return true;
@@ -148,7 +149,7 @@ namespace Jint.Runtime.Debugger
         {
             var info = new DebugInformation { CurrentStatement = statement, CallStack = _debugCallStack };
 
-            if (_engine.ExecutionContext?.LexicalEnvironment != null)
+            if (_engine.ExecutionContext.LexicalEnvironment != null)
             {
                 var lexicalEnvironment = _engine.ExecutionContext.LexicalEnvironment;
                 info.Locals = GetLocalVariables(lexicalEnvironment);

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

@@ -263,7 +263,7 @@ namespace Jint.Runtime.Descriptors
             if (hasGetProperty)
             {
                 var getter = obj.UnwrapJsValue(getProperty);
-                if (!ReferenceEquals(getter, JsValue.Undefined) && getter.TryCast<ICallable>() == null)
+                if (!getter.IsUndefined() && getter.TryCast<ICallable>() == null)
                 {
                     throw new JavaScriptException(engine.TypeError);
                 }
@@ -274,7 +274,7 @@ namespace Jint.Runtime.Descriptors
             if (hasSetProperty)
             {
                 var setter = obj.UnwrapJsValue(setProperty);
-                if (!ReferenceEquals(setter, JsValue.Undefined) && setter.TryCast<ICallable>() == null)
+                if (!setter.IsUndefined() && setter.TryCast<ICallable>() == null)
                 {
                     throw new JavaScriptException(engine.TypeError);
                 }
@@ -282,7 +282,7 @@ namespace Jint.Runtime.Descriptors
                 ((GetSetPropertyDescriptor) desc).SetSet(setter);
             }
 
-            if (!ReferenceEquals(desc.Get, null) || !ReferenceEquals(desc.Get, null))
+            if (!ReferenceEquals(desc.Get, null))
             {
                 if (!ReferenceEquals(desc.Value, null) || desc.WritableSet)
                 {
@@ -299,7 +299,7 @@ namespace Jint.Runtime.Descriptors
             {
                 return Native.Undefined.Instance;
             }
-
+            
             var obj = engine.Object.Construct(Arguments.Empty);
 
             if (desc.IsDataDescriptor())

+ 3 - 6
Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs

@@ -10,8 +10,6 @@ namespace Jint.Runtime.Environments
     /// </summary>
     public sealed class DeclarativeEnvironmentRecord : EnvironmentRecord
     {
-        private readonly Engine _engine;
-
         private const string BindingNameArguments = "arguments";
         private Binding _argumentsBinding;
 
@@ -19,14 +17,13 @@ namespace Jint.Runtime.Environments
 
         public DeclarativeEnvironmentRecord(Engine engine) : base(engine)
         {
-            _engine = engine;
         }
 
         public override bool HasBinding(string name)
         {
-            if (name.Length == 9 && name == BindingNameArguments)
+            if (_argumentsBinding != null && name.Length == 9 && name == BindingNameArguments)
             {
-                return _argumentsBinding != null;
+                return true;
             }
 
             return _bindings?.ContainsKey(name) == true;
@@ -80,7 +77,7 @@ namespace Jint.Runtime.Environments
         {
             var binding = name == BindingNameArguments ? _argumentsBinding : _bindings[name];
 
-            if (!binding.Mutable && ReferenceEquals(binding.Value, Undefined))
+            if (!binding.Mutable && binding.Value.IsUndefined())
             {
                 if (strict)
                 {

+ 15 - 4
Jint/Runtime/Environments/ExecutionContext.cs

@@ -2,11 +2,22 @@
 
 namespace Jint.Runtime.Environments
 {
-    public sealed class ExecutionContext
+    public readonly struct ExecutionContext
     {
-        public LexicalEnvironment LexicalEnvironment { get; set; }
-        public LexicalEnvironment VariableEnvironment { get; set; }
-        public JsValue ThisBinding { get; set; }
+        public ExecutionContext(LexicalEnvironment lexicalEnvironment, LexicalEnvironment variableEnvironment, JsValue thisBinding)
+        {
+            LexicalEnvironment = lexicalEnvironment;
+            VariableEnvironment = variableEnvironment;
+            ThisBinding = thisBinding;
+        }
 
+        public readonly LexicalEnvironment LexicalEnvironment;
+        public readonly LexicalEnvironment VariableEnvironment;
+        public readonly JsValue ThisBinding;
+
+        public ExecutionContext UpdateLexicalEnvironment(LexicalEnvironment newEnv)
+        {
+            return new ExecutionContext(newEnv, VariableEnvironment, ThisBinding);
+        }
     }
 }

+ 20 - 8
Jint/Runtime/Environments/LexicalEnvironment.cs

@@ -31,26 +31,38 @@ namespace Jint.Runtime.Environments
 
         public LexicalEnvironment Outer => _outer;
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static Reference GetIdentifierReference(LexicalEnvironment lex, string name, bool strict)
         {
-            while (true)
+            // optimize for common case where result is in one of the nearest scopes
+            if (lex._record.HasBinding(name))
             {
-                if (lex == null)
-                {
-                    return new Reference(Undefined.Instance, name, strict);
-                }
+                return lex._engine.ReferencePool.Rent(lex._record, name, strict);
+            }
 
+            if (lex._outer == null)
+            {
+                return new Reference(Undefined.Instance, name, strict);
+            }
+            
+            return GetIdentifierReferenceLooping(lex._outer, name, strict);
+        }
+
+        private static Reference GetIdentifierReferenceLooping(LexicalEnvironment lex, string name, bool strict)
+        {
+            while (true)
+            {
                 if (lex.Record.HasBinding(name))
                 {
-                    return lex._engine.ReferencePool.Rent(lex.Record, name, strict);
+                    return lex._engine.ReferencePool.Rent(lex._record, name, strict);
                 }
 
-                if (lex.Outer == null)
+                if (lex._outer == null)
                 {
                     return lex._engine.ReferencePool.Rent(Undefined.Instance, name, strict);
                 }
 
-                lex = lex.Outer;
+                lex = lex._outer;
             }
         }
 

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

@@ -12,13 +12,11 @@ namespace Jint.Runtime.Environments
     /// </summary>
     public sealed class ObjectEnvironmentRecord : EnvironmentRecord
     {
-        private readonly Engine _engine;
         private readonly ObjectInstance _bindingObject;
         private readonly bool _provideThis;
 
         public ObjectEnvironmentRecord(Engine engine, ObjectInstance bindingObject, bool provideThis) : base(engine)
         {
-            _engine = engine;
             _bindingObject = bindingObject;
             _provideThis = provideThis;
         }

+ 19 - 22
Jint/Runtime/ExpressionIntepreter.cs

@@ -85,8 +85,7 @@ namespace Jint.Runtime
                     var rprim = TypeConverter.ToPrimitive(rval);
                     if (lprim.IsString() || rprim.IsString())
                     {
-                        var jsString = lprim as JsString;
-                        if (ReferenceEquals(jsString, null))
+                        if (!(lprim is JsString jsString))
                         {
                             jsString = new JsString.ConcatenatedString(TypeConverter.ToString(lprim));
                         }
@@ -103,7 +102,7 @@ namespace Jint.Runtime
                     break;
 
                 case AssignmentOperator.TimesAssign:
-                    if (ReferenceEquals(lval, Undefined.Instance) || ReferenceEquals(rval, Undefined.Instance))
+                    if (lval.IsUndefined() || rval.IsUndefined())
                     {
                         lval = Undefined.Instance;
                     }
@@ -118,7 +117,7 @@ namespace Jint.Runtime
                     break;
 
                 case AssignmentOperator.ModuloAssign:
-                    if (ReferenceEquals(lval, Undefined.Instance) || ReferenceEquals(rval, Undefined.Instance))
+                    if (lval.IsUndefined() || rval.IsUndefined())
                     {
                         lval = Undefined.Instance;
                     }
@@ -165,7 +164,7 @@ namespace Jint.Runtime
 
         private JsValue Divide(JsValue lval, JsValue rval)
         {
-            if (ReferenceEquals(lval, Undefined.Instance) || ReferenceEquals(rval, Undefined.Instance))
+            if (lval.IsUndefined() || rval.IsUndefined())
             {
                 return Undefined.Instance;
             }
@@ -257,7 +256,7 @@ namespace Jint.Runtime
                     break;
 
                 case BinaryOperator.Times:
-                    if (ReferenceEquals(left, Undefined.Instance) || ReferenceEquals(right, Undefined.Instance))
+                    if (left.IsUndefined() || right.IsUndefined())
                     {
                         value = Undefined.Instance;
                     }
@@ -272,7 +271,7 @@ namespace Jint.Runtime
                     break;
 
                 case BinaryOperator.Modulo:
-                    if (ReferenceEquals(left, Undefined.Instance) || ReferenceEquals(right, Undefined.Instance))
+                    if (left.IsUndefined() || right.IsUndefined())
                     {
                         value = Undefined.Instance;
                     }
@@ -292,7 +291,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.Greater:
                     value = Compare(right, left, false);
-                    if (ReferenceEquals(value, Undefined.Instance))
+                    if (value.IsUndefined())
                     {
                         value = false;
                     }
@@ -300,7 +299,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.GreaterOrEqual:
                     value = Compare(left, right);
-                    if (ReferenceEquals(value, Undefined.Instance) || value.AsBoolean())
+                    if (value.IsUndefined() || ((JsBoolean) value)._value)
                     {
                         value = false;
                     }
@@ -312,7 +311,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.Less:
                     value = Compare(left, right);
-                    if (ReferenceEquals(value, Undefined.Instance))
+                    if (value.IsUndefined())
                     {
                         value = false;
                     }
@@ -320,7 +319,7 @@ namespace Jint.Runtime
 
                 case BinaryOperator.LessOrEqual:
                     value = Compare(right, left, false);
-                    if (ReferenceEquals(value, Undefined.Instance) || value.AsBoolean())
+                    if (value.IsUndefined() || ((JsBoolean) value)._value)
                     {
                         value = false;
                     }
@@ -417,12 +416,12 @@ namespace Jint.Runtime
 				return StrictlyEqual(x, y);
             }
 
-            if (ReferenceEquals(x, Null.Instance) && ReferenceEquals(y, Undefined.Instance))
+            if (x.IsNull() && y.IsUndefined())
             {
                 return true;
             }
 
-            if (ReferenceEquals(x, Undefined.Instance) && ReferenceEquals(y, Null.Instance))
+            if (x.IsUndefined() && y.IsNull())
             {
                 return true;
             }
@@ -477,8 +476,8 @@ namespace Jint.Runtime
 
             if (typea == Types.Number)
             {
-                var nx = x.AsNumber();
-                var ny = y.AsNumber();
+                var nx = ((JsNumber) x)._value;
+                var ny = ((JsNumber) y)._value;
 
                 if (double.IsNaN(nx) || double.IsNaN(ny))
                 {
@@ -495,12 +494,12 @@ namespace Jint.Runtime
 
             if (typea == Types.String)
             {
-                return x.AsString() == y.AsString();
+                return x.AsStringWithoutTypeCheck() == y.AsStringWithoutTypeCheck();
             }
 
             if (typea == Types.Boolean)
             {
-                return x.AsBoolean() == y.AsBoolean();
+                return ((JsBoolean) x)._value == ((JsBoolean) y)._value;
             }
 
 			if (typea == Types.Object)
@@ -636,7 +635,6 @@ namespace Jint.Runtime
             return LexicalEnvironment.GetIdentifierReference(env, identifier.Name, strict);
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public JsValue EvaluateLiteral(Literal literal)
         {
             switch (literal.TokenType)
@@ -880,7 +878,7 @@ namespace Jint.Runtime
                 }
             }
 
-            if (ReferenceEquals(func, Undefined.Instance))
+            if (func.IsUndefined())
             {
                 throw new JavaScriptException(_engine.TypeError, r == null ? "" : string.Format("Object has no method '{0}'", r.GetReferencedName()));
             }
@@ -1126,11 +1124,11 @@ namespace Jint.Runtime
 
                     var v = _engine.GetValue(value, true);
 
-                    if (ReferenceEquals(v, Undefined.Instance))
+                    if (v.IsUndefined())
                     {
                         return "undefined";
                     }
-                    if (ReferenceEquals(v, Null.Instance))
+                    if (v.IsNull())
                     {
                         return "object";
                     }
@@ -1151,7 +1149,6 @@ namespace Jint.Runtime
             }
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private void BuildArguments(
             List<ArgumentListElement> expressionArguments, 
             JsValue[] targetArray,

+ 1 - 1
Jint/Runtime/Interop/NamespaceReference.cs

@@ -50,7 +50,7 @@ namespace Jint.Runtime.Interop
             for (int i = 0; i < arguments.Length; i++)
             {
                 var genericTypeReference = arguments.At(i);
-                if (ReferenceEquals(genericTypeReference, Undefined)
+                if (genericTypeReference.IsUndefined()
                     || !genericTypeReference.IsObject() 
                     || genericTypeReference.AsObject().Class != "TypeReference")
                 {

+ 1 - 1
Jint/Runtime/Interop/ObjectWrapper.cs

@@ -140,7 +140,7 @@ namespace Jint.Runtime.Interop
             return PropertyDescriptor.Undefined;
         }
 
-        private bool EqualsIgnoreCasing(string s1, string s2)
+        private static bool EqualsIgnoreCasing(string s1, string s2)
         {
             bool equals = false;
             if (s1.Length == s2.Length)

+ 1 - 3
Jint/Runtime/Interop/TypeReference.cs

@@ -13,7 +13,7 @@ namespace Jint.Runtime.Interop
     public class TypeReference : FunctionInstance, IConstructor, IObjectWrapper
     {
         private TypeReference(Engine engine)
-            : base(engine, null, null, false)
+            : base(engine, null, null, false, "TypeReference")
         {
         }
 
@@ -202,7 +202,5 @@ namespace Jint.Runtime.Interop
         }
 
         public object Target => ReferenceType;
-
-        public override string Class => "TypeReference";
     }
 }

+ 4 - 4
Jint/Runtime/JavaScriptException.cs

@@ -77,7 +77,7 @@ namespace Jint.Runtime
                 return message;
             }
             if (error.IsString())
-                return error.AsString();
+                return error.AsStringWithoutTypeCheck();
 
             return error.ToString();
         }
@@ -100,7 +100,7 @@ namespace Jint.Runtime
                 if (Error.IsObject() == false)
                     return null;
                 var callstack = Error.AsObject().Get("callstack");
-                if (ReferenceEquals(callstack, JsValue.Undefined))
+                if (callstack.IsUndefined())
                     return null;
                 return callstack.AsString();
             }
@@ -117,8 +117,8 @@ namespace Jint.Runtime
 
         public Location Location { get; set; }
 
-        public int LineNumber { get { return null == Location ? 0 : Location.Start.Line; } }
+        public int LineNumber => Location?.Start.Line ?? 0;
 
-        public int Column { get { return null == Location ? 0 : Location.Start.Column; } }
+        public int Column => Location?.Start.Column ?? 0;
     }
 }

+ 64 - 0
Jint/Runtime/RefStack.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Runtime.CompilerServices;
+using Jint.Runtime.Environments;
+
+namespace Jint.Runtime
+{
+    internal sealed class ExecutionContextStack
+    {
+        private ExecutionContext[] _array;
+        private int _size;
+
+        private const int DefaultCapacity = 4;
+
+        public ExecutionContextStack()
+        {
+            _array = new ExecutionContext[4];
+            _size = 0;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public ref readonly ExecutionContext Peek()
+        {
+            if (_size == 0)
+            {
+                ThrowEmptyStackException();
+            }
+            return ref _array[_size - 1];
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Pop()
+        {
+            if (_size == 0)
+            {
+                ThrowEmptyStackException();
+            }
+            _size--;
+        }
+
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public void Push(in ExecutionContext item)
+        {
+            if (_size == _array.Length)
+            {
+                var newSize = 2 * _array.Length;
+                var newArray = new ExecutionContext[newSize];
+                Array.Copy(_array, 0, newArray, 0, _size);
+                _array = newArray;
+            }
+
+            _array[_size++] = item;
+        }
+
+        private static void ThrowEmptyStackException()
+        {
+            throw new InvalidOperationException("stack is empty");
+        }
+
+        public void ReplaceTopLexicalEnvironment(LexicalEnvironment newEnv)
+        {
+            _array[_size - 1] = _array[_size - 1].UpdateLexicalEnvironment(newEnv);
+        }
+    }
+}

+ 14 - 13
Jint/Runtime/StatementInterpreter.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Runtime.Descriptors;
@@ -24,7 +25,7 @@ namespace Jint.Runtime
 
         public Completion ExecuteEmptyStatement(EmptyStatement emptyStatement)
         {
-            return Completion.Empty;
+            return new Completion(CompletionType.Normal, null, null);
         }
 
         public Completion ExecuteExpressionStatement(ExpressionStatement expressionStatement)
@@ -46,7 +47,7 @@ namespace Jint.Runtime
             }
             else
             {
-                return Completion.Empty;
+                return new Completion(CompletionType.Normal, null, null);
             }
 
             return result;
@@ -214,9 +215,9 @@ namespace Jint.Runtime
 
             var varRef = _engine.EvaluateExpression(identifier) as Reference;
             var experValue = _engine.GetValue(_engine.EvaluateExpression(forInStatement.Right), true);
-            if (ReferenceEquals(experValue, Undefined.Instance) || ReferenceEquals(experValue,  Null.Instance))
+            if (experValue.IsUndefined() || experValue.IsNull())
             {
-                return Completion.Empty;
+                return new Completion(CompletionType.Normal, null, null);
             }
 
             var obj = TypeConverter.ToObject(_engine, experValue);
@@ -232,7 +233,7 @@ namespace Jint.Runtime
 
                 for (var i = 0; i < keys.GetLength(); i++)
                 {
-                    var p = keys.GetOwnProperty(TypeConverter.ToString(i)).Value.AsString();
+                    var p = keys.GetOwnProperty(TypeConverter.ToString(i)).Value.AsStringWithoutTypeCheck();
 
                     if (processedKeys.Contains(p))
                     {
@@ -247,7 +248,6 @@ namespace Jint.Runtime
                         continue;
                     }
 
-
                     var value = cursor.GetOwnProperty(p);
                     if (!value.Enumerable)
                     {
@@ -333,7 +333,7 @@ namespace Jint.Runtime
             var obj = TypeConverter.ToObject(_engine, jsValue);
             var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
             var newEnv = LexicalEnvironment.NewObjectEnvironment(_engine, obj, oldEnv, true);
-            _engine.ExecutionContext.LexicalEnvironment = newEnv;
+            _engine.UpdateLexicalEnvironment(newEnv);
 
             Completion c;
             try
@@ -346,7 +346,7 @@ namespace Jint.Runtime
             }
             finally
             {
-                _engine.ExecutionContext.LexicalEnvironment = oldEnv;
+                _engine.UpdateLexicalEnvironment(oldEnv);
             }
 
             return c;
@@ -420,7 +420,7 @@ namespace Jint.Runtime
 
         public Completion ExecuteStatementList(List<StatementListItem> statementList)
         {
-            var c = Completion.Empty;
+            var c = new Completion(CompletionType.Normal, null, null);
             Completion sl = c;
             Statement s = null;
 
@@ -483,9 +483,10 @@ namespace Jint.Runtime
                     var oldEnv = _engine.ExecutionContext.LexicalEnvironment;
                     var catchEnv = LexicalEnvironment.NewDeclarativeEnvironment(_engine, oldEnv);
                     catchEnv.Record.CreateMutableBinding(((Identifier) catchClause.Param).Name, c);
-                    _engine.ExecutionContext.LexicalEnvironment = catchEnv;
+
+                    _engine.UpdateLexicalEnvironment(catchEnv);
                     b = ExecuteStatement(catchClause.Body);
-                    _engine.ExecutionContext.LexicalEnvironment = oldEnv;
+                    _engine.UpdateLexicalEnvironment(oldEnv);
                 }
             }
 
@@ -534,7 +535,7 @@ namespace Jint.Runtime
                 }
             }
 
-            return Completion.EmptyUndefined;
+            return new Completion(CompletionType.Normal, Undefined.Instance, null);
         }
 
         public Completion ExecuteBlockStatement(BlockStatement blockStatement)
@@ -554,7 +555,7 @@ namespace Jint.Runtime
                 System.Diagnostics.Debugger.Break();
             }
 
-            return Completion.Empty;
+            return new Completion(CompletionType.Normal, null, null);
         }
     }
 }

+ 53 - 68
Jint/Runtime/TypeConverter.cs

@@ -52,17 +52,10 @@ namespace Jint.Runtime
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/5.1/#sec-9.1
         /// </summary>
-        /// <param name="input"></param>
-        /// <param name="preferredType"></param>
-        /// <returns></returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static JsValue ToPrimitive(JsValue input, Types preferredType = Types.None)
         {
-            if (ReferenceEquals(input, Null.Instance) || ReferenceEquals(input, Undefined.Instance))
-            {
-                return input;
-            }
-
-            if (input.IsPrimitive())
+            if (input._type == Types.Null || input._type == Types.Undefined || input.IsPrimitive())
             {
                 return input;
             }
@@ -76,24 +69,17 @@ namespace Jint.Runtime
         /// </summary>
         public static bool ToBoolean(JsValue o)
         {
-            var type = o.Type;
-
-            if (type == Types.Object)
-            {
-                return true;
-            }
-
-            if (type == Types.Boolean)
+            if (o.IsBoolean())
             {
                 return ((JsBoolean) o)._value;
             }
 
-            if (ReferenceEquals(o, Undefined.Instance) || ReferenceEquals(o, Null.Instance))
+            if (o.IsUndefined() || o.IsNull())
             {
                 return false;
             }
 
-            if (type == Types.Number)
+            if (o.IsNumber())
             {
                 var n = ((JsNumber) o)._value;
                 if (n.Equals(0) || double.IsNaN(n))
@@ -104,7 +90,7 @@ namespace Jint.Runtime
                 return true;
             }
 
-            if (type == Types.String)
+            if (o.IsString())
             {
                 return !((JsString) o).IsNullOrEmpty();
             }
@@ -120,38 +106,37 @@ namespace Jint.Runtime
         public static double ToNumber(JsValue o)
         {
             // check number first as this is what is usually expected
-            var type = o.Type;
-            if (type == Types.Number)
+            if (o.IsNumber())
             {
                 return ((JsNumber) o)._value;
             }
 
-            if (type == Types.Object)
+            if (o.IsUndefined())
             {
-                if (o is IPrimitiveInstance p)
-                {
-                    o = p.PrimitiveValue;
-                }
+                return double.NaN;
             }
 
-            if (ReferenceEquals(o, Undefined.Instance))
+            if (o.IsNull())
             {
-                return double.NaN;
+                return 0;
             }
 
-            if (ReferenceEquals(o, Null.Instance))
+            if (o._type == Types.Object)
             {
-                return 0;
+                if (o is IPrimitiveInstance p)
+                {
+                    o = p.PrimitiveValue;
+                }
             }
 
-            if (type == Types.Boolean)
+            if (o.IsBoolean())
             {
                 return ((JsBoolean) o)._value ? 1 : 0;
             }
 
-            if (type == Types.String)
+            if (o.IsString())
             {
-                return ToNumber(o.AsString());
+                return ToNumber(o.AsStringWithoutTypeCheck());
             }
 
             return ToNumber(ToPrimitive(o, Types.Number));
@@ -344,13 +329,22 @@ namespace Jint.Runtime
         {
             if (o.IsString())
             {
-                return o.AsString();
+                return o.AsStringWithoutTypeCheck();
+            }
+
+            if (o.IsUndefined())
+            {
+                return Undefined.Text;
+            }
+
+            if (o.IsNull())
+            {
+                return Null.Text;
             }
 
             if (o.IsObject())
             {
-                var p = o.AsObject() as IPrimitiveInstance;
-                if (p != null)
+                if (o is IPrimitiveInstance p)
                 {
                     o = p.PrimitiveValue;
                 }
@@ -366,24 +360,14 @@ namespace Jint.Runtime
                 }
             }
 
-            if (ReferenceEquals(o, Undefined.Instance))
-            {
-                return Undefined.Text;
-            }
-
-            if (ReferenceEquals(o, Null.Instance))
-            {
-                return Null.Text;
-            }
-
             if (o.IsBoolean())
             {
-                return o.AsBoolean() ? "true" : "false";
+                return ((JsBoolean) o)._value ? "true" : "false";
             }
 
             if (o.IsNumber())
             {
-                return ToString(o.AsNumber());
+                return ToString(((JsNumber) o)._value);
             }
 
             if (o.IsSymbol())
@@ -394,43 +378,45 @@ namespace Jint.Runtime
             return ToString(ToPrimitive(o, Types.String));
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static ObjectInstance ToObject(Engine engine, JsValue value)
         {
             if (value.IsObject())
             {
-                return value.AsObject();
-            }
-
-            if (ReferenceEquals(value, Undefined.Instance))
-            {
-                throw new JavaScriptException(engine.TypeError);
-            }
-
-            if (ReferenceEquals(value, Null.Instance))
-            {
-                throw new JavaScriptException(engine.TypeError);
+                return (ObjectInstance) value;
             }
 
             if (value.IsBoolean())
             {
-                return engine.Boolean.Construct(value.AsBoolean());
+                return engine.Boolean.Construct(((JsBoolean) value)._value);
             }
 
             if (value.IsNumber())
             {
-                return engine.Number.Construct(value.AsNumber());
+                return engine.Number.Construct(((JsNumber) value)._value);
             }
 
             if (value.IsString())
             {
-                return engine.String.Construct(value.AsString());
+                return engine.String.Construct(value.AsStringWithoutTypeCheck());
             }
 
             if (value.IsSymbol())
             {
-                return engine.Symbol.Construct(value.AsSymbol());
+                return engine.Symbol.Construct(((JsSymbol) value)._value);
             }
 
+            if (value.IsUndefined() || value.IsNull())
+            {
+                ThrowTypeError(engine);
+            }
+            
+            ThrowTypeError(engine);
+            return null;
+        }
+
+        private static void ThrowTypeError(Engine engine)
+        {
             throw new JavaScriptException(engine.TypeError);
         }
 
@@ -438,8 +424,7 @@ namespace Jint.Runtime
         {
             if (value.IsObject())
             {
-                var primitive = value.TryCast<IPrimitiveInstance>();
-                if (primitive != null)
+                if (value is IPrimitiveInstance primitive)
                 {
                     return primitive.Type;
                 }
@@ -456,7 +441,7 @@ namespace Jint.Runtime
             MemberExpression expression,
             object baseReference)
         {
-            if (!ReferenceEquals(o, Undefined.Instance) && !ReferenceEquals(o, Null.Instance))
+            if (!o.IsUndefined() && !o.IsNull())
             {
                 return;
             }
@@ -486,7 +471,7 @@ namespace Jint.Runtime
 
         public static void CheckObjectCoercible(Engine engine, JsValue o)
         {
-            if (ReferenceEquals(o, Undefined.Instance) || ReferenceEquals(o, Null.Instance))
+            if (o.IsUndefined() || o.IsNull())
             {
                 throw new JavaScriptException(engine.TypeError);
             }