Sfoglia il codice sorgente

Optimize global identifier access a bit and reduce allocations (#741)

Marko Lahma 5 anni fa
parent
commit
56ca37b78c
27 ha cambiato i file con 168 aggiunte e 126 eliminazioni
  1. 1 1
      Jint/Collections/HybridDictionary.cs
  2. 0 6
      Jint/JsValueExtensions.cs
  3. 2 2
      Jint/Native/Global/GlobalObject.cs
  4. 1 1
      Jint/Native/JsValue.cs
  5. 4 4
      Jint/Native/Json/JsonSerializer.cs
  6. 15 14
      Jint/Native/Object/ObjectInstance.cs
  7. 1 1
      Jint/Native/String/StringInstance.cs
  8. 1 1
      Jint/Native/String/StringPrototype.cs
  9. 2 2
      Jint/Runtime/Environments/DeclarativeEnvironmentRecord.cs
  10. 3 3
      Jint/Runtime/Environments/EnvironmentRecord.cs
  11. 1 1
      Jint/Runtime/Environments/FunctionEnvironmentRecord.cs
  12. 79 30
      Jint/Runtime/Environments/GlobalEnvironmentRecord.cs
  13. 8 1
      Jint/Runtime/Environments/LexicalEnvironment.cs
  14. 14 2
      Jint/Runtime/Environments/ObjectEnvironmentRecord.cs
  15. 1 1
      Jint/Runtime/Interpreter/Expressions/BindingPatternAssignmentExpression.cs
  16. 2 2
      Jint/Runtime/Interpreter/Expressions/JintArrayExpression.cs
  17. 1 1
      Jint/Runtime/Interpreter/Expressions/JintBinaryExpression.cs
  18. 2 2
      Jint/Runtime/Interpreter/Expressions/JintCallExpression.cs
  19. 0 33
      Jint/Runtime/Interpreter/Expressions/JintExpression.cs
  20. 7 1
      Jint/Runtime/Interpreter/Expressions/JintIdentifierExpression.cs
  21. 5 2
      Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs
  22. 3 2
      Jint/Runtime/Interpreter/Expressions/JintNewExpression.cs
  23. 1 1
      Jint/Runtime/Interpreter/Expressions/JintObjectExpression.cs
  24. 5 1
      Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs
  25. 5 3
      Jint/Runtime/Interpreter/Statements/JintForStatement.cs
  26. 1 1
      Jint/Runtime/JavaScriptException.cs
  27. 3 7
      Jint/Runtime/TypeConverter.cs

+ 1 - 1
Jint/Collections/HybridDictionary.cs

@@ -13,7 +13,7 @@ namespace Jint.Collections
 
         private readonly bool _checkExistingKeys;
         private ListDictionary<TValue> _list;
-        private StringDictionarySlim<TValue> _dictionary;
+        internal StringDictionarySlim<TValue> _dictionary;
 
         public HybridDictionary() : this(0, checkExistingKeys: true)
         {

+ 0 - 6
Jint/JsValueExtensions.cs

@@ -42,12 +42,6 @@ namespace Jint
                 ThrowWrongTypeException(value, "string");
             }
 
-            return AsStringWithoutTypeCheck(value);
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        internal static string AsStringWithoutTypeCheck(this JsValue value)
-        {
             return value.ToString();
         }
 

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

@@ -754,9 +754,9 @@ namespace Jint.Native.Global
             // here we are called only from global environment record context
             // we can take some shortcuts to be faster
 
-            if (!Properties.TryGetValue(property, out var existingDescriptor))
+            if (!_properties.TryGetValue(property, out var existingDescriptor))
             {
-                Properties[property] = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
+                _properties[property] = new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable);
                 return true;
             }
 

+ 1 - 1
Jint/Native/JsValue.cs

@@ -559,7 +559,7 @@ namespace Jint.Native
                         Value = ((JsBoolean) value)._value + " (bool)";
                         break;
                     case Types.String:
-                        Value = value.AsStringWithoutTypeCheck() + " (string)";
+                        Value = value.ToString() + " (string)";
                         break;
                     case Types.Number:
                         Value = ((JsNumber) value)._value + " (number)";

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

@@ -53,7 +53,7 @@ namespace Jint.Native.Json
                         string item = null;
                         if (v.IsString())
                         {
-                            item = v.AsStringWithoutTypeCheck();
+                            item = v.ToString();
                         }
                         else if (v.IsNumber())
                         {
@@ -105,7 +105,7 @@ namespace Jint.Native.Json
             }
             else if (space.IsString())
             {
-                var stringSpace = space.AsStringWithoutTypeCheck();
+                var stringSpace = space.ToString();
                 _gap = stringSpace.Length <= 10 ? stringSpace : stringSpace.Substring(0, 10);
             }
             else
@@ -177,7 +177,7 @@ namespace Jint.Native.Json
 
             if (value.IsString())
             {
-                return Quote(value.AsStringWithoutTypeCheck());
+                return Quote(value.ToString());
             }
 
             if (value.IsNumber())
@@ -264,7 +264,7 @@ namespace Jint.Native.Json
                 var strP = Str(TypeConverter.ToString(i), value);
                 if (strP.IsUndefined())
                     strP = "null";
-                partial.Add(strP.AsStringWithoutTypeCheck());
+                partial.Add(strP.ToString());
             }
             if (partial.Count == 0)
             {

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

@@ -25,7 +25,7 @@ namespace Jint.Native.Object
         private bool _initialized;
         private readonly ObjectClass _class;
 
-        private PropertyDictionary _properties;
+        internal PropertyDictionary _properties;
         internal SymbolDictionary _symbols;
 
         internal ObjectInstance _prototype;
@@ -317,11 +317,6 @@ namespace Jint.Native.Object
 
         internal static JsValue UnwrapJsValue(PropertyDescriptor desc, JsValue thisObject)
         {
-            if (desc == PropertyDescriptor.Undefined)
-            {
-                return Undefined;
-            }
-
             var value = (desc._flags & PropertyFlag.CustomJsValue) != 0
                 ? desc.CustomValue
                 : desc._value;
@@ -333,6 +328,15 @@ namespace Jint.Native.Object
                 return value ?? Undefined;
             }
 
+            return UnwrapFromGetter(desc, thisObject);
+        }
+
+        /// <summary>
+        /// A rarer case.
+        /// </summary>
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private static JsValue UnwrapFromGetter(PropertyDescriptor desc, JsValue thisObject)
+        {
             var getter = desc.Get ?? Undefined;
             if (getter.IsUndefined())
             {
@@ -457,10 +461,7 @@ namespace Jint.Native.Object
                 {
                     return parent.Set(property, value, receiver);
                 }
-                else
-                {
-                    ownDesc = new PropertyDescriptor(Undefined, PropertyFlag.ConfigurableEnumerableWritable);
-                }
+                ownDesc = new PropertyDescriptor(Undefined, PropertyFlag.ConfigurableEnumerableWritable);
             }
 
             if (ownDesc.IsDataDescriptor())
@@ -506,7 +507,7 @@ namespace Jint.Native.Object
 
             return true;
         }
-
+        
         /// <summary>
         /// Returns a Boolean value indicating whether a
         /// [[Put]] operation with PropertyName can be
@@ -681,6 +682,7 @@ namespace Jint.Native.Object
                             };
                         }
 
+                        propertyDescriptor._flags |= desc._flags & PropertyFlag.MutableBinding; 
                         o.SetOwnProperty(property, propertyDescriptor);
                     }
                     else
@@ -697,7 +699,7 @@ namespace Jint.Native.Object
             var currentSet = current.Set;
             var currentValue = current.Value;
 
-            if ((current._flags & PropertyFlag.ConfigurableSet | PropertyFlag.EnumerableSet | PropertyFlag.WritableSet) == 0 &&
+            if ((current._flags & (PropertyFlag.ConfigurableSet | PropertyFlag.EnumerableSet | PropertyFlag.WritableSet)) == 0 &&
                 ReferenceEquals(currentGet, null) &&
                 ReferenceEquals(currentSet, null) &&
                 ReferenceEquals(currentValue, null))
@@ -797,7 +799,6 @@ namespace Jint.Native.Object
 
             if (o is object)
             {
-                
                 if (!ReferenceEquals(descValue, null))
                 {
                     current.Value = descValue;
@@ -817,7 +818,7 @@ namespace Jint.Native.Object
                 {
                     current.Configurable = desc.Configurable;
                 }
-                
+
                 PropertyDescriptor mutable = null;
                 if (!ReferenceEquals(descGet, null))
                 {

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

@@ -55,7 +55,7 @@ namespace Jint.Native.String
                 return PropertyDescriptor.Undefined;
             }
 
-            var str = PrimitiveValue.AsStringWithoutTypeCheck();
+            var str = PrimitiveValue.ToString();
             var number = TypeConverter.ToNumber(property);
             if (!IsInt32(number, out var index) || index < 0 || index >= str.Length)
             {

+ 1 - 1
Jint/Native/String/StringPrototype.cs

@@ -652,7 +652,7 @@ namespace Jint.Native.String
             {
                 if (arguments[i].Type == Types.String)
                 {
-                    capacity += arguments[i].AsStringWithoutTypeCheck().Length;
+                    capacity += arguments[i].ToString().Length;
                 }
             }
 

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

@@ -23,7 +23,7 @@ namespace Jint.Runtime.Environments
         }
 
         internal sealed override bool TryGetBinding(
-            BindingName name,
+            in BindingName name,
             bool strict,
             out Binding binding,
             out JsValue value)
@@ -65,7 +65,7 @@ namespace Jint.Runtime.Environments
             _dictionary[name] = binding.ChangeValue(value);
         }
 
-        internal override void SetMutableBinding(BindingName name, JsValue value, bool strict)
+        internal override void SetMutableBinding(in BindingName name, JsValue value, bool strict)
         {
             SetMutableBinding(name.Key.Name, value, strict);
         }

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

@@ -23,7 +23,7 @@ namespace Jint.Runtime.Environments
         public abstract bool HasBinding(string name);
 
         internal abstract bool TryGetBinding(
-            BindingName name,
+            in BindingName name,
             bool strict,
             out Binding binding,
             out JsValue value);
@@ -57,7 +57,7 @@ namespace Jint.Runtime.Environments
         /// <param name="strict">The identify strict mode references.</param>
         public abstract void SetMutableBinding(string name, JsValue value, bool strict);
 
-        internal abstract void SetMutableBinding(BindingName name, JsValue value, bool strict);
+        internal abstract void SetMutableBinding(in BindingName name, JsValue value, bool strict);
 
         /// <summary>
         /// Returns the value of an already existing binding from an environment record.
@@ -107,7 +107,7 @@ namespace Jint.Runtime.Environments
         /// <summary>
         /// Helper to cache JsString/Key when environments use different lookups.
         /// </summary>
-        internal class BindingName
+        internal readonly struct BindingName
         {
             public readonly Key Key;
             public readonly JsString StringValue;

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

@@ -218,7 +218,7 @@ namespace Jint.Runtime.Environments
                             propertyName = ExceptionHelper.ThrowArgumentOutOfRangeException<JsString>("property", "unknown object pattern property type");
                         }
 
-                        processedProperties?.Add(propertyName.AsStringWithoutTypeCheck());
+                        processedProperties?.Add(propertyName.ToString());
                         jsValues[0] = argumentObject.Get(propertyName);
                         SetFunctionParameter(p.Value, jsValues, 0, initiallyEmpty);
                     }

+ 79 - 30
Jint/Runtime/Environments/GlobalEnvironmentRecord.cs

@@ -1,5 +1,6 @@
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using Jint.Native;
 using Jint.Native.Global;
 using Jint.Native.Object;
@@ -12,17 +13,19 @@ namespace Jint.Runtime.Environments
     /// </summary>
     internal sealed class GlobalEnvironmentRecord : EnvironmentRecord
     {
+        private readonly GlobalObject _global;
         private readonly DeclarativeEnvironmentRecord _declarativeRecord;
         private readonly ObjectEnvironmentRecord _objectRecord;
         private readonly HashSet<string> _varNames = new HashSet<string>();
 
         public GlobalEnvironmentRecord(Engine engine, GlobalObject global) : base(engine)
         {
+            _global = global;
             _objectRecord = new ObjectEnvironmentRecord(engine, global, provideThis: false, withEnvironment: false);
             _declarativeRecord = new DeclarativeEnvironmentRecord(engine);
         }
 
-        public ObjectInstance GlobalThisValue => _objectRecord._bindingObject;
+        public ObjectInstance GlobalThisValue => _global;
 
         public override bool HasBinding(string name)
         {
@@ -30,13 +33,53 @@ namespace Jint.Runtime.Environments
         }
 
         internal override bool TryGetBinding(
-            BindingName name,
+            in BindingName name,
             bool strict,
             out Binding binding,
             out JsValue value)
         {
-            return (_declarativeRecord._hasBindings && _declarativeRecord.TryGetBinding(name, strict, out binding, out value))
-                   || _objectRecord.TryGetBinding(name, strict, out binding, out value);
+            if (_declarativeRecord._hasBindings &&
+                _declarativeRecord.TryGetBinding(name, strict, out binding, out value))
+            {
+                return true;
+            }
+
+            // we unwrap by name
+            binding = default;
+            value = default;
+
+            // normal case is to find
+            if (_global._properties._dictionary.TryGetValue(name.Key, out var property)
+                && property != PropertyDescriptor.Undefined)
+            {
+                value = ObjectInstance.UnwrapJsValue(property, _global);
+                return true;
+            }
+
+            return TryGetBindingForGlobalParent(name, out value, property);
+        }
+        
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        private bool TryGetBindingForGlobalParent(
+            in BindingName name,
+            out JsValue value,
+            PropertyDescriptor property)
+        {
+            value = default;
+
+            var parent = _global._prototype;
+            if (parent != null)
+            {
+                property = parent.GetOwnProperty(name.StringValue);
+            }
+
+            if (property == PropertyDescriptor.Undefined)
+            {
+                return false;
+            }
+
+            value = ObjectInstance.UnwrapJsValue(property, _global);
+            return true;
         }
 
         /// <summary>
@@ -70,7 +113,10 @@ namespace Jint.Runtime.Environments
             }
             else
             {
-                _objectRecord.InitializeBinding(name, value);
+                if (!_global.Set(name, value))
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
             }
         }
 
@@ -82,11 +128,16 @@ namespace Jint.Runtime.Environments
             }
             else
             {
-                _objectRecord.SetMutableBinding(name, value, strict);
+                // fast inlined path as we know we target global, otherwise would be
+                // _objectRecord.SetMutableBinding(name, value, strict); 
+                if (!_global.Set(name, value) && strict)
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
             }
         }
 
-        internal override void SetMutableBinding(BindingName name, JsValue value, bool strict)
+        internal override void SetMutableBinding(in BindingName name, JsValue value, bool strict)
         {
             if (_declarativeRecord._hasBindings && _declarativeRecord.HasBinding(name.Key.Name))
             {
@@ -94,7 +145,12 @@ namespace Jint.Runtime.Environments
             }
             else
             {
-                _objectRecord.SetMutableBinding(name, value, strict);
+                // fast inlined path as we know we target global, otherwise would be
+                // _objectRecord.SetMutableBinding(name, value, strict); 
+                if (!_global.Set(name.Key, value) && strict)
+                {
+                    ExceptionHelper.ThrowTypeError(_engine);
+                }
             }
         }
 
@@ -112,7 +168,7 @@ namespace Jint.Runtime.Environments
                 return _declarativeRecord.DeleteBinding(name);
             }
 
-            if (_objectRecord._bindingObject.HasOwnProperty(name))
+            if (_global.HasOwnProperty(name))
             {
                 var status = _objectRecord.DeleteBinding(name);
                 if (status)
@@ -143,7 +199,7 @@ namespace Jint.Runtime.Environments
 
         public override JsValue GetThisBinding()
         {
-            return _objectRecord._bindingObject;
+            return _global;
         }
 
         public bool HasVarDeclaration(string name)
@@ -158,8 +214,7 @@ namespace Jint.Runtime.Environments
 
         public bool HasRestrictedGlobalProperty(string name)
         {
-            var globalObject = _objectRecord._bindingObject;
-            var existingProp = globalObject.GetOwnProperty(name);
+            var existingProp = _global.GetOwnProperty(name);
             if (existingProp == PropertyDescriptor.Undefined)
             {
                 return false;
@@ -170,22 +225,20 @@ namespace Jint.Runtime.Environments
 
         public bool CanDeclareGlobalVar(string name)
         {
-            var globalObject = _objectRecord._bindingObject;
-            if (globalObject.HasOwnProperty(name))
+            if (_global._properties.ContainsKey(name))
             {
                 return true;
             }
 
-            return globalObject.Extensible;
+            return _global.Extensible;
         }
 
         public bool CanDeclareGlobalFunction(string name)
         {
-            var globalObject = _objectRecord._bindingObject;
-            var existingProp = globalObject.GetOwnProperty(name);
-            if (existingProp == PropertyDescriptor.Undefined)
+            if (!_global._properties.TryGetValue(name, out var existingProp) 
+                || existingProp == PropertyDescriptor.Undefined)
             {
-                return globalObject.Extensible;
+                return _global.Extensible;
             }
 
             if (existingProp.Configurable)
@@ -203,13 +256,10 @@ namespace Jint.Runtime.Environments
 
         public void CreateGlobalVarBinding(string name, bool canBeDeleted)
         {
-            var globalObject = _objectRecord._bindingObject;
-            var hasProperty = globalObject.HasOwnProperty(name);
-            var extensible = globalObject.Extensible;
-            if (!hasProperty && extensible)
+            var hasProperty = _global.HasOwnProperty(name);
+            if (!hasProperty && _global.Extensible)
             {
-                _objectRecord.CreateMutableBinding(name, canBeDeleted);
-                _objectRecord.InitializeBinding(name, Undefined);
+                _objectRecord.CreateMutableBindingAndInitialize(name, Undefined, canBeDeleted);
             }
 
             _varNames.Add(name);
@@ -217,8 +267,7 @@ namespace Jint.Runtime.Environments
 
         public void CreateGlobalFunctionBinding(string name, JsValue value, bool canBeDeleted)
         {
-            var globalObject = _objectRecord._bindingObject;
-            var existingProp = globalObject.GetOwnProperty(name);
+            var existingProp = _global.GetOwnProperty(name);
 
             PropertyDescriptor desc;
             if (existingProp == PropertyDescriptor.Undefined || existingProp.Configurable)
@@ -230,8 +279,8 @@ namespace Jint.Runtime.Environments
                 desc = new PropertyDescriptor(value, PropertyFlag.None);
             }
 
-            globalObject.DefinePropertyOrThrow(name, desc);
-            globalObject.Set(name, value, false);
+            _global.DefinePropertyOrThrow(name, desc);
+            _global.Set(name, value, false);
             _varNames.Add(name);
         }
 
@@ -242,7 +291,7 @@ namespace Jint.Runtime.Environments
 
         internal override string[] GetAllBindingNames()
         {
-            return _objectRecord._bindingObject.GetOwnProperties().Select(x => x.Key.ToString()).ToArray();
+            return _global.GetOwnProperties().Select(x => x.Key.ToString()).ToArray();
         }
 
         public override bool Equals(JsValue other)

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

@@ -25,7 +25,7 @@ namespace Jint.Runtime.Environments
 
         internal static bool TryGetIdentifierEnvironmentWithBindingValue(
             LexicalEnvironment lex,
-            EnvironmentRecord.BindingName name,
+            in EnvironmentRecord.BindingName name,
             bool strict,
             out EnvironmentRecord record,
             out JsValue value)
@@ -33,6 +33,13 @@ namespace Jint.Runtime.Environments
             record = default;
             value = default;
 
+            if (ReferenceEquals(lex, lex._engine.GlobalEnvironment)
+                && lex._record.TryGetBinding(name, strict, out _, out value))
+            {
+                record = lex._record;
+                return true;
+            }
+
             while (lex != null)
             {
                 if (lex._record.TryGetBinding(

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

@@ -51,7 +51,7 @@ namespace Jint.Runtime.Environments
         }
 
         internal override bool TryGetBinding(
-            BindingName name,
+            in BindingName name,
             bool strict,
             out Binding binding,
             out JsValue value)
@@ -100,6 +100,18 @@ namespace Jint.Runtime.Environments
                 ? new PropertyDescriptor(Undefined, PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding)
                 : new PropertyDescriptor(Undefined, PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding);
 
+            _bindingObject.DefinePropertyOrThrow(name, propertyDescriptor);
+        }  
+        
+        /// <summary>
+        /// http://www.ecma-international.org/ecma-262/6.0/#sec-object-environment-records-createmutablebinding-n-d
+        /// </summary>
+        internal void CreateMutableBindingAndInitialize(string name, JsValue value, bool canBeDeleted = false)
+        {
+            var propertyDescriptor = canBeDeleted
+                ? new PropertyDescriptor(value, PropertyFlag.ConfigurableEnumerableWritable | PropertyFlag.MutableBinding)
+                : new PropertyDescriptor(value, PropertyFlag.NonConfigurable | PropertyFlag.MutableBinding);
+
             _bindingObject.DefinePropertyOrThrow(name, propertyDescriptor);
         }
         
@@ -124,7 +136,7 @@ namespace Jint.Runtime.Environments
             SetMutableBinding(new BindingName(name), value, strict);
         }
 
-        internal override void SetMutableBinding(BindingName name, JsValue value, bool strict)
+        internal override void SetMutableBinding(in BindingName name, JsValue value, bool strict)
         {
             if (!_bindingObject.Set(name.StringValue, value) && strict)
             {

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

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

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

@@ -24,9 +24,9 @@ namespace Jint.Runtime.Interpreter.Expressions
                 var expr = node.Elements[n];
                 if (expr != null)
                 {
-                    var expression = Build(_engine, (Expression) expr);
+                    var expression = Build(_engine, expr);
                     _expressions[n] = expression;
-                    _hasSpreads |= expression is JintSpreadExpression;
+                    _hasSpreads |= expr.Type == Nodes.SpreadElement;
                 }
             }
 

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

@@ -155,7 +155,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
             if ((typeX & InternalTypes.String) != 0)
             {
-                return x.AsStringWithoutTypeCheck() == y.AsStringWithoutTypeCheck();
+                return x.ToString() == y.ToString();
             }
 
             if (typeX == InternalTypes.Boolean)

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

@@ -42,9 +42,9 @@ namespace Jint.Runtime.Interpreter.Expressions
             bool cacheable = true;
             for (var i = 0; i < expression.Arguments.Count; i++)
             {
-                var expressionArgument = (Expression) expression.Arguments[i];
+                var expressionArgument = expression.Arguments[i];
                 cachedArgumentsHolder.JintArguments[i] = Build(_engine, expressionArgument);
-                cacheable &= expressionArgument is Literal;
+                cacheable &= expressionArgument.Type == Nodes.Literal;
                 _hasSpreads |= CanSpread(expressionArgument);
                 if (expressionArgument is ArrayExpression ae)
                 {

+ 0 - 33
Jint/Runtime/Interpreter/Expressions/JintExpression.cs

@@ -4,7 +4,6 @@ using Jint.Native;
 using Jint.Native.Array;
 using Jint.Native.Iterator;
 using Jint.Native.Number;
-using Jint.Runtime.Environments;
 
 namespace Jint.Runtime.Interpreter.Expressions
 {
@@ -367,38 +366,6 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
         }
 
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        protected bool TryGetIdentifierEnvironmentWithBindingValue(
-            EnvironmentRecord.BindingName expressionName,
-            out EnvironmentRecord record,
-            out JsValue value)
-        {
-            var env = _engine.ExecutionContext.LexicalEnvironment;
-            var strict = StrictModeScope.IsStrictModeCode;
-            return LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
-                env,
-                expressionName,
-                strict,
-                out record,
-                out value);
-        }
-
-        [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        protected bool TryGetIdentifierEnvironmentWithBindingValue(
-            bool strict,
-            EnvironmentRecord.BindingName expressionName,
-            out EnvironmentRecord record,
-            out JsValue value)
-        {
-            var env = _engine.ExecutionContext.LexicalEnvironment;
-            return LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
-                env,
-                expressionName,
-                strict,
-                out record,
-                out value);
-        }
-        
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         protected static bool AreIntegerOperands(JsValue left, JsValue right)
         {

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

@@ -43,7 +43,13 @@ namespace Jint.Runtime.Interpreter.Expressions
             }
 
             var strict = StrictModeScope.IsStrictModeCode;
-            return TryGetIdentifierEnvironmentWithBindingValue(strict, _expressionName, out _, out var value)
+            var env = _engine.ExecutionContext.LexicalEnvironment;
+            return LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
+                env,
+                _expressionName,
+                strict,
+                out _,
+                out var value)
                 ? value ?? ExceptionHelper.ThrowReferenceError<JsValue>(_engine, _expressionName.Key.Name + " has not been initialized")
                 : _engine.GetValue(_engine._referencePool.Rent(JsValue.Undefined, _expressionName.StringValue, strict), true);
         }

+ 5 - 2
Jint/Runtime/Interpreter/Expressions/JintMemberExpression.cs

@@ -1,5 +1,6 @@
 using Esprima.Ast;
 using Jint.Native;
+using Jint.Runtime.Environments;
 using Jint.Runtime.References;
 
 namespace Jint.Runtime.Interpreter.Expressions
@@ -53,9 +54,11 @@ namespace Jint.Runtime.Interpreter.Expressions
             {
                 baseReferenceName = _objectIdentifierExpression._expressionName.Key.Name;
                 var strict = isStrictModeCode;
-                TryGetIdentifierEnvironmentWithBindingValue(
-                    strict,
+                var env = _engine.ExecutionContext.LexicalEnvironment;
+                LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
+                    env,
                     _objectIdentifierExpression._expressionName,
+                    strict,
                     out _,
                     out baseValue);
             }

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

@@ -28,8 +28,9 @@ namespace Jint.Runtime.Interpreter.Expressions
             _jintArguments = new JintExpression[expression.Arguments.Count];
             for (var i = 0; i < _jintArguments.Length; i++)
             {
-                _jintArguments[i] = Build(_engine, (Expression) expression.Arguments[i]);
-                _hasSpreads |= _jintArguments[i] is JintSpreadExpression;
+                var argument = expression.Arguments[i];
+                _jintArguments[i] = Build(_engine, argument);
+                _hasSpreads |= argument.Type == Nodes.SpreadElement;
             }
         }
 

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

@@ -73,7 +73,7 @@ namespace Jint.Runtime.Interpreter.Expressions
 
                     if (p.Kind == PropertyKind.Init || p.Kind == PropertyKind.Data)
                     {
-                        var propertyValue = (Expression) p.Value;
+                        var propertyValue = p.Value;
                         _valueExpressions[i] = Build(_engine, propertyValue);
                         _canBuildFast &= !propertyValue.IsFunctionWithName();
                     }

+ 5 - 1
Jint/Runtime/Interpreter/Expressions/JintUpdateExpression.cs

@@ -1,5 +1,6 @@
 using Esprima.Ast;
 using Jint.Native;
+using Jint.Runtime.Environments;
 using Jint.Runtime.References;
 
 namespace Jint.Runtime.Interpreter.Expressions
@@ -76,8 +77,11 @@ namespace Jint.Runtime.Interpreter.Expressions
         {
             var strict = StrictModeScope.IsStrictModeCode;
             var name = _leftIdentifier._expressionName;
-            if (TryGetIdentifierEnvironmentWithBindingValue(
+            var env = _engine.ExecutionContext.LexicalEnvironment;
+            if (LexicalEnvironment.TryGetIdentifierEnvironmentWithBindingValue(
+                env,
                 name,
+                strict,
                 out var environmentRecord,
                 out var value))
             {

+ 5 - 3
Jint/Runtime/Interpreter/Statements/JintForStatement.cs

@@ -20,6 +20,8 @@ namespace Jint.Runtime.Interpreter.Statements
         private JintStatement _body;
         private List<string> _boundNames;
 
+        private bool _shouldCreatePerIterationEnvironment;
+
         public JintForStatement(Engine engine, ForStatement statement) : base(engine, statement)
         {
             _initialized = false;
@@ -40,6 +42,7 @@ namespace Jint.Runtime.Interpreter.Statements
                         d.GetBoundNames(_boundNames);
                     }
                     _initStatement = new JintVariableDeclaration(_engine, d);
+                    _shouldCreatePerIterationEnvironment = d.Kind == VariableDeclarationKind.Let;
                 }
                 else
                 {
@@ -113,8 +116,7 @@ namespace Jint.Runtime.Interpreter.Statements
         {
             var v = Undefined.Instance;
 
-            var shouldCreatePerIterationEnvironment = _initStatement?._statement?.Kind == VariableDeclarationKind.Let;
-            if (shouldCreatePerIterationEnvironment)
+            if (_shouldCreatePerIterationEnvironment)
             {
                 CreatePerIterationEnvironment();
             }
@@ -148,7 +150,7 @@ namespace Jint.Runtime.Interpreter.Statements
                     }
                 }
 
-                if (shouldCreatePerIterationEnvironment)
+                if (_shouldCreatePerIterationEnvironment)
                 {
                     CreatePerIterationEnvironment();
                 }

+ 1 - 1
Jint/Runtime/JavaScriptException.cs

@@ -109,7 +109,7 @@ namespace Jint.Runtime
                 return message;
             }
             if (error.IsString())
-                return error.AsStringWithoutTypeCheck();
+                return error.ToString();
 
             return error.ToString();
         }

+ 3 - 7
Jint/Runtime/TypeConverter.cs

@@ -192,7 +192,7 @@ namespace Jint.Runtime
                 InternalTypes.Null => 0,
                 InternalTypes.Object when o is IPrimitiveInstance p => ToNumber(ToPrimitive(p.PrimitiveValue, Types.Number)),
                 InternalTypes.Boolean => (((JsBoolean) o)._value ? 1 : 0),
-                InternalTypes.String => ToNumber(o.AsStringWithoutTypeCheck()),
+                InternalTypes.String => ToNumber(o.ToString()),
                 InternalTypes.Symbol =>
                 // TODO proper TypeError would require Engine instance and a lot of API changes
                 ExceptionHelper.ThrowTypeErrorNoEngine<double>("Cannot convert a Symbol value to a number"),
@@ -454,11 +454,7 @@ namespace Jint.Runtime
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static string ToString(JsValue o)
         {
-            if (o.IsString())
-            {
-                return o.AsStringWithoutTypeCheck();
-            }
-            return ToStringNonString(o);
+            return o.IsString() ? o.ToString() : ToStringNonString(o);
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -498,7 +494,7 @@ namespace Jint.Runtime
                 InternalTypes.Boolean => engine.Boolean.Construct(((JsBoolean) value)._value),
                 InternalTypes.Number => engine.Number.Construct(((JsNumber) value)._value),
                 InternalTypes.Integer => engine.Number.Construct(((JsNumber) value)._value),
-                InternalTypes.String => engine.String.Construct(value.AsStringWithoutTypeCheck()),
+                InternalTypes.String => engine.String.Construct(value.ToString()),
                 InternalTypes.Symbol => engine.Symbol.Construct(((JsSymbol) value)),
                 _ => ExceptionHelper.ThrowTypeError<ObjectInstance>(engine)
             };