123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- using System.Reflection;
- using Jint.Native;
- using Jint.Runtime;
- using Jint.Tests.Runtime.TestClasses;
- namespace Jint.Tests.Runtime;
- public class ErrorTests
- {
- [Fact]
- public void CanReturnCorrectErrorMessageAndLocation1()
- {
- const string script = @"
- var a = {};
- var b = a.user.name;
- ";
- var engine = new Engine();
- var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
- Assert.Equal("Cannot read property 'name' of undefined", e.Message);
- Assert.Equal(4, e.Location.Start.Line);
- Assert.Equal(15, e.Location.Start.Column);
- }
- [Fact]
- public void CanReturnCorrectErrorMessageAndLocation1WithoutReferencedName()
- {
- const string script = @"
- var c = a(b().Length);
- ";
- var engine = new Engine();
- engine.SetValue("a", new Action<string>((_) => { }));
- engine.SetValue("b", new Func<string>(() => null));
- var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
- Assert.Equal("Cannot read property 'Length' of null", e.Message);
- Assert.Equal(2, e.Location.Start.Line);
- Assert.Equal(14, e.Location.Start.Column);
- }
- [Fact]
- public void CanReturnCorrectErrorMessageAndLocation2()
- {
- const string script = @"
- test();
- ";
- var engine = new Engine();
- var e = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
- Assert.Equal("test is not defined", e.Message);
- Assert.Equal(2, e.Location.Start.Line);
- Assert.Equal(1, e.Location.Start.Column);
- }
- [Fact]
- public void CanProduceCorrectStackTraceForInternalError()
- {
- var engine = new Engine();
- engine.Execute(@"
- var a = function(v) {
- return v.xxx.yyy;
- }
- var b = function(v) {
- return a(v);
- }
- ", "custom.js");
- var e = Assert.Throws<JavaScriptException>(() => engine.Execute("var x = b(7);", "main.js"));
- Assert.Equal("Cannot read property 'yyy' of undefined", e.Message);
- Assert.Equal(3, e.Location.Start.Line);
- Assert.Equal(15, e.Location.Start.Column);
- Assert.Equal("custom.js", e.Location.SourceFile);
- var stack = e.JavaScriptStackTrace;
- EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:16
- at b (v) custom.js:7:10
- at main.js:1:9", stack);
- }
- [Fact]
- public void CanProduceCorrectStackTraceForScriptError()
- {
- var engine = new Engine();
- engine.Execute(@"
- var a = function(v) {
- throw new Error('Error thrown from script');
- }
- var b = function(v) {
- return a(v);
- }
- ", "custom.js");
- var e = Assert.Throws<JavaScriptException>(() => engine.Execute("var x = b(7);", "main.js"));
- Assert.Equal("Error thrown from script", e.Message);
- Assert.Equal(3, e.Location.Start.Line);
- Assert.Equal(8, e.Location.Start.Column);
- Assert.Equal("custom.js", e.Location.SourceFile);
- var stack = e.JavaScriptStackTrace;
- EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:9
- at b (v) custom.js:7:10
- at main.js:1:9", stack);
- }
- [Fact]
- public void ErrorObjectHasTheStackTraceImmediately()
- {
- var engine = new Engine();
- engine.Execute(@"
- var a = function(v) {
- return Error().stack;
- }
- var b = function(v) {
- return a(v);
- }
- ", "custom.js");
- var e = engine.Evaluate(@"b(7)", "main.js").AsString();
- var stack = e;
- EqualIgnoringNewLineDifferences(@" at a (v) custom.js:3:10
- at b (v) custom.js:7:10
- at main.js:1:1", stack);
- }
- [Fact]
- public void ThrownErrorObjectHasStackTraceInCatch()
- {
- var engine = new Engine();
- engine.Execute(@"
- var a = function(v) {
- try {
- throw Error();
- } catch(err) {
- return err.stack;
- }
- }
- var b = function(v) {
- return a(v);
- }
- ", "custom.js");
- var e = engine.Evaluate(@"b(7)", "main.js").AsString();
- var stack = e;
- EqualIgnoringNewLineDifferences(@" at a (v) custom.js:4:11
- at b (v) custom.js:11:10
- at main.js:1:1", stack);
- }
- [Fact]
- public void GeneratedErrorHasStackTraceInCatch()
- {
- var engine = new Engine();
- engine.Execute(@"
- var a = function(v) {
- try {
- var a = ''.xyz();
- } catch(err) {
- return err.stack;
- }
- }
- var b = function(v) {
- return a(v);
- }
- ", "custom.js");
- var e = engine.Evaluate(@"b(7)", "main.js").AsString();
- var stack = e;
- EqualIgnoringNewLineDifferences(@" at a (v) custom.js:4:13
- at b (v) custom.js:11:10
- at main.js:1:1", stack);
- }
- [Fact]
- public void ErrorObjectHasOwnPropertyStack()
- {
- var res = new Engine().Evaluate(@"Error().hasOwnProperty('stack')").AsBoolean();
- Assert.True(res);
- }
- private class Folder
- {
- public Folder Parent { get; set; }
- public string Name { get; set; }
- }
- [Fact]
- public void CallStackBuildingShouldSkipResolvingFromEngine()
- {
- var engine = new Engine(o => o.LimitRecursion(200));
- var recordedFolderTraversalOrder = new List<string>();
- engine.SetValue("log", new Action<object>(o => recordedFolderTraversalOrder.Add(o.ToString())));
- var folder = new Folder
- {
- Name = "SubFolder2",
- Parent = new Folder
- {
- Name = "SubFolder1",
- Parent = new Folder
- {
- Name = "Root",
- Parent = null,
- }
- }
- };
- engine.SetValue("folder", folder);
- var javaScriptException = Assert.Throws<JavaScriptException>(() =>
- engine.Execute(@"
- var Test = {
- recursive: function(folderInstance) {
- // Enabling the guard here corrects the problem, but hides the hard fault
- // if (folderInstance==null) return null;
- log(folderInstance.Name);
- if (folderInstance==null) return null;
- return this.recursive(folderInstance.parent);
- }
- }
- Test.recursive(folder);"
- ));
- Assert.Equal("Cannot read property 'Name' of null", javaScriptException.Message);
- EqualIgnoringNewLineDifferences(@" at recursive (folderInstance) <anonymous>:6:44
- at recursive (folderInstance) <anonymous>:8:32
- at recursive (folderInstance) <anonymous>:8:32
- at recursive (folderInstance) <anonymous>:8:32
- at <anonymous>:12:17", javaScriptException.JavaScriptStackTrace);
- var expected = new List<string>
- {
- "SubFolder2", "SubFolder1", "Root"
- };
- Assert.Equal(expected, recordedFolderTraversalOrder);
- }
- [Fact]
- public void StackTraceCollectedOnThreeLevels()
- {
- var engine = new Engine();
- const string script = @"var a = function(v) {
- return v.xxx.yyy;
- }
- var b = function(v) {
- return a(v);
- }
- var x = b(7);";
- var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(script));
- const string expected = @"Error: Cannot read property 'yyy' of undefined
- at a (v) <anonymous>:2:18
- at b (v) <anonymous>:6:12
- at <anonymous>:9:9";
- EqualIgnoringNewLineDifferences(expected, ex.GetJavaScriptErrorString());
- Assert.Equal(2, ex.Location.Start.Line);
- Assert.Equal(17, ex.Location.Start.Column);
- }
- [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 parsingOptions = new ScriptParsingOptions
- {
- CompileRegex = false,
- Tolerant = true
- };
- var ex = Assert.Throws<JavaScriptException>(() => engine.Execute(script, "get-item.js", parsingOptions));
- const string expected = @"Error: 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.GetJavaScriptErrorString());
- Assert.Equal(2, ex.Location.Start.Line);
- Assert.Equal(21, ex.Location.Start.Column);
- }
- // Verify #1202
- [Fact]
- public void StackIsUnwoundWhenExceptionHandledByInteropCode()
- {
- var engine = new Engine()
- .SetValue("handle", new Action<Action>(Handler));
- const string Script = @"
- function throwIt(message) {
- throw new Error(message);
- }
- handle(() => throwIt('e1'));
- handle(() => throwIt('e2'));
- handle(() => throwIt('e3'));
-
- try {
- throwIt('e4');
- } catch(x){
- x.stack; // return stack trace string
- }
- ";
- var stack = engine.Evaluate(Script).AsString();
- EqualIgnoringNewLineDifferences(@" at throwIt (message) <anonymous>:3:11
- at <anonymous>:11:5", stack);
- static void Handler(Action callback)
- {
- try
- {
- callback();
- }
- catch (JavaScriptException)
- {
- // handle JS error
- }
- }
- }
- [Fact]
- public void StackTraceIsForOriginalException()
- {
- var engine = new Engine();
- engine.SetValue("HelloWorld", new HelloWorld());
- const string script = @"HelloWorld.ThrowException();";
- var ex = Assert.Throws<DivideByZeroException>(() => engine.Execute(script));
- const string expected = "HelloWorld";
- ContainsIgnoringNewLineDifferences(expected, ex.ToString());
- }
- [Theory]
- [InlineData("Error")]
- [InlineData("EvalError")]
- [InlineData("RangeError")]
- [InlineData("SyntaxError")]
- [InlineData("TypeError")]
- [InlineData("ReferenceError")]
- public void ErrorsHaveCorrectConstructor(string type)
- {
- var engine = new Engine();
- engine.Execute($"const o = new {type}();");
- Assert.True(engine.Evaluate($"o.constructor === {type}").AsBoolean());
- Assert.Equal(type, engine.Evaluate("o.constructor.name").AsString());
- }
- [Fact]
- public void CallStackWorksWithRecursiveCalls()
- {
- static ScriptParsingOptions CreateParsingOptions()
- {
- return new ScriptParsingOptions
- {
- CompileRegex = false,
- Tolerant = true
- };
- }
- var e = Assert.Throws<JavaScriptException>(() =>
- {
- var engine = new Engine();
- engine.SetValue("executeFile", (Action<string>) (path =>
- {
- var content = path switch
- {
- "first-file.js" => @"num = num * 3;
- executeFile(""second-file.js"");",
- "second-file.js" => @"// Intentionally making a mistake in the variable name
- nuм -= 3;",
- _ => throw new FileNotFoundException($"File '{path}' not exist.", path)
- };
- engine.Execute(content, path, CreateParsingOptions());
- }));
- engine.Execute(
- @"var num = 5;
- executeFile(""first-file.js"");",
- "main-file.js",
- CreateParsingOptions()
- );
- });
- Assert.Equal("nuм is not defined", e.Message);
- const string Expected = @" at delegate second-file.js:2:1
- at delegate first-file.js:2:1
- at main-file.js:2:1";
- EqualIgnoringNewLineDifferences(Expected, e.JavaScriptStackTrace);
- }
- [Fact]
- public void ShouldReportCorrectColumn()
- {
- var e = Assert.Throws<JavaScriptException>(() =>
- {
- var engine = new Engine();
- engine.Execute(@"var $variable1 = 611;
- var _variable2 = 711;
- var variable3 = 678;
- $variable1 + -variable2 - variable3;");
- });
- Assert.Equal(5, e.Location.Start.Line);
- Assert.Equal(14, e.Location.Start.Column);
- Assert.Equal(" at <anonymous>:5:15", e.JavaScriptStackTrace);
- }
- [Fact]
- public void InvokingDelegateShouldContainJavascriptExceptionAsInnerException()
- {
- Delegate func = null;
- void SetFuncValue(Delegate scriptFunc) => func = scriptFunc;
- var engine = new Engine();
- engine.SetValue("SetFuncValue", SetFuncValue);
- engine.Execute("SetFuncValue(() => { foo.bar });");
- var ex = Assert.Throws<TargetInvocationException>(() => func?.DynamicInvoke(JsValue.Undefined, Array.Empty<JsValue>()));
- var exception = Assert.IsType<JavaScriptException>(ex.InnerException);
- Assert.Equal("foo is not defined", exception.Message);
- }
- [Fact]
- public void JavaScriptExceptionLocationOnModuleShouldBeRight()
- {
- var engine = new Engine();
- engine.Modules.Add("my_module", @"
- function throw_error(){
- throw Error(""custom error"")
- }
- throw_error();
- ");
- var ex= Assert.Throws<JavaScriptException>(() => engine.Modules.Import("my_module"));
- Assert.Equal(ex.Location.Start.Line, 3);
- Assert.Equal(ex.Location.Start.Column, 10);
- }
- private static void EqualIgnoringNewLineDifferences(string expected, string actual)
- {
- expected = expected.Replace("\r\n", "\n");
- actual = actual.Replace("\r\n", "\n");
- Assert.Equal(expected, actual);
- }
- private static void ContainsIgnoringNewLineDifferences(string expectedSubstring, string actualString)
- {
- expectedSubstring = expectedSubstring.Replace("\r\n", "\n");
- actualString = actualString.Replace("\r\n", "\n");
- Assert.Contains(expectedSubstring, actualString);
- }
- }
|