From fa48ccde254b2b3df56154313b55abf3a6c967cc Mon Sep 17 00:00:00 2001 From: "yangguo@chromium.org" Date: Mon, 18 Jun 2012 13:39:24 +0000 Subject: [PATCH] Print correct line number for Error thrown inside eval. R=rossberg@chromium.org BUG=v8:1914 TEST=eval-stack-trace.js Review URL: https://chromiumcodereview.appspot.com/10565002 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11851 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/messages.js | 29 +++--- test/mjsunit/eval-stack-trace.js | 203 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 220 insertions(+), 12 deletions(-) create mode 100644 test/mjsunit/eval-stack-trace.js diff --git a/src/messages.js b/src/messages.js index 2a00ba8..d91c251 100644 --- a/src/messages.js +++ b/src/messages.js @@ -921,17 +921,25 @@ function CallSiteToString() { var fileLocation = ""; if (this.isNative()) { fileLocation = "native"; - } else if (this.isEval()) { - fileName = this.getScriptNameOrSourceURL(); - if (!fileName) { - fileLocation = this.getEvalOrigin(); - } } else { - fileName = this.getFileName(); - } + if (this.isEval()) { + fileName = this.getScriptNameOrSourceURL(); + if (!fileName) { + fileLocation = this.getEvalOrigin(); + fileLocation += ", "; // Expecting source position to follow. + } + } else { + fileName = this.getFileName(); + } - if (fileName) { - fileLocation += fileName; + if (fileName) { + fileLocation += fileName; + } else { + // Source code does not originate from a file and is not native, but we + // can still get the source position inside the source string, e.g. in + // an eval string. + fileLocation += ""; + } var lineNumber = this.getLineNumber(); if (lineNumber != null) { fileLocation += ":" + lineNumber; @@ -942,9 +950,6 @@ function CallSiteToString() { } } - if (!fileLocation) { - fileLocation = "unknown source"; - } var line = ""; var functionName = this.getFunctionName(); var addSuffix = true; diff --git a/test/mjsunit/eval-stack-trace.js b/test/mjsunit/eval-stack-trace.js new file mode 100644 index 0000000..723d522 --- /dev/null +++ b/test/mjsunit/eval-stack-trace.js @@ -0,0 +1,203 @@ +// Copyright 2012 the V8 project authors. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Return the stack frames of an Error object. +Error.prototype.getFrames = function() { + Error.prepareStackTrace = function(error, frames) { + return frames; + } + var frames = this.stack; + Error.prepareStackTrace = undefined; + return frames; +} + +String.prototype.contains = function(pattern) { + return this.indexOf(pattern) > -1; +} + +// Check for every frame that a certain method returns the +// expected value for every frame. +Array.prototype.verifyEquals = function(frames, func_name) { + this.forEach( + function(element, index) { + var frame = frames[index]; + if (element === null) return; + assertEquals(element, (frame[func_name])()); + } + ); +} + +// Check for every frame that a certain method has a return value +// that contains the expected pattern for every frame. +Array.prototype.verifyContains = function(frames, func_name) { + this.forEach( + function(element, index) { + var frame = frames[index]; + if (element === null) return; + assertTrue((frame[func_name])().contains(element)); + } + ); +} + +// Check for every frame that a certain method returns undefined +// when expected. +Array.prototype.verifyUndefined = function(frames, func_name) { + this.forEach( + function(element, index) { + var frame = frames[index]; + if (element === null) return; + assertEquals(element, (frame[func_name])() === undefined); + } + ); +} + + +// Simple eval. +var code1 = "function f() { \n" + + " throw new Error(3); \n" + // Line 2 + "} \n" + + "f(); \n"; // Line 4 + +function g() { + eval(code1); +} + +try { + g(); +} catch (e) { + // We expect something like + // f (eval at g (eval-stack.js:87:8), :2:9) + // eval (eval at g (eval-stack.js:87:8), :4:1) + // g (eval-stack.js:87:3) + // eval-stack.js:94:3 + var frames = e.getFrames(); + assertEquals(4, frames.length); + ["f", "eval", "g"] + .verifyEquals(frames, "getFunctionName"); + [2, 4] + .verifyEquals(frames, "getLineNumber"); + [":2:", ":4:"] + .verifyContains(frames, "toString"); + [true, true, false, false] + .verifyUndefined(frames, "getFileName"); + ["eval at g", "eval at g"] + .verifyContains(frames, "getEvalOrigin"); +} + + +// Nested eval. +var code2 = "function h() { \n" + + " // Empty \n" + + " eval(code1); \n" + // Line 3 + "} \n" + + "h(); \n"; // Line 5 + +try { + eval(code2); +} catch (e) { + // We expect something like + // f (eval at h (eval at (eval-stack.js:116:8)), + // :2:9) + // eval (eval at h (eval at (eval-stack.js:116:8)), + // :4:1) + // h (eval at (eval-stack.js:116:8), :3:3) + // eval (eval at (eval-stack.js:116:8), :5:1) + // eval-stack.js:116:3 + var frames = e.getFrames(); + assertEquals(5, frames.length); + ["f", "eval", "h", "eval"] + .verifyEquals(frames, "getFunctionName"); + [2, 4, 3, 5] + .verifyEquals(frames, "getLineNumber"); + [":2:", ":4:", ":3:", ":5:"] + .verifyContains(frames, "toString"); + [true, true, true, true, false] + .verifyUndefined(frames, "getFileName"); + ["eval at h (eval at (", + "eval at h (eval at (", + "eval at (", + "eval at ("] + .verifyContains(frames, "getEvalOrigin"); +} + + +// Nested eval calling through non-eval defined function. +var code3 = "function h() { \n" + + " // Empty \n" + + " g(); \n" + // Line 3 + "} \n" + + "h(); \n"; // Line 5 + +try { + eval(code3); +} catch (e) { + // We expect something like + // f (eval at g (test.js:83:8), :2:9) + // eval (eval at g (test.js:83:8), :4:1) + // g (test.js:83:3) + // h (eval at (test.js:149:8), :3:3) + // eval (eval at (test.js:149:8), :5:1) + // test.js:149:3 + var frames = e.getFrames(); + assertEquals(6, frames.length); + ["f", "eval", "g", "h", "eval"] + .verifyEquals(frames, "getFunctionName"); + [2, 4, null, 3, 5] + .verifyEquals(frames, "getLineNumber"); + [":2:", ":4:", null, ":3:", ":5:"] + .verifyContains(frames, "toString"); + [true, true, false, true, true, false] + .verifyUndefined(frames, "getFileName"); + ["eval at g (", + "eval at g (", + null, + "eval at (", + "eval at ("] + .verifyContains(frames, "getEvalOrigin"); +} + + +// Calling function defined in eval. +eval("function f() { \n" + + " throw new Error(3); \n" + + "} \n"); + +try { + f(); +} catch (e) { + // We expect something like + // f (eval at (test.js:182:40), :2:9) + // test.js:186:3 + var frames = e.getFrames(); + assertEquals(2, frames.length); + ["f"].verifyEquals(frames, "getFunctionName"); + [2].verifyEquals(frames, "getLineNumber"); + [":2:"].verifyContains(frames, "toString"); + [true, false].verifyUndefined(frames, "getFileName"); + ["eval at ("].verifyContains(frames, "getEvalOrigin"); +} + -- 2.7.4