Browse Source

Fix array indexing with hex literal (#711)

Marko Lahma 5 years ago
parent
commit
a81a881486

+ 10 - 0
Jint.Tests/Runtime/EngineTests.cs

@@ -1885,6 +1885,16 @@ var prep = function (fn) { fn(); };
             ");
             ");
         }
         }
 
 
+        [Fact]
+        public void HexZeroAsArrayIndexShouldWork()
+        {
+            var engine = new Engine();
+            engine.Execute("var t = '1234'; var value = null;");
+            Assert.Equal("1", engine.Execute("value = t[0x0];").GetValue("value").AsString());
+            Assert.Equal("1", engine.Execute("value = t[0];").GetValue("value").AsString());
+            Assert.Equal("1", engine.Execute("value = t['0'];").GetValue("value").AsString());
+        }
+
         [Fact]
         [Fact]
         public void DatePrototypeFunctionWorkOnDateOnly()
         public void DatePrototypeFunctionWorkOnDateOnly()
         {
         {

+ 42 - 19
Jint/Engine.cs

@@ -546,27 +546,16 @@ namespace Jint
                 else
                 else
                 {
                 {
                     // check if we are accessing a string, boxing operation can be costly to do index access
                     // check if we are accessing a string, boxing operation can be costly to do index access
-                    ObjectInstance o;
-                    if (baseValue is JsString s)
+                    // we have good chance to have fast path with integer or string indexer
+                    ObjectInstance o = null;
+                    if ((property._type & (InternalTypes.String | InternalTypes.Integer)) != 0
+                        && baseValue is JsString s
+                        && TryHandleStringValue(property, s, ref o, out var jsValue))
                     {
                     {
-                        if (property == CommonProperties.Length)
-                        {
-                            return s.Length;
-                        }
-
-                        if (property is JsNumber number && number.IsInteger())
-                        {
-                            var index = number.AsInteger();
-                            if (index < 0 || index >= s.Length)
-                            {
-                                return JsValue.Undefined;
-                            }
-                            return s[index];
-                        }
-
-                        o = String.PrototypeObject;
+                        return jsValue;
                     }
                     }
-                    else
+
+                    if (o is null)
                     {
                     {
                         o = TypeConverter.ToObject(this, baseValue);
                         o = TypeConverter.ToObject(this, baseValue);
                     }
                     }
@@ -608,6 +597,40 @@ namespace Jint
             return bindingValue;
             return bindingValue;
         }
         }
 
 
+        private bool TryHandleStringValue(JsValue property, JsString s, ref ObjectInstance o, out JsValue jsValue)
+        {
+            if (property == CommonProperties.Length)
+            {
+                jsValue = JsNumber.Create((uint) s.Length);
+                return true;
+            }
+
+            if (property is JsNumber number && number.IsInteger())
+            {
+                var index = number.AsInteger();
+                var str = s._value;
+                if (index < 0 || index >= str.Length)
+                {
+                    jsValue = JsValue.Undefined;
+                    return true;
+                }
+
+                jsValue = JsString.Create(str[index]);
+                return true;
+            }
+
+            if (property is JsString propertyString
+                && propertyString._value.Length > 0
+                && char.IsLower(propertyString._value[0]))
+            {
+                // trying to find property that's always in prototype
+                o = String.PrototypeObject;
+            }
+
+            jsValue = JsValue.Undefined;
+            return false;
+        }
+
         /// <summary>
         /// <summary>
         /// http://www.ecma-international.org/ecma-262/#sec-putvalue
         /// http://www.ecma-international.org/ecma-262/#sec-putvalue
         /// </summary>
         /// </summary>

+ 3 - 1
Jint/Runtime/Interpreter/Expressions/JintLiteralExpression.cs

@@ -1,4 +1,5 @@
 using System;
 using System;
+using System.Globalization;
 using Esprima;
 using Esprima;
 using Esprima.Ast;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Native;
@@ -36,7 +37,8 @@ namespace Jint.Runtime.Interpreter.Expressions
 
 
             if (literal.TokenType == TokenType.NumericLiteral)
             if (literal.TokenType == TokenType.NumericLiteral)
             {
             {
-                return int.TryParse(literal.Raw, out var intValue)
+                var intValue = (int) literal.NumericValue;
+                return literal.NumericValue == intValue
                        && (intValue != 0 || BitConverter.DoubleToInt64Bits(literal.NumericValue) != JsNumber.NegativeZeroBits)
                        && (intValue != 0 || BitConverter.DoubleToInt64Bits(literal.NumericValue) != JsNumber.NegativeZeroBits)
                     ? JsNumber.Create(intValue)
                     ? JsNumber.Create(intValue)
                     : JsNumber.Create(literal.NumericValue);
                     : JsNumber.Create(literal.NumericValue);