diff --git a/src/org/mozilla/javascript/RhinoException.java b/src/org/mozilla/javascript/RhinoException.java index dfd2820c29..6fb2109cd9 100644 --- a/src/org/mozilla/javascript/RhinoException.java +++ b/src/org/mozilla/javascript/RhinoException.java @@ -4,7 +4,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - package org.mozilla.javascript; import java.io.CharArrayWriter; @@ -16,31 +15,23 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -/** - * The class of exceptions thrown by the JavaScript engine. - */ -public abstract class RhinoException extends RuntimeException -{ +/** The class of exceptions thrown by the JavaScript engine. */ +public abstract class RhinoException extends RuntimeException { private static final Pattern JAVA_STACK_PATTERN = Pattern.compile("_c_(.*)_\\d+"); - RhinoException() - { + RhinoException() { Evaluator e = Context.createInterpreter(); - if (e != null) - e.captureStackInfo(this); + if (e != null) e.captureStackInfo(this); } - RhinoException(String details) - { + RhinoException(String details) { super(details); Evaluator e = Context.createInterpreter(); - if (e != null) - e.captureStackInfo(this); + if (e != null) e.captureStackInfo(this); } @Override - public final String getMessage() - { + public final String getMessage() { String details = details(); if (sourceName == null || lineNumber <= 0) { return details; @@ -56,108 +47,85 @@ public final String getMessage() return buf.toString(); } - public String details() - { + public String details() { return super.getMessage(); } /** - * Get the uri of the script source containing the error, or null - * if that information is not available. + * Get the uri of the script source containing the error, or null if that information is not + * available. */ - public final String sourceName() - { + public final String sourceName() { return sourceName; } /** * Initialize the uri of the script source containing the error. * - * @param sourceName the uri of the script source responsible for the error. - * It should not be null. - * + * @param sourceName the uri of the script source responsible for the error. It should not be + * null. * @throws IllegalStateException if the method is called more then once. */ - public final void initSourceName(String sourceName) - { + public final void initSourceName(String sourceName) { if (sourceName == null) throw new IllegalArgumentException(); if (this.sourceName != null) throw new IllegalStateException(); this.sourceName = sourceName; } - /** - * Returns the line number of the statement causing the error, - * or zero if not available. - */ - public final int lineNumber() - { + /** Returns the line number of the statement causing the error, or zero if not available. */ + public final int lineNumber() { return lineNumber; } /** * Initialize the line number of the script statement causing the error. * - * @param lineNumber the line number in the script source. - * It should be positive number. - * + * @param lineNumber the line number in the script source. It should be positive number. * @throws IllegalStateException if the method is called more then once. */ - public final void initLineNumber(int lineNumber) - { + public final void initLineNumber(int lineNumber) { if (lineNumber <= 0) throw new IllegalArgumentException(String.valueOf(lineNumber)); if (this.lineNumber > 0) throw new IllegalStateException(); this.lineNumber = lineNumber; } - /** - * The column number of the location of the error, or zero if unknown. - */ - public final int columnNumber() - { + /** The column number of the location of the error, or zero if unknown. */ + public final int columnNumber() { return columnNumber; } /** * Initialize the column number of the script statement causing the error. * - * @param columnNumber the column number in the script source. - * It should be positive number. - * + * @param columnNumber the column number in the script source. It should be positive number. * @throws IllegalStateException if the method is called more then once. */ - public final void initColumnNumber(int columnNumber) - { + public final void initColumnNumber(int columnNumber) { if (columnNumber <= 0) throw new IllegalArgumentException(String.valueOf(columnNumber)); if (this.columnNumber > 0) throw new IllegalStateException(); this.columnNumber = columnNumber; } - /** - * The source text of the line causing the error, or null if unknown. - */ - public final String lineSource() - { + /** The source text of the line causing the error, or null if unknown. */ + public final String lineSource() { return lineSource; } /** * Initialize the text of the source line containing the error. * - * @param lineSource the text of the source line responsible for the error. - * It should not be null. - * + * @param lineSource the text of the source line responsible for the error. It should not be + * null. * @throws IllegalStateException if the method is called more then once. */ - public final void initLineSource(String lineSource) - { + public final void initLineSource(String lineSource) { if (lineSource == null) throw new IllegalArgumentException(); if (this.lineSource != null) throw new IllegalStateException(); this.lineSource = lineSource; } - final void recordErrorOrigin(String sourceName, int lineNumber, - String lineSource, int columnNumber) - { + final void recordErrorOrigin( + String sourceName, int lineNumber, String lineSource, int columnNumber) { // XXX: for compatibility allow for now -1 to mean 0 if (lineNumber == -1) { lineNumber = 0; @@ -177,38 +145,33 @@ final void recordErrorOrigin(String sourceName, int lineNumber, } } - private String generateStackTrace() - { + private String generateStackTrace() { // Get stable reference to work properly with concurrent access CharArrayWriter writer = new CharArrayWriter(); super.printStackTrace(new PrintWriter(writer)); String origStackTrace = writer.toString(); Evaluator e = Context.createInterpreter(); - if (e != null) - return e.getPatchedStack(this, origStackTrace); + if (e != null) return e.getPatchedStack(this, origStackTrace); return null; } /** - * Get a string representing the script stack of this exception. - * If optimization is enabled, this includes java stack elements - * whose source and method names suggest they have been generated - * by the Rhino script compiler. + * Get a string representing the script stack of this exception. If optimization is enabled, + * this includes java stack elements whose source and method names suggest they have been + * generated by the Rhino script compiler. + * * @return a script stack dump * @since 1.6R6 */ - public String getScriptStackTrace() - { + public String getScriptStackTrace() { return getScriptStackTrace(NativeError.DEFAULT_STACK_LIMIT, null); } /** - * Get a string representing the script stack of this exception. - * If optimization is enabled, this includes java stack elements - * whose source and method names suggest they have been generated - * by the Rhino script compiler. - * The optional "limit" parameter limits the number of stack frames returned. - * The "functionName" parameter will exclude any stack frames "below" the + * Get a string representing the script stack of this exception. If optimization is enabled, + * this includes java stack elements whose source and method names suggest they have been + * generated by the Rhino script compiler. The optional "limit" parameter limits the number of + * stack frames returned. The "functionName" parameter will exclude any stack frames "below" the * specified function on the stack. * * @param limit the number of stack frames returned @@ -216,14 +179,12 @@ public String getScriptStackTrace() * @return a script stack dump * @since 1.8.0 */ - public String getScriptStackTrace(int limit, String functionName) - { + public String getScriptStackTrace(int limit, String functionName) { ScriptStackElement[] stack = getScriptStack(limit, functionName); return formatStackTrace(stack, details()); } - static String formatStackTrace(ScriptStackElement[] stack, String message) - { + static String formatStackTrace(ScriptStackElement[] stack, String message) { StringBuilder buffer = new StringBuilder(); String lineSeparator = SecurityUtilities.getSystemProperty("line.separator"); @@ -235,42 +196,46 @@ static String formatStackTrace(ScriptStackElement[] stack, String message) for (ScriptStackElement elem : stack) { switch (stackStyle) { - case MOZILLA: - elem.renderMozillaStyle(buffer); - break; - case V8: - elem.renderV8Style(buffer); - break; - case RHINO: - elem.renderJavaStyle(buffer); - break; + case MOZILLA: + elem.renderMozillaStyle(buffer); + buffer.append(lineSeparator); + break; + case MOZILLA_LF: + elem.renderMozillaStyle(buffer); + buffer.append('\n'); + break; + case V8: + elem.renderV8Style(buffer); + buffer.append(lineSeparator); + break; + case RHINO: + elem.renderJavaStyle(buffer); + buffer.append(lineSeparator); + break; } - buffer.append(lineSeparator); } return buffer.toString(); } /** * Get a string representing the script stack of this exception. - * @deprecated the filter argument is ignored as we are able to - * recognize script stack elements by our own. Use - * #getScriptStackTrace() instead. + * + * @deprecated the filter argument is ignored as we are able to recognize script stack elements + * by our own. Use #getScriptStackTrace() instead. * @param filter ignored * @return a script stack dump * @since 1.6R6 */ @Deprecated - public String getScriptStackTrace(FilenameFilter filter) - { + public String getScriptStackTrace(FilenameFilter filter) { return getScriptStackTrace(); } /** - * Get the script stack of this exception as an array of - * {@link ScriptStackElement}s. - * If optimization is enabled, this includes java stack elements - * whose source and method names suggest they have been generated - * by the Rhino script compiler. + * Get the script stack of this exception as an array of {@link ScriptStackElement}s. If + * optimization is enabled, this includes java stack elements whose source and method names + * suggest they have been generated by the Rhino script compiler. + * * @return the script stack for this exception * @since 1.7R3 */ @@ -279,14 +244,13 @@ public ScriptStackElement[] getScriptStack() { } /** - * Get the script stack of this exception as an array of - * {@link ScriptStackElement}s. - * If optimization is enabled, this includes java stack elements - * whose source and method names suggest they have been generated - * by the Rhino script compiler. + * Get the script stack of this exception as an array of {@link ScriptStackElement}s. If + * optimization is enabled, this includes java stack elements whose source and method names + * suggest they have been generated by the Rhino script compiler. * * @param limit the number of stack frames returned, or -1 for unlimited - * @param hideFunction the name of a function on the stack -- frames below it will be ignored, or null + * @param hideFunction the name of a function on the stack -- frames below it will be ignored, + * or null * @return the script stack for this exception * @since 1.8.0 */ @@ -317,8 +281,8 @@ public ScriptStackElement[] getScriptStack(int limit, String hideFunction) { Matcher match = JAVA_STACK_PATTERN.matcher(methodName); // the method representing the main script is always "_c_script_0" - // at least we hope so - methodName = !"_c_script_0".equals(methodName) && match.find() ? - match.group(1) : null; + methodName = + !"_c_script_0".equals(methodName) && match.find() ? match.group(1) : null; if (!printStarted && hideFunction.equals(methodName)) { printStarted = true; @@ -345,10 +309,8 @@ public ScriptStackElement[] getScriptStack(int limit, String hideFunction) { return list.toArray(new ScriptStackElement[list.size()]); } - @Override - public void printStackTrace(PrintWriter s) - { + public void printStackTrace(PrintWriter s) { if (interpreterStackInfo == null) { super.printStackTrace(s); } else { @@ -357,8 +319,7 @@ public void printStackTrace(PrintWriter s) } @Override - public void printStackTrace(PrintStream s) - { + public void printStackTrace(PrintStream s) { if (interpreterStackInfo == null) { super.printStackTrace(s); } else { @@ -367,11 +328,10 @@ public void printStackTrace(PrintStream s) } /** - * Returns true if subclasses of RhinoException - * use the Mozilla/Firefox style of rendering script stacks - * (functionName()@fileName:lineNumber) - * instead of Rhino's own Java-inspired format - * ( at fileName:lineNumber (functionName)). + * Returns true if subclasses of RhinoException use the Mozilla/Firefox style of + * rendering script stacks (functionName()@fileName:lineNumber) instead of Rhino's + * own Java-inspired format ( at fileName:lineNumber (functionName)). + * * @return true if stack is rendered in Mozilla/Firefox style * @see ScriptStackElement * @since 1.7R3 @@ -381,12 +341,11 @@ public static boolean usesMozillaStackStyle() { } /** - * Tell subclasses of RhinoException whether to - * use the Mozilla/Firefox style of rendering script stacks - * (functionName()@fileName:lineNumber) - * instead of Rhino's own Java-inspired format - * ( at fileName:lineNumber (functionName)). - * Use "setStackStyle" to select between more than just the "Mozilla" and "Rhino" formats. + * Tell subclasses of RhinoException whether to use the Mozilla/Firefox style of + * rendering script stacks (functionName()@fileName:lineNumber) instead of Rhino's + * own Java-inspired format ( at fileName:lineNumber (functionName)). Use + * "setStackStyle" to select between more than just the "Mozilla" and "Rhino" formats. + * * @param flag whether to render stacks in Mozilla/Firefox style * @see ScriptStackElement * @since 1.7R3 @@ -398,6 +357,7 @@ public static void useMozillaStackStyle(boolean flag) { /** * Specify the stack style to use from between three different formats: "Rhino" (the default), * "Mozilla", and "V8." See StackStyle for information about each. + * * @param style the style to select -- an instance of the StackStyle class * @see StackStyle * @since 1.8.0 @@ -406,10 +366,7 @@ public static void setStackStyle(StackStyle style) { stackStyle = style; } - /** - * Return the current stack style in use. - * Return the current stack style in use. - */ + /** Return the current stack style in use. Return the current stack style in use. */ public static StackStyle getStackStyle() { return stackStyle; } diff --git a/src/org/mozilla/javascript/StackStyle.java b/src/org/mozilla/javascript/StackStyle.java index 911f4a83e1..1805e9a563 100644 --- a/src/org/mozilla/javascript/StackStyle.java +++ b/src/org/mozilla/javascript/StackStyle.java @@ -4,27 +4,29 @@ package org.mozilla.javascript; -/** - * This class distinguishes between the three different supported stack trace formats. - */ - +/** This class distinguishes between the three different supported stack trace formats. */ public enum StackStyle { /** - * This is the default stack trace style in Rhino, which is like Java: - * at fileName:lineNumber (functionName) + * This is the default stack trace style in Rhino, which is like Java: + * at fileName:lineNumber (functionName) */ RHINO, /** - * This stack trace style comes from the old Mozilla code: - * functionName()@fileName:lineNumber + * This stack trace style comes from the old Mozilla code: + * functionName()@fileName:lineNumber */ MOZILLA, /** - * This stack trace style matches that output from V8, either: - * at functionName (fileName:lineNumber:columnNumber) - * or, for anonymous functions: + * This is the same as MOZILLA but uses LF as speparator instead of the system dependent line + * separator. + */ + MOZILLA_LF, + + /** + * This stack trace style matches that output from V8, either: + * at functionName (fileName:lineNumber:columnNumber) or, for anonymous functions: * at fileName:lineNumber:columnNumber */ V8 diff --git a/testsrc/jstests/extensions/stack-traces-mozilla-lf.js b/testsrc/jstests/extensions/stack-traces-mozilla-lf.js new file mode 100644 index 0000000000..443f22ddb4 --- /dev/null +++ b/testsrc/jstests/extensions/stack-traces-mozilla-lf.js @@ -0,0 +1,230 @@ +load("testsrc/assert.js"); + +function nestedThrower(msg) { + throw new Error(msg); +} +function parentThrower(msg) { + nestedThrower(msg); +} +function grandparentThrower(msg) { + parentThrower(msg); +} +function ObjectThrower(msg) { + nestedThrower(msg); +} +function nestedCapture(o, f) { + Error.captureStackTrace(o, f); +} +function parentCapture(o, f) { + nestedCapture(o, f); +} +function grandParentCapture(o, f) { + parentCapture(o, f); +} +function countLines(msg) { + if (!msg) { + return 0; + } + // Subtract one for the newline at the end + return msg.split('\n').length - 1; +} + +// Test that toString contains the error but not the stack +// and test that the stack contains the file name +try { + throw new Error('Test 1'); +} catch (e) { + assertFalse(e.stack == undefined); + assertTrue(/Test 1/.test(e.toString())); + assertFalse(/stack-traces-mozilla-lf.js/.test(e.toString())); + assertFalse(/Test 1/.test(e.stack)); + assertTrue(/stack-traces-mozilla-lf.js/.test(e.stack)); +} + +// Assert that the function name is nested inside a nested stack trace +try { + nestedThrower('Nested 1'); +} catch (e) { + assertFalse(/Nested 1/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); +} + +// Do the same for a second level of nesting +try { + parentThrower('Nested 2'); +} catch (e) { + assertFalse(/Nested 2/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/parentThrower/.test(e.stack)); +} + +// Do the same for a constructor +try { + new ObjectThrower('Nested 3'); +} catch (e) { + assertFalse(/Nested 3/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/ObjectThrower/.test(e.stack)); +} + +// Count stack lines before and after changing limit +try { + grandparentThrower('Count 1'); +} catch (e) { + assertTrue(countLines(e.stack) >= 3); +} + +assertTrue(Error.stackTraceLimit != undefined); +Error.stackTraceLimit = 2; +assertEquals(2, Error.stackTraceLimit); + +try { + grandparentThrower('Count 2'); +} catch (e) { + assertEquals(2, countLines(e.stack)); +} + +Error.stackTraceLimit = 0; +assertEquals(0, Error.stackTraceLimit); + +try { + grandparentThrower('Count 3'); +} catch (e) { + assertEquals(0, countLines(e.stack)); +} + +Error.stackTraceLimit = Infinity; +assertEquals(Infinity, Error.stackTraceLimit); + +try { + grandparentThrower('Count 1'); +} catch (e) { + assertTrue(countLines(e.stack) >= 3); +} + +// Test captureStackTrace + +var o = {}; +grandParentCapture(o); +assertTrue(/nestedCapture/.test(o.stack)); +assertTrue(/parentCapture/.test(o.stack)); +assertTrue(/grandParentCapture/.test(o.stack)); + +// Put in a function to be hidden from the stack + +var m = {}; +grandParentCapture(m, parentCapture); +assertTrue(/grandParentCapture/.test(m.stack)); +assertFalse(/parentCapture/.test(m.stack)); +assertFalse(/nestedCapture/.test(m.stack)); + +// Put in a function not in the stack + +var n = {}; +grandParentCapture(n, print); +assertFalse(/nestedCapture/.test(n.stack)); +assertFalse(/parentCapture/.test(n.stack)); +assertFalse(/grandParentCapture/.test(n.stack)); + +// Test prepareStackTrace + +assertEquals(undefined, Error.prepareStackTrace); + +var prepareCalled = false; + +function diagnoseStack(err, stack) { + var s = ''; + prepareCalled = true; + stack.forEach(function (e) { + assertEquals('object', typeof e); + s += e.getFunctionName() + ' '; + s += e.getMethodName() + ' '; + s += e.getFileName() + ' '; + s += e.getLineNumber() + ' '; + s += '\n'; + }); + return s; +} +Error.prepareStackTrace = diagnoseStack; + +var s1 = {}; +grandParentCapture(s1); +assertTrue(/nestedCapture/.test(s1.stack)); +assertTrue(/parentCapture/.test(s1.stack)); +assertTrue(/grandParentCapture/.test(s1.stack)); +assertTrue(prepareCalled); + +// Test that it is limited by the function flag as above + +var s2 = {}; +grandParentCapture(s2, parentCapture); +assertFalse(/nestedCapture/.test(s2.stack)); +assertFalse(/parentCapture/.test(s2.stack)); +assertTrue(/grandParentCapture/.test(s2.stack)); + +// Test that it all works on a throw. Note that the error text isn't included. + +try { + grandparentThrower('Custom 1'); +} catch (e) { + assertFalse(/Custom 1/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/parentThrower/.test(e.stack)); + assertTrue(/grandparentThrower/.test(e.stack)); +} + +// And test that it works the old way when we format it back +Error.prepareStackTrace = undefined; + +try { + grandparentThrower('Custom 2'); +} catch (e) { + assertFalse(/Custom 2/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/parentThrower/.test(e.stack)); + assertTrue(/grandparentThrower/.test(e.stack)); +} + +// test that all the functions on a stack frame work + +function printFrame(l, f) { + var o = + {typeofThis: typeof f.getThis(), + typeName: f.getTypeName(), + function: f.getFunction(), + functionName: f.getFunctionName(), + methodName: f.getMethodName(), + fileName: f.getFileName(), + lineNumber: f.getLineNumber(), + columnNumber: f.getColumnNumber(), + evalOrigin: f.getEvalOrigin(), + topLevel: f.isToplevel(), + eval: f.isEval(), + native: f.isNative(), + constructor: f.isConstructor() + }; + + l.push(o); + return l; +} + +Error.prepareStackTrace = function (e, frames) { + return frames.reduce(printFrame, []); +}; + +try { + grandparentThrower('testing stack'); +} catch (e) { + e.stack.forEach(function (f) { + verifyFrame(f); + }); +} + +function verifyFrame(f) { + assertEquals(typeof f.fileName, 'string'); + assertEquals(typeof f.lineNumber, 'number'); + assertEquals(typeof f.topLevel, 'boolean'); + assertEquals(typeof f.eval, 'boolean'); + assertEquals(typeof f.native, 'boolean'); + assertEquals(typeof f.constructor, 'boolean'); +} diff --git a/testsrc/jstests/extensions/stack-traces-mozilla.js b/testsrc/jstests/extensions/stack-traces-mozilla.js new file mode 100644 index 0000000000..fa27d2bfac --- /dev/null +++ b/testsrc/jstests/extensions/stack-traces-mozilla.js @@ -0,0 +1,230 @@ +load("testsrc/assert.js"); + +function nestedThrower(msg) { + throw new Error(msg); +} +function parentThrower(msg) { + nestedThrower(msg); +} +function grandparentThrower(msg) { + parentThrower(msg); +} +function ObjectThrower(msg) { + nestedThrower(msg); +} +function nestedCapture(o, f) { + Error.captureStackTrace(o, f); +} +function parentCapture(o, f) { + nestedCapture(o, f); +} +function grandParentCapture(o, f) { + parentCapture(o, f); +} +function countLines(msg) { + if (!msg) { + return 0; + } + // Subtract one for the newline at the end + return msg.split('\n').length - 1; +} + +// Test that toString contains the error but not the stack +// and test that the stack contains the file name +try { + throw new Error('Test 1'); +} catch (e) { + assertFalse(e.stack == undefined); + assertTrue(/Test 1/.test(e.toString())); + assertFalse(/stack-traces-mozilla.js/.test(e.toString())); + assertFalse(/Test 1/.test(e.stack)); + assertTrue(/stack-traces-mozilla.js/.test(e.stack)); +} + +// Assert that the function name is nested inside a nested stack trace +try { + nestedThrower('Nested 1'); +} catch (e) { + assertFalse(/Nested 1/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); +} + +// Do the same for a second level of nesting +try { + parentThrower('Nested 2'); +} catch (e) { + assertFalse(/Nested 2/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/parentThrower/.test(e.stack)); +} + +// Do the same for a constructor +try { + new ObjectThrower('Nested 3'); +} catch (e) { + assertFalse(/Nested 3/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/ObjectThrower/.test(e.stack)); +} + +// Count stack lines before and after changing limit +try { + grandparentThrower('Count 1'); +} catch (e) { + assertTrue(countLines(e.stack) >= 3); +} + +assertTrue(Error.stackTraceLimit != undefined); +Error.stackTraceLimit = 2; +assertEquals(2, Error.stackTraceLimit); + +try { + grandparentThrower('Count 2'); +} catch (e) { + assertEquals(2, countLines(e.stack)); +} + +Error.stackTraceLimit = 0; +assertEquals(0, Error.stackTraceLimit); + +try { + grandparentThrower('Count 3'); +} catch (e) { + assertEquals(0, countLines(e.stack)); +} + +Error.stackTraceLimit = Infinity; +assertEquals(Infinity, Error.stackTraceLimit); + +try { + grandparentThrower('Count 1'); +} catch (e) { + assertTrue(countLines(e.stack) >= 3); +} + +// Test captureStackTrace + +var o = {}; +grandParentCapture(o); +assertTrue(/nestedCapture/.test(o.stack)); +assertTrue(/parentCapture/.test(o.stack)); +assertTrue(/grandParentCapture/.test(o.stack)); + +// Put in a function to be hidden from the stack + +var m = {}; +grandParentCapture(m, parentCapture); +assertTrue(/grandParentCapture/.test(m.stack)); +assertFalse(/parentCapture/.test(m.stack)); +assertFalse(/nestedCapture/.test(m.stack)); + +// Put in a function not in the stack + +var n = {}; +grandParentCapture(n, print); +assertFalse(/nestedCapture/.test(n.stack)); +assertFalse(/parentCapture/.test(n.stack)); +assertFalse(/grandParentCapture/.test(n.stack)); + +// Test prepareStackTrace + +assertEquals(undefined, Error.prepareStackTrace); + +var prepareCalled = false; + +function diagnoseStack(err, stack) { + var s = ''; + prepareCalled = true; + stack.forEach(function (e) { + assertEquals('object', typeof e); + s += e.getFunctionName() + ' '; + s += e.getMethodName() + ' '; + s += e.getFileName() + ' '; + s += e.getLineNumber() + ' '; + s += '\n'; + }); + return s; +} +Error.prepareStackTrace = diagnoseStack; + +var s1 = {}; +grandParentCapture(s1); +assertTrue(/nestedCapture/.test(s1.stack)); +assertTrue(/parentCapture/.test(s1.stack)); +assertTrue(/grandParentCapture/.test(s1.stack)); +assertTrue(prepareCalled); + +// Test that it is limited by the function flag as above + +var s2 = {}; +grandParentCapture(s2, parentCapture); +assertFalse(/nestedCapture/.test(s2.stack)); +assertFalse(/parentCapture/.test(s2.stack)); +assertTrue(/grandParentCapture/.test(s2.stack)); + +// Test that it all works on a throw. Note that the error text isn't included. + +try { + grandparentThrower('Custom 1'); +} catch (e) { + assertFalse(/Custom 1/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/parentThrower/.test(e.stack)); + assertTrue(/grandparentThrower/.test(e.stack)); +} + +// And test that it works the old way when we format it back +Error.prepareStackTrace = undefined; + +try { + grandparentThrower('Custom 2'); +} catch (e) { + assertFalse(/Custom 2/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/parentThrower/.test(e.stack)); + assertTrue(/grandparentThrower/.test(e.stack)); +} + +// test that all the functions on a stack frame work + +function printFrame(l, f) { + var o = + {typeofThis: typeof f.getThis(), + typeName: f.getTypeName(), + function: f.getFunction(), + functionName: f.getFunctionName(), + methodName: f.getMethodName(), + fileName: f.getFileName(), + lineNumber: f.getLineNumber(), + columnNumber: f.getColumnNumber(), + evalOrigin: f.getEvalOrigin(), + topLevel: f.isToplevel(), + eval: f.isEval(), + native: f.isNative(), + constructor: f.isConstructor() + }; + + l.push(o); + return l; +} + +Error.prepareStackTrace = function (e, frames) { + return frames.reduce(printFrame, []); +}; + +try { + grandparentThrower('testing stack'); +} catch (e) { + e.stack.forEach(function (f) { + verifyFrame(f); + }); +} + +function verifyFrame(f) { + assertEquals(typeof f.fileName, 'string'); + assertEquals(typeof f.lineNumber, 'number'); + assertEquals(typeof f.topLevel, 'boolean'); + assertEquals(typeof f.eval, 'boolean'); + assertEquals(typeof f.native, 'boolean'); + assertEquals(typeof f.constructor, 'boolean'); +} diff --git a/testsrc/jstests/extensions/stack-traces-rhino.js b/testsrc/jstests/extensions/stack-traces-rhino.js new file mode 100644 index 0000000000..a1b218b822 --- /dev/null +++ b/testsrc/jstests/extensions/stack-traces-rhino.js @@ -0,0 +1,230 @@ +load("testsrc/assert.js"); + +function nestedThrower(msg) { + throw new Error(msg); +} +function parentThrower(msg) { + nestedThrower(msg); +} +function grandparentThrower(msg) { + parentThrower(msg); +} +function ObjectThrower(msg) { + nestedThrower(msg); +} +function nestedCapture(o, f) { + Error.captureStackTrace(o, f); +} +function parentCapture(o, f) { + nestedCapture(o, f); +} +function grandParentCapture(o, f) { + parentCapture(o, f); +} +function countLines(msg) { + if (!msg) { + return 0; + } + // Subtract one for the newline at the end + return msg.split('\n').length - 1; +} + +// Test that toString contains the error but not the stack +// and test that the stack contains the file name +try { + throw new Error('Test 1'); +} catch (e) { + assertFalse(e.stack == undefined); + assertTrue(/Test 1/.test(e.toString())); + assertFalse(/stack-traces-rhino.js/.test(e.toString())); + assertFalse(/Test 1/.test(e.stack)); + assertTrue(/stack-traces-rhino.js/.test(e.stack)); +} + +// Assert that the function name is nested inside a nested stack trace +try { + nestedThrower('Nested 1'); +} catch (e) { + assertFalse(/Nested 1/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); +} + +// Do the same for a second level of nesting +try { + parentThrower('Nested 2'); +} catch (e) { + assertFalse(/Nested 2/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/parentThrower/.test(e.stack)); +} + +// Do the same for a constructor +try { + new ObjectThrower('Nested 3'); +} catch (e) { + assertFalse(/Nested 3/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/ObjectThrower/.test(e.stack)); +} + +// Count stack lines before and after changing limit +try { + grandparentThrower('Count 1'); +} catch (e) { + assertTrue(countLines(e.stack) >= 3); +} + +assertTrue(Error.stackTraceLimit != undefined); +Error.stackTraceLimit = 2; +assertEquals(2, Error.stackTraceLimit); + +try { + grandparentThrower('Count 2'); +} catch (e) { + assertEquals(2, countLines(e.stack)); +} + +Error.stackTraceLimit = 0; +assertEquals(0, Error.stackTraceLimit); + +try { + grandparentThrower('Count 3'); +} catch (e) { + assertEquals(0, countLines(e.stack)); +} + +Error.stackTraceLimit = Infinity; +assertEquals(Infinity, Error.stackTraceLimit); + +try { + grandparentThrower('Count 1'); +} catch (e) { + assertTrue(countLines(e.stack) >= 3); +} + +// Test captureStackTrace + +var o = {}; +grandParentCapture(o); +assertTrue(/nestedCapture/.test(o.stack)); +assertTrue(/parentCapture/.test(o.stack)); +assertTrue(/grandParentCapture/.test(o.stack)); + +// Put in a function to be hidden from the stack + +var m = {}; +grandParentCapture(m, parentCapture); +assertTrue(/grandParentCapture/.test(m.stack)); +assertFalse(/parentCapture/.test(m.stack)); +assertFalse(/nestedCapture/.test(m.stack)); + +// Put in a function not in the stack + +var n = {}; +grandParentCapture(n, print); +assertFalse(/nestedCapture/.test(n.stack)); +assertFalse(/parentCapture/.test(n.stack)); +assertFalse(/grandParentCapture/.test(n.stack)); + +// Test prepareStackTrace + +assertEquals(undefined, Error.prepareStackTrace); + +var prepareCalled = false; + +function diagnoseStack(err, stack) { + var s = ''; + prepareCalled = true; + stack.forEach(function (e) { + assertEquals('object', typeof e); + s += e.getFunctionName() + ' '; + s += e.getMethodName() + ' '; + s += e.getFileName() + ' '; + s += e.getLineNumber() + ' '; + s += '\n'; + }); + return s; +} +Error.prepareStackTrace = diagnoseStack; + +var s1 = {}; +grandParentCapture(s1); +assertTrue(/nestedCapture/.test(s1.stack)); +assertTrue(/parentCapture/.test(s1.stack)); +assertTrue(/grandParentCapture/.test(s1.stack)); +assertTrue(prepareCalled); + +// Test that it is limited by the function flag as above + +var s2 = {}; +grandParentCapture(s2, parentCapture); +assertFalse(/nestedCapture/.test(s2.stack)); +assertFalse(/parentCapture/.test(s2.stack)); +assertTrue(/grandParentCapture/.test(s2.stack)); + +// Test that it all works on a throw. Note that the error text isn't included. + +try { + grandparentThrower('Custom 1'); +} catch (e) { + assertFalse(/Custom 1/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/parentThrower/.test(e.stack)); + assertTrue(/grandparentThrower/.test(e.stack)); +} + +// And test that it works the old way when we format it back +Error.prepareStackTrace = undefined; + +try { + grandparentThrower('Custom 2'); +} catch (e) { + assertFalse(/Custom 2/.test(e.stack)); + assertTrue(/nestedThrower/.test(e.stack)); + assertTrue(/parentThrower/.test(e.stack)); + assertTrue(/grandparentThrower/.test(e.stack)); +} + +// test that all the functions on a stack frame work + +function printFrame(l, f) { + var o = + {typeofThis: typeof f.getThis(), + typeName: f.getTypeName(), + function: f.getFunction(), + functionName: f.getFunctionName(), + methodName: f.getMethodName(), + fileName: f.getFileName(), + lineNumber: f.getLineNumber(), + columnNumber: f.getColumnNumber(), + evalOrigin: f.getEvalOrigin(), + topLevel: f.isToplevel(), + eval: f.isEval(), + native: f.isNative(), + constructor: f.isConstructor() + }; + + l.push(o); + return l; +} + +Error.prepareStackTrace = function (e, frames) { + return frames.reduce(printFrame, []); +}; + +try { + grandparentThrower('testing stack'); +} catch (e) { + e.stack.forEach(function (f) { + verifyFrame(f); + }); +} + +function verifyFrame(f) { + assertEquals(typeof f.fileName, 'string'); + assertEquals(typeof f.lineNumber, 'number'); + assertEquals(typeof f.topLevel, 'boolean'); + assertEquals(typeof f.eval, 'boolean'); + assertEquals(typeof f.native, 'boolean'); + assertEquals(typeof f.constructor, 'boolean'); +} diff --git a/testsrc/jstests/extensions/stack-traces.js b/testsrc/jstests/extensions/stack-traces-v8.js similarity index 95% rename from testsrc/jstests/extensions/stack-traces.js rename to testsrc/jstests/extensions/stack-traces-v8.js index f99602fab5..136cfd8a5e 100644 --- a/testsrc/jstests/extensions/stack-traces.js +++ b/testsrc/jstests/extensions/stack-traces-v8.js @@ -36,9 +36,9 @@ try { } catch (e) { assertFalse(e.stack == undefined); assertTrue(/Test 1/.test(e.toString())); - assertFalse(/stack-traces.js/.test(e.toString())); + assertFalse(/stack-traces-v8.js/.test(e.toString())); assertTrue(/Test 1/.test(e.stack)); - assertTrue(/stack-traces.js/.test(e.stack)); + assertTrue(/stack-traces-v8.js/.test(e.stack)); } // Assert that the function name is nested inside a nested stack trace @@ -167,8 +167,10 @@ assertTrue(/grandParentCapture/.test(s2.stack)); try { grandparentThrower('Custom 1'); } catch (e) { + assertFalse(/Custom 1/.test(e.stack)); assertTrue(/nestedThrower/.test(e.stack)); assertTrue(/parentThrower/.test(e.stack)); + assertTrue(/grandparentThrower/.test(e.stack)); } // And test that it works the old way when we format it back @@ -180,6 +182,7 @@ try { assertTrue(/Custom 2/.test(e.stack)); assertTrue(/nestedThrower/.test(e.stack)); assertTrue(/parentThrower/.test(e.stack)); + assertTrue(/grandparentThrower/.test(e.stack)); } // test that all the functions on a stack frame work diff --git a/testsrc/org/mozilla/javascript/tests/StackTraceExtensionMozillaLfTest.java b/testsrc/org/mozilla/javascript/tests/StackTraceExtensionMozillaLfTest.java new file mode 100644 index 0000000000..b6c6aae3cc --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/StackTraceExtensionMozillaLfTest.java @@ -0,0 +1,80 @@ +package org.mozilla.javascript.tests; + +import static org.junit.Assert.assertFalse; + +import java.io.FileReader; +import java.io.IOException; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.RhinoException; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.StackStyle; +import org.mozilla.javascript.tools.shell.Global; + +public class StackTraceExtensionMozillaLfTest { + @BeforeClass + public static void init() { + RhinoException.setStackStyle(StackStyle.MOZILLA); + } + + @AfterClass + public static void terminate() { + RhinoException.setStackStyle(StackStyle.RHINO); + } + + private void testTraces(int opt) { + final ContextFactory factory = + new ContextFactory() { + @Override + protected boolean hasFeature(Context cx, int featureIndex) { + switch (featureIndex) { + case Context.FEATURE_LOCATION_INFORMATION_IN_ERROR: + return true; + default: + return super.hasFeature(cx, featureIndex); + } + } + }; + + Context cx = factory.enterContext(); + try { + cx.setLanguageVersion(Context.VERSION_1_8); + cx.setOptimizationLevel(opt); + cx.setGeneratingDebug(true); + + Global global = new Global(cx); + Scriptable root = cx.newObject(global); + + FileReader rdr = + new FileReader("testsrc/jstests/extensions/stack-traces-mozilla-lf.js"); + + try { + cx.evaluateReader(root, rdr, "stack-traces-mozilla-lf.js", 1, null); + } finally { + rdr.close(); + } + } catch (IOException ioe) { + assertFalse("I/O Error: " + ioe, true); + } finally { + Context.exit(); + } + } + + @Test + public void testStackTrace0() { + testTraces(0); + } + + @Test + public void testStackTrace9() { + testTraces(9); + } + + @Test + public void testStackTraceInt() { + testTraces(-1); + } +} diff --git a/testsrc/org/mozilla/javascript/tests/StackTraceExtensionMozillaTest.java b/testsrc/org/mozilla/javascript/tests/StackTraceExtensionMozillaTest.java new file mode 100644 index 0000000000..f4afb6724d --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/StackTraceExtensionMozillaTest.java @@ -0,0 +1,79 @@ +package org.mozilla.javascript.tests; + +import static org.junit.Assert.assertFalse; + +import java.io.FileReader; +import java.io.IOException; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.RhinoException; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.StackStyle; +import org.mozilla.javascript.tools.shell.Global; + +public class StackTraceExtensionMozillaTest { + @BeforeClass + public static void init() { + RhinoException.setStackStyle(StackStyle.MOZILLA); + } + + @AfterClass + public static void terminate() { + RhinoException.setStackStyle(StackStyle.RHINO); + } + + private void testTraces(int opt) { + final ContextFactory factory = + new ContextFactory() { + @Override + protected boolean hasFeature(Context cx, int featureIndex) { + switch (featureIndex) { + case Context.FEATURE_LOCATION_INFORMATION_IN_ERROR: + return true; + default: + return super.hasFeature(cx, featureIndex); + } + } + }; + + Context cx = factory.enterContext(); + try { + cx.setLanguageVersion(Context.VERSION_1_8); + cx.setOptimizationLevel(opt); + cx.setGeneratingDebug(true); + + Global global = new Global(cx); + Scriptable root = cx.newObject(global); + + FileReader rdr = new FileReader("testsrc/jstests/extensions/stack-traces-mozilla.js"); + + try { + cx.evaluateReader(root, rdr, "stack-traces-mozilla.js", 1, null); + } finally { + rdr.close(); + } + } catch (IOException ioe) { + assertFalse("I/O Error: " + ioe, true); + } finally { + Context.exit(); + } + } + + @Test + public void testStackTrace0() { + testTraces(0); + } + + @Test + public void testStackTrace9() { + testTraces(9); + } + + @Test + public void testStackTraceInt() { + testTraces(-1); + } +} diff --git a/testsrc/org/mozilla/javascript/tests/StackTraceExtensionRhinoTest.java b/testsrc/org/mozilla/javascript/tests/StackTraceExtensionRhinoTest.java new file mode 100644 index 0000000000..e003fbf2f5 --- /dev/null +++ b/testsrc/org/mozilla/javascript/tests/StackTraceExtensionRhinoTest.java @@ -0,0 +1,65 @@ +package org.mozilla.javascript.tests; + +import static org.junit.Assert.assertFalse; + +import java.io.FileReader; +import java.io.IOException; +import org.junit.Test; +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.tools.shell.Global; + +public class StackTraceExtensionRhinoTest { + private void testTraces(int opt) { + final ContextFactory factory = + new ContextFactory() { + @Override + protected boolean hasFeature(Context cx, int featureIndex) { + switch (featureIndex) { + case Context.FEATURE_LOCATION_INFORMATION_IN_ERROR: + return true; + default: + return super.hasFeature(cx, featureIndex); + } + } + }; + + Context cx = factory.enterContext(); + try { + cx.setLanguageVersion(Context.VERSION_1_8); + cx.setOptimizationLevel(opt); + cx.setGeneratingDebug(true); + + Global global = new Global(cx); + Scriptable root = cx.newObject(global); + + FileReader rdr = new FileReader("testsrc/jstests/extensions/stack-traces-rhino.js"); + + try { + cx.evaluateReader(root, rdr, "stack-traces-rhino.js", 1, null); + } finally { + rdr.close(); + } + } catch (IOException ioe) { + assertFalse("I/O Error: " + ioe, true); + } finally { + Context.exit(); + } + } + + @Test + public void testStackTrace0() { + testTraces(0); + } + + @Test + public void testStackTrace9() { + testTraces(9); + } + + @Test + public void testStackTraceInt() { + testTraces(-1); + } +} diff --git a/testsrc/org/mozilla/javascript/tests/StackTraceExtensionTest.java b/testsrc/org/mozilla/javascript/tests/StackTraceExtensionV8Test.java similarity index 60% rename from testsrc/org/mozilla/javascript/tests/StackTraceExtensionTest.java rename to testsrc/org/mozilla/javascript/tests/StackTraceExtensionV8Test.java index f94303b249..57a6741ac4 100644 --- a/testsrc/org/mozilla/javascript/tests/StackTraceExtensionTest.java +++ b/testsrc/org/mozilla/javascript/tests/StackTraceExtensionV8Test.java @@ -4,7 +4,6 @@ import java.io.FileReader; import java.io.IOException; - import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -15,34 +14,30 @@ import org.mozilla.javascript.StackStyle; import org.mozilla.javascript.tools.shell.Global; -public class StackTraceExtensionTest -{ +public class StackTraceExtensionV8Test { @BeforeClass - public static void init() - { + public static void init() { RhinoException.setStackStyle(StackStyle.V8); } @AfterClass - public static void terminate() - { + public static void terminate() { RhinoException.setStackStyle(StackStyle.RHINO); } - private void testTraces(int opt) - { - final ContextFactory factory = new ContextFactory() { - @Override - protected boolean hasFeature(Context cx, int featureIndex) - { - switch (featureIndex) { - case Context.FEATURE_LOCATION_INFORMATION_IN_ERROR: - return true; - default: - return super.hasFeature(cx, featureIndex); - } - } - }; + private void testTraces(int opt) { + final ContextFactory factory = + new ContextFactory() { + @Override + protected boolean hasFeature(Context cx, int featureIndex) { + switch (featureIndex) { + case Context.FEATURE_LOCATION_INFORMATION_IN_ERROR: + return true; + default: + return super.hasFeature(cx, featureIndex); + } + } + }; Context cx = factory.enterContext(); try { @@ -53,10 +48,10 @@ protected boolean hasFeature(Context cx, int featureIndex) Global global = new Global(cx); Scriptable root = cx.newObject(global); - FileReader rdr = new FileReader("testsrc/jstests/extensions/stack-traces.js"); + FileReader rdr = new FileReader("testsrc/jstests/extensions/stack-traces-v8.js"); try { - cx.evaluateReader(root, rdr, "stack-traces.js", 1, null); + cx.evaluateReader(root, rdr, "stack-traces-v8.js", 1, null); } finally { rdr.close(); } @@ -68,20 +63,17 @@ protected boolean hasFeature(Context cx, int featureIndex) } @Test - public void testStackTrace0() - { + public void testStackTrace0() { testTraces(0); } @Test - public void testStackTrace9() - { + public void testStackTrace9() { testTraces(9); } @Test - public void testStackTraceInt() - { + public void testStackTraceInt() { testTraces(-1); } } diff --git a/testsrc/org/mozilla/javascript/tests/StackTraceTest.java b/testsrc/org/mozilla/javascript/tests/StackTraceTest.java index 358ed96565..b6f0c1220c 100644 --- a/testsrc/org/mozilla/javascript/tests/StackTraceTest.java +++ b/testsrc/org/mozilla/javascript/tests/StackTraceTest.java @@ -2,52 +2,125 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -/** - * - */ +/** */ package org.mozilla.javascript.tests; +import junit.framework.TestCase; import org.mozilla.javascript.JavaScriptException; import org.mozilla.javascript.RhinoException; import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.StackStyle; -import junit.framework.TestCase; - -/** - * @author Marc Guillemot - */ +/** @author Marc Guillemot */ public class StackTraceTest extends TestCase { - final static String LS = System.getProperty("line.separator"); + static final String LS = System.getProperty("line.separator"); + + /** + * As of CVS head on May, 11. 2009, stacktrace information is lost when a call to some native + * function has been made. + */ + public void testFailureStackTraceRhino() { + final StackStyle stackStyle = RhinoException.getStackStyle(); + try { + RhinoException.setStackStyle(StackStyle.RHINO); + + final String source1 = "function f2() { throw 'hello'; }; f2();"; + final String source2 = "function f2() { 'H'.toLowerCase(); throw 'hello'; }; f2();"; + final String source3 = + "function f2() { new java.lang.String('H').toLowerCase(); throw 'hello'; }; f2();"; + final String result = "\tat test.js (f2)" + LS + "\tat test.js" + LS; + + runWithExpectedStackTrace(source1, result); + runWithExpectedStackTrace(source2, result); + runWithExpectedStackTrace(source3, result); + } finally { + RhinoException.setStackStyle(stackStyle); + } + } /** - * As of CVS head on May, 11. 2009, stacktrace information is lost when a call to some - * native function has been made. + * As of CVS head on May, 11. 2009, stacktrace information is lost when a call to some native + * function has been made. */ - public void testFailureStackTrace() { - RhinoException.useMozillaStackStyle(false); - final String source1 = "function f2() { throw 'hello'; }; f2();"; - final String source2 = "function f2() { 'H'.toLowerCase(); throw 'hello'; }; f2();"; - final String source3 = "function f2() { new java.lang.String('H').toLowerCase(); throw 'hello'; }; f2();"; - final String result = "\tat test.js (f2)" + LS + "\tat test.js" + LS; - - runWithExpectedStackTrace(source1, result); - runWithExpectedStackTrace(source2, result); - runWithExpectedStackTrace(source3, result); + public void testFailureStackTraceMozilla() { + final StackStyle stackStyle = RhinoException.getStackStyle(); + try { + RhinoException.setStackStyle(StackStyle.MOZILLA); + + final String source1 = "function f2() { throw 'hello'; }; f2();"; + final String source2 = "function f2() { 'H'.toLowerCase(); throw 'hello'; }; f2();"; + final String source3 = + "function f2() { new java.lang.String('H').toLowerCase(); throw 'hello'; }; f2();"; + final String result = "f2()@test.js" + LS + "@test.js" + LS; + + runWithExpectedStackTrace(source1, result); + runWithExpectedStackTrace(source2, result); + runWithExpectedStackTrace(source3, result); + } finally { + RhinoException.setStackStyle(stackStyle); + } + } + + /** + * As of CVS head on May, 11. 2009, stacktrace information is lost when a call to some native + * function has been made. + */ + public void testFailureStackTraceMozillaLf() { + final StackStyle stackStyle = RhinoException.getStackStyle(); + try { + RhinoException.setStackStyle(StackStyle.MOZILLA_LF); + + final String source1 = "function f2() { throw 'hello'; }; f2();"; + final String source2 = "function f2() { 'H'.toLowerCase(); throw 'hello'; }; f2();"; + final String source3 = + "function f2() { new java.lang.String('H').toLowerCase(); throw 'hello'; }; f2();"; + final String result = "f2()@test.js\n@test.js\n"; + + runWithExpectedStackTrace(source1, result); + runWithExpectedStackTrace(source2, result); + runWithExpectedStackTrace(source3, result); + } finally { + RhinoException.setStackStyle(stackStyle); + } + } + + /** + * As of CVS head on May, 11. 2009, stacktrace information is lost when a call to some native + * function has been made. + */ + public void testFailureStackTraceV8() { + final StackStyle stackStyle = RhinoException.getStackStyle(); + try { + RhinoException.setStackStyle(StackStyle.V8); + + final String source1 = "function f2() { throw 'hello'; }; f2();"; + final String source2 = "function f2() { 'H'.toLowerCase(); throw 'hello'; }; f2();"; + final String source3 = + "function f2() { new java.lang.String('H').toLowerCase(); throw 'hello'; }; f2();"; + final String result = + "hello" + LS + " at f2 (test.js:0:0)" + LS + " at test.js:0:0" + LS; + + runWithExpectedStackTrace(source1, result); + runWithExpectedStackTrace(source2, result); + runWithExpectedStackTrace(source3, result); + } finally { + RhinoException.setStackStyle(stackStyle); + } } - private void runWithExpectedStackTrace(final String _source, final String _expectedStackTrace) - { - Utils.runWithOptimizationLevel(cx -> { - final Scriptable scope = cx.initStandardObjects(); - try { - cx.evaluateString(scope, _source, "test.js", 0, null); - } - catch (final JavaScriptException e) - { - assertEquals(_expectedStackTrace, e.getScriptStackTrace()); - return null; - } - throw new RuntimeException("Exception expected!"); - }, -1); + private static void runWithExpectedStackTrace( + final String _source, final String _expectedStackTrace) { + Utils.runWithOptimizationLevel( + cx -> { + final Scriptable scope = cx.initStandardObjects(); + try { + cx.evaluateString(scope, _source, "test.js", 0, null); + } catch (final JavaScriptException e) { + assertEquals(_expectedStackTrace, e.getScriptStackTrace()); + return null; + } + throw new RuntimeException("Exception expected!"); + }, + -1); } - } +}