Browse Source

Fix IIFE stack trace (#841)

Marko Lahma 4 years ago
parent
commit
8c357f5b88

+ 34 - 0
Jint.Tests/Runtime/ErrorTests.cs

@@ -158,6 +158,40 @@ var x = b(7);";
             
             
             EqualIgnoringNewLineDifferences(expected, ex.ToString());
             EqualIgnoringNewLineDifferences(expected, ex.ToString());
         }
         }
+        
+        [Fact]
+        public void StackTraceCollectedForImmediatelyInvokedFunctionExpression()
+        {
+            var engine = new Engine();
+            const string script = @"function getItem(items, itemIndex) {
+    var item = items[itemIndex];
+
+    return item;
+}
+
+(function (getItem) {
+    var items = null,
+        item = getItem(items, 5)
+        ;
+
+    return item;
+})(getItem);";
+
+            var parserOptions = new ParserOptions("get-item.js")
+            {
+                AdaptRegexp = true,
+                Tolerant = true
+            };
+            var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(script, parserOptions));
+
+            const string expected = @"Jint.Runtime.JavaScriptException: Cannot read property '5' of null
+   at getItem (items, itemIndex) get-item.js:2:22
+   at (anonymous) (getItem) get-item.js:9:16
+   at get-item.js:13:2";
+            
+            EqualIgnoringNewLineDifferences(expected, ex.ToString());
+        }
+
 
 
         private static void EqualIgnoringNewLineDifferences(string expected, string actual)
         private static void EqualIgnoringNewLineDifferences(string expected, string actual)
         {
         {

+ 0 - 1
Jint/Native/Array/ArrayPrototype.cs

@@ -1,6 +1,5 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using Jint.Collections;
 using Jint.Collections;
 using Jint.Native.Number;
 using Jint.Native.Number;

+ 13 - 2
Jint/Runtime/CallStack/CallStackElement.cs

@@ -20,8 +20,19 @@ namespace Jint.Runtime.CallStack
         public readonly FunctionInstance Function;
         public readonly FunctionInstance Function;
         public readonly JintExpression? Expression;
         public readonly JintExpression? Expression;
 
 
-        public Location Location =>
-            Expression?._expression.Location ?? ((Node?) Function._functionDefinition?.Function)?.Location ?? default;
+        public Location Location
+        {
+            get
+            {
+                var expressionLocation = Expression?._expression.Location;
+                if (expressionLocation != null && expressionLocation.Value != default)
+                {
+                    return expressionLocation.Value;
+                }
+
+                return ((Node?) Function._functionDefinition?.Function)?.Location ?? default;
+            }
+        }
 
 
         public NodeList<Expression>? Arguments =>
         public NodeList<Expression>? Arguments =>
             Function._functionDefinition?.Function.Params;
             Function._functionDefinition?.Function.Params;

+ 8 - 8
Jint/Runtime/CallStack/JintCallStack.cs

@@ -78,8 +78,8 @@ namespace Jint.Runtime.CallStack
             static void AppendLocation(
             static void AppendLocation(
                 StringBuilder sb,
                 StringBuilder sb,
                 string shortDescription,
                 string shortDescription,
-                Location loc,
-                in NodeList<Expression>? arguments)
+                in Location loc,
+                in CallStackElement? element)
             {
             {
                 sb
                 sb
                     .Append("   at");
                     .Append("   at");
@@ -91,18 +91,18 @@ namespace Jint.Runtime.CallStack
                         .Append(shortDescription);
                         .Append(shortDescription);
                 }
                 }
 
 
-                if (arguments is not null)
+                if (element?.Arguments is not null)
                 {
                 {
                     // it's a function
                     // it's a function
                     sb.Append(" (");
                     sb.Append(" (");
-                    for (var index = 0; index < arguments.Value.Count; index++)
+                    for (var index = 0; index < element.Value.Arguments.Value.Count; index++)
                     {
                     {
                         if (index != 0)
                         if (index != 0)
                         {
                         {
                             sb.Append(", ");
                             sb.Append(", ");
                         }
                         }
 
 
-                        var arg = arguments.Value[index];
+                        var arg = element.Value.Arguments.Value[index];
                         sb.Append(GetPropertyKey(arg));
                         sb.Append(GetPropertyKey(arg));
                     }
                     }
                     sb.Append(")");
                     sb.Append(")");
@@ -112,7 +112,7 @@ namespace Jint.Runtime.CallStack
                     .Append(" ")
                     .Append(" ")
                     .Append(loc.Source)
                     .Append(loc.Source)
                     .Append(":")
                     .Append(":")
-                    .Append(loc.Start.Line)
+                    .Append(loc.End.Line)
                     .Append(":")
                     .Append(":")
                     .Append(loc.Start.Column + 1) // report column number instead of index
                     .Append(loc.Start.Column + 1) // report column number instead of index
                     .AppendLine();
                     .AppendLine();
@@ -125,7 +125,7 @@ namespace Jint.Runtime.CallStack
             var element = index >= 0 ? _stack[index] : (CallStackElement?) null;
             var element = index >= 0 ? _stack[index] : (CallStackElement?) null;
             var shortDescription = element?.ToString() ?? "";
             var shortDescription = element?.ToString() ?? "";
 
 
-            AppendLocation(sb.Builder, shortDescription, location, element?.Arguments);
+            AppendLocation(sb.Builder, shortDescription, location, element);
 
 
             location = element?.Location ?? default;
             location = element?.Location ?? default;
             index--;
             index--;
@@ -135,7 +135,7 @@ namespace Jint.Runtime.CallStack
                 element = index >= 0 ? _stack[index] : null;
                 element = index >= 0 ? _stack[index] : null;
                 shortDescription = element?.ToString() ?? "";
                 shortDescription = element?.ToString() ?? "";
 
 
-                AppendLocation(sb.Builder, shortDescription, location, element?.Arguments);
+                AppendLocation(sb.Builder, shortDescription, location, element);
 
 
                 location = element?.Location ?? default;
                 location = element?.Location ?? default;
                 index--;
                 index--;

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

@@ -89,7 +89,12 @@ namespace Jint.Runtime.Interpreter.Expressions
             var property = _determinedProperty ?? _propertyExpression.GetValue();
             var property = _determinedProperty ?? _propertyExpression.GetValue();
             if (baseValue.IsNullOrUndefined())
             if (baseValue.IsNullOrUndefined())
             {
             {
-                TypeConverter.CheckObjectCoercible(_engine, baseValue, _memberExpression.Property, _determinedProperty?.ToString() ?? baseReferenceName);
+                // we can use base data types securely, object evaluation can mess things up
+                var referenceName = property.IsPrimitive()
+                    ? TypeConverter.ToString(property)
+                    : _determinedProperty?.ToString() ?? baseReferenceName;
+
+                TypeConverter.CheckObjectCoercible(_engine, baseValue, _memberExpression.Property, referenceName);
             }
             }
 
 
             // only convert if necessary
             // only convert if necessary

+ 3 - 4
Jint/Runtime/TypeConverter.cs

@@ -2,7 +2,6 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
 using System.Runtime.CompilerServices;
 using System.Runtime.CompilerServices;
-using Esprima;
 using Esprima.Ast;
 using Esprima.Ast;
 using Jint.Native;
 using Jint.Native;
 using Jint.Native.Number;
 using Jint.Native.Number;
@@ -552,7 +551,7 @@ namespace Jint.Runtime
         {
         {
             if (!engine.Options.ReferenceResolver.CheckCoercible(o))
             if (!engine.Options.ReferenceResolver.CheckCoercible(o))
             {
             {
-                ThrowMemberNullOrUndefinedError(engine, o, sourceNode.Location, referenceName);
+                ThrowMemberNullOrUndefinedError(engine, o, sourceNode, referenceName);
             }
             }
         }
         }
 
 
@@ -560,12 +559,12 @@ namespace Jint.Runtime
         private static void ThrowMemberNullOrUndefinedError(
         private static void ThrowMemberNullOrUndefinedError(
             Engine engine,
             Engine engine,
             JsValue o,
             JsValue o,
-            in Location location,
+            Node sourceNode,
             string referencedName)
             string referencedName)
         {
         {
             referencedName ??= "unknown";
             referencedName ??= "unknown";
             var message = $"Cannot read property '{referencedName}' of {o}";
             var message = $"Cannot read property '{referencedName}' of {o}";
-            throw new JavaScriptException(engine.TypeError, message).SetCallstack(engine, location);
+            throw new JavaScriptException(engine.TypeError, message).SetCallstack(engine, sourceNode.Location);
         }
         }
 
 
         public static void CheckObjectCoercible(Engine engine, JsValue o)
         public static void CheckObjectCoercible(Engine engine, JsValue o)