Commit cbeb7656 authored by joepeck@webkit.org's avatar joepeck@webkit.org

Web Inspector: [JSC] Caught exception is treated as uncaught

https://bugs.webkit.org/show_bug.cgi?id=93607

Reviewed by Geoff Garen.

Source/JavaScriptCore:

Check up the entire call stack to see if there is an exception handler.

* interpreter/Interpreter.cpp:
(JSC::GetExceptionHandlerFunctor::GetExceptionHandlerFunctor):
(JSC::GetExceptionHandlerFunctor::handler):
(JSC::GetExceptionHandlerFunctor::operator()):

LayoutTests:

Add tests for different inspector pause on exceptions states.

* inspector-protocol/debugger/resources/exception.js: Added.
* inspector-protocol/debugger/setPauseOnExceptions-all-expected.txt: Added.
* inspector-protocol/debugger/setPauseOnExceptions-all.html: Added.
* inspector-protocol/debugger/setPauseOnExceptions-none-expected.txt: Added.
* inspector-protocol/debugger/setPauseOnExceptions-none.html: Added.
* inspector-protocol/debugger/setPauseOnExceptions-uncaught-expected.txt: Added.
* inspector-protocol/debugger/setPauseOnExceptions-uncaught.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@155471 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent de9e47e4
2013-09-10 Joseph Pecoraro <pecoraro@apple.com>
Web Inspector: [JSC] Caught exception is treated as uncaught
https://bugs.webkit.org/show_bug.cgi?id=93607
Reviewed by Geoff Garen.
Add tests for different inspector pause on exceptions states.
* inspector-protocol/debugger/resources/exception.js: Added.
* inspector-protocol/debugger/setPauseOnExceptions-all-expected.txt: Added.
* inspector-protocol/debugger/setPauseOnExceptions-all.html: Added.
* inspector-protocol/debugger/setPauseOnExceptions-none-expected.txt: Added.
* inspector-protocol/debugger/setPauseOnExceptions-none.html: Added.
* inspector-protocol/debugger/setPauseOnExceptions-uncaught-expected.txt: Added.
* inspector-protocol/debugger/setPauseOnExceptions-uncaught.html: Added.
2013-09-10 Grzegorz Czajkowski <g.czajkowski@samsung.com>
needsFocus is always undefined in grammar-paste.html
function exceptionBasic()
{
({}).a.b.c.d;
}
function exceptionDOM()
{
document.body.appendChild(0);
}
function throwString()
{
throw "exception string";
}
function throwParam(o)
{
throw o;
}
function exceptionInHostFunction()
{
[1].map(function(x) {
throw "exception in host function";
});
}
function catchNested(func, depth /* optionalArgsToFunc... */)
{
if (depth > 1) {
var args = Array.prototype.slice.call(arguments, 0);
--args[1];
catchNested.apply(this, args);
} else {
try {
func.apply(this, Array.prototype.slice.call(arguments, 2));
} catch (e) {
console.log("catchNested caught exception: " + JSON.stringify(e));
}
}
}
function noException()
{
return "no exception";
}
CONSOLE MESSAGE: line 1: caught inline: {"line":1,"column":36}
CONSOLE MESSAGE: line 38: catchNested caught exception: {"line":3,"column":11,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 38: catchNested caught exception: {"code":8,"message":"NotFoundError: DOM Exception 8","name":"NotFoundError","line":8,"column":30,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 38: catchNested caught exception: "exception in host function"
CONSOLE MESSAGE: line 38: catchNested caught exception: "exception string"
CONSOLE MESSAGE: line 38: catchNested caught exception: {"line":18,"column":12,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 1: TypeError: undefined is not an object (evaluating '({}).a.b')
CONSOLE MESSAGE: line 3: TypeError: undefined is not an object (evaluating '({}).a.b')
CONSOLE MESSAGE: line 8: NotFoundError: DOM Exception 8: An attempt was made to reference a Node in a context where it does not exist.
CONSOLE MESSAGE: line 24: exception in host function
CONSOLE MESSAGE: line 13: exception string
CONSOLE MESSAGE: line 18: [object Object]
CONSOLE MESSAGE: line 18: Error: error message
Debugger.setPauseOnExceptions all
Breakpoints Enabled
Debugger.setPauseOnExceptions - all
PASS - did not pause on non-exception statements
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
PASS - paused for each caught exception
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
PASS - paused for each uncaught exception
PASS - did not pause on non-exception statements
<html>
<head>
<script src="../../http/tests/inspector-protocol/resources/protocol-test.js"></script>
<script src="resources/exception.js"></script>
<script>
function test()
{
var expectPause = true;
var statementsWithoutExceptions = [
"1+1;",
"noException();",
"catchNested(noException, 7);"
];
var currentCaughtExceptionStatement = 0;
var statementsWithCaughtExceptions = [
"try{ ({}).a.b.c.d } catch (e) { console.log('caught inline: ' + JSON.stringify(e)); }",
"catchNested(exceptionBasic, 1);",
"catchNested(exceptionDOM, 2);",
"catchNested(exceptionInHostFunction, 3);",
"catchNested(throwString, 10);",
"catchNested(throwParam, 5, new Error('error message'));"
];
var currentUncaughtExceptionStatement = 0;
var statementsWithUncaughtExceptions = [
"({}).a.b.c.d;",
"exceptionBasic();",
"exceptionDOM();",
"exceptionInHostFunction();",
"throwString();",
"throwParam({x:1});",
"throwParam(new Error('error message'));"
];
// FIXME: <https://webkit.org/b/121108> Web Inspector gets paused twice when there is an exception in host function
var extraCaughtExceptionPauses = 1;
var extraUncaughtExceptionPauses = 1;
function evaluateStatements(list, callback)
{
for (var i = 0; i < list.length; ++i) {
var statement = list[i];
var last = i === list.length - 1;
var responseCallback = last ? function() { setTimeout(callback, 10); } : function(){};
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { " + statement + " }, 0);"}, responseCallback);
}
}
function evaluateExpectedNoPauses(callback)
{
var statements = statementsWithoutExceptions;
evaluateStatements(statements, function() {
InspectorTest.log("PASS - did not pause on non-exception statements");
callback();
});
}
var triggerNext = triggerNextCaughtException;
function triggerNextCaughtException()
{
// Evaluate statement and expect to pause.
if (currentCaughtExceptionStatement < statementsWithCaughtExceptions.length) {
var statement = statementsWithCaughtExceptions[currentCaughtExceptionStatement++];
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { " + statement + " }, 0);"});
return;
}
// FIXME: <https://webkit.org/b/121108> Web Inspector gets paused twice when there is an exception in host function
if (extraCaughtExceptionPauses-- > 0)
return;
// Done evaluating caught exceptions statements. Move on to uncaught exceptions.
InspectorTest.log("PASS - paused for each caught exception");
triggerNext = triggerNextUncaughtException;
triggerNext();
}
function triggerNextUncaughtException()
{
// Evaluate statement and expect to pause.
if (currentUncaughtExceptionStatement < statementsWithUncaughtExceptions.length) {
var statement = statementsWithUncaughtExceptions[currentUncaughtExceptionStatement++];
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { " + statement + " }, 0);"});
return;
}
// FIXME: <https://webkit.org/b/121108> Web Inspector gets paused twice when there is an exception in host function
if (extraUncaughtExceptionPauses-- > 0)
return;
// Done evaluating statements to pause. Evaluate some more we do not expect to pause.
InspectorTest.log("PASS - paused for each uncaught exception");
expectPause = false;
evaluateExpectedNoPauses(function() {
InspectorTest.completeTest();
});
}
InspectorTest.sendCommand("Debugger.enable", {});
InspectorTest.sendCommand("Debugger.setBreakpointsActive", {active: true}, function() {
InspectorTest.log("Breakpoints Enabled");
InspectorTest.sendCommand("Debugger.setPauseOnExceptions", {state: "all"}, function(responseObject) {
InspectorTest.checkForError(responseObject);
InspectorTest.log("Debugger.setPauseOnExceptions - all");
evaluateExpectedNoPauses(function() {
expectPause = true;
triggerNext();
});
});
});
InspectorTest.eventHandler["Debugger.paused"] = function(messageObject)
{
InspectorTest.log("Paused!");
if (!expectPause) {
InspectorTest.log("FAIL - debugger paused when we did not expect to");
InspectorTest.completeTest();
return;
}
InspectorTest.sendCommand("Debugger.resume", {});
}
InspectorTest.eventHandler["Debugger.resumed"] = function(messageObject)
{
InspectorTest.log("Resumed");
triggerNext();
}
}
</script>
</head>
<body onload="runTest()">
<p>Debugger.setPauseOnExceptions all</p>
</body>
</html>
CONSOLE MESSAGE: line 1: caught inline: {"line":1,"column":36}
CONSOLE MESSAGE: line 38: catchNested caught exception: {"line":3,"column":11,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 38: catchNested caught exception: {"code":8,"message":"NotFoundError: DOM Exception 8","name":"NotFoundError","line":8,"column":30,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 38: catchNested caught exception: "exception in host function"
CONSOLE MESSAGE: line 38: catchNested caught exception: "exception string"
CONSOLE MESSAGE: line 38: catchNested caught exception: {"line":18,"column":12,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 3: TypeError: undefined is not an object (evaluating '({}).a.b')
CONSOLE MESSAGE: line 8: NotFoundError: DOM Exception 8: An attempt was made to reference a Node in a context where it does not exist.
CONSOLE MESSAGE: line 24: exception in host function
CONSOLE MESSAGE: line 13: exception string
CONSOLE MESSAGE: line 18: [object Object]
CONSOLE MESSAGE: line 18: Error: error message
Debugger.setPauseOnExceptions none
Breakpoints Enabled
Debugger.setPauseOnExceptions - none
PASS: No pauses
<html>
<head>
<script src="../../http/tests/inspector-protocol/resources/protocol-test.js"></script>
<script src="resources/exception.js"></script>
<script>
function test()
{
InspectorTest.sendCommand("Debugger.enable", {});
InspectorTest.sendCommand("Debugger.setBreakpointsActive", {active: true}, function() {
InspectorTest.log("Breakpoints Enabled");
InspectorTest.sendCommand("Debugger.setPauseOnExceptions", {state: "none"}, function(responseObject) {
InspectorTest.checkForError(responseObject);
InspectorTest.log("Debugger.setPauseOnExceptions - none");
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { 1+1; }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { noException(); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { catchNested(noException, 7); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { try{ ({}).a.b.c.d } catch (e) { console.log('caught inline: ' + JSON.stringify(e)); } }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { catchNested(exceptionBasic, 1); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { catchNested(exceptionDOM, 2); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { catchNested(exceptionInHostFunction, 3); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { catchNested(throwString, 10); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { catchNested(throwParam, 5, new Error('error message')); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { {}.a.b.c.d; }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { exceptionBasic(); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { exceptionDOM(); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { exceptionInHostFunction(); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { throwString(); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { throwParam({x:1}); }, 0);"});
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { throwParam(new Error('error message')); }, 0);"}, function() {
setTimeout(function() {
InspectorTest.log("PASS: No pauses");
InspectorTest.completeTest();
}, 10);
});
});
});
InspectorTest.eventHandler["Debugger.paused"] = function(messageObject)
{
InspectorTest.log("Paused!");
InspectorTest.log("FAIL");
InspectorTest.completeTest();
}
}
</script>
</head>
<body onload="runTest()">
<p>Debugger.setPauseOnExceptions none</p>
</body>
</html>
CONSOLE MESSAGE: line 1: caught inline: {"line":1,"column":36}
CONSOLE MESSAGE: line 38: catchNested caught exception: {"line":3,"column":11,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 38: catchNested caught exception: {"code":8,"message":"NotFoundError: DOM Exception 8","name":"NotFoundError","line":8,"column":30,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 38: catchNested caught exception: "exception in host function"
CONSOLE MESSAGE: line 38: catchNested caught exception: "exception string"
CONSOLE MESSAGE: line 38: catchNested caught exception: {"line":18,"column":12,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 1: TypeError: undefined is not an object (evaluating '({}).a.b')
CONSOLE MESSAGE: line 3: TypeError: undefined is not an object (evaluating '({}).a.b')
CONSOLE MESSAGE: line 8: NotFoundError: DOM Exception 8: An attempt was made to reference a Node in a context where it does not exist.
CONSOLE MESSAGE: line 24: exception in host function
CONSOLE MESSAGE: line 13: exception string
CONSOLE MESSAGE: line 18: [object Object]
CONSOLE MESSAGE: line 18: Error: error message
CONSOLE MESSAGE: line 1: caught inline: {"line":1,"column":36}
CONSOLE MESSAGE: line 38: catchNested caught exception: {"line":3,"column":11,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 38: catchNested caught exception: {"code":8,"message":"NotFoundError: DOM Exception 8","name":"NotFoundError","line":8,"column":30,"sourceURL":"exception.js"}
CONSOLE MESSAGE: line 38: catchNested caught exception: "exception in host function"
CONSOLE MESSAGE: line 38: catchNested caught exception: "exception string"
CONSOLE MESSAGE: line 38: catchNested caught exception: {"line":18,"column":12,"sourceURL":"exception.js"}
Debugger.setPauseOnExceptions uncaught
Breakpoints Enabled
Debugger.setPauseOnExceptions - uncaught
PASS - did not pause on non-exception statements or caught exception statements
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
Paused!
Resumed
PASS - paused for each uncaught exception
PASS - did not pause on non-exception statements or caught exception statements
PASS
<html>
<head>
<script src="../../http/tests/inspector-protocol/resources/protocol-test.js"></script>
<script src="resources/exception.js"></script>
<script>
function test()
{
var expectPause = false;
var statementsWithoutExceptions = [
"1+1;",
"noException();",
"catchNested(noException, 7);"
];
var statementsWithCaughtExceptions = [
"try{ ({}).a.b.c.d } catch (e) { console.log('caught inline: ' + JSON.stringify(e)); }",
"catchNested(exceptionBasic, 1);",
"catchNested(exceptionDOM, 2);",
"catchNested(exceptionInHostFunction, 3);",
"catchNested(throwString, 10);",
"catchNested(throwParam, 5, new Error('error message'));"
];
var currentUncaughtExceptionStatement = 0;
var statementsWithUncaughtExceptions = [
"({}).a.b.c.d;",
"exceptionBasic();",
"exceptionDOM();",
"exceptionInHostFunction();",
"throwString();",
"throwParam({x:1});",
"throwParam(new Error('error message'));"
];
// FIXME: <https://webkit.org/b/121108> Web Inspector gets paused twice when there is an exception in host function
var extraPauses = 1;
function evaluateStatements(list, callback)
{
for (var i = 0; i < list.length; ++i) {
var statement = list[i];
var last = i === list.length - 1;
var responseCallback = last ? function() { setTimeout(callback, 10); } : function(){};
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { " + statement + " }, 0);"}, responseCallback);
}
}
function evaluateExpectedNoPauses(callback)
{
var statements = statementsWithoutExceptions.concat(statementsWithCaughtExceptions);
evaluateStatements(statements, function() {
InspectorTest.log("PASS - did not pause on non-exception statements or caught exception statements");
callback();
});
}
function triggerNextUncaughtException()
{
// Evaluate statement and expect to pause.
if (currentUncaughtExceptionStatement < statementsWithUncaughtExceptions.length) {
var statement = statementsWithUncaughtExceptions[currentUncaughtExceptionStatement++];
InspectorTest.sendCommand("Runtime.evaluate", {expression: "setTimeout(function() { " + statement + " }, 0);"});
return;
}
// FIXME: <https://webkit.org/b/121108> Web Inspector gets paused twice when there is an exception in host function
if (extraPauses-- > 0)
return;
// Done evaluating statements to pause. Evaluate some more we do not expect to pause.
InspectorTest.log("PASS - paused for each uncaught exception");
expectPause = false;
evaluateExpectedNoPauses(function() {
InspectorTest.log("PASS");
InspectorTest.completeTest();
});
}
InspectorTest.sendCommand("Debugger.enable", {});
InspectorTest.sendCommand("Debugger.setBreakpointsActive", {active: true}, function() {
InspectorTest.log("Breakpoints Enabled");
InspectorTest.sendCommand("Debugger.setPauseOnExceptions", {state: "uncaught"}, function(responseObject) {
InspectorTest.checkForError(responseObject);
InspectorTest.log("Debugger.setPauseOnExceptions - uncaught");
evaluateExpectedNoPauses(function() {
expectPause = true;
triggerNextUncaughtException();
});
});
});
InspectorTest.eventHandler["Debugger.paused"] = function(messageObject)
{
InspectorTest.log("Paused!");
if (!expectPause) {
InspectorTest.log("FAIL - debugger paused when we did not expect to");
InspectorTest.completeTest();
return;
}
InspectorTest.sendCommand("Debugger.resume", {});
}
InspectorTest.eventHandler["Debugger.resumed"] = function(messageObject)
{
InspectorTest.log("Resumed");
triggerNextUncaughtException();
}
}
</script>
</head>
<body onload="runTest()">
<p>Debugger.setPauseOnExceptions uncaught</p>
</body>
</html>
2013-09-10 Joseph Pecoraro <pecoraro@apple.com>
Web Inspector: [JSC] Caught exception is treated as uncaught
https://bugs.webkit.org/show_bug.cgi?id=93607
Reviewed by Geoff Garen.
Check up the entire call stack to see if there is an exception handler.
* interpreter/Interpreter.cpp:
(JSC::GetExceptionHandlerFunctor::GetExceptionHandlerFunctor):
(JSC::GetExceptionHandlerFunctor::handler):
(JSC::GetExceptionHandlerFunctor::operator()):
2013-09-10 Filip Pizlo <fpizlo@apple.com>
SpecType should have SpecInt48AsDouble
......
......@@ -566,6 +566,33 @@ JSString* Interpreter::stackTraceAsString(ExecState* exec, Vector<StackFrame> st
return jsString(&exec->vm(), builder.toString());
}
class GetExceptionHandlerFunctor {
public:
GetExceptionHandlerFunctor()
: m_handler(0)
{
}
HandlerInfo* handler() { return m_handler; }
StackVisitor::Status operator()(StackVisitor& visitor)
{
CodeBlock* codeBlock = visitor->codeBlock();
if (!codeBlock)
return StackVisitor::Continue;
unsigned bytecodeOffset = visitor->bytecodeOffset();
m_handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset);
if (m_handler)
return StackVisitor::Done;
return StackVisitor::Continue;
}
private:
HandlerInfo* m_handler;
};
class UnwindFunctor {
public:
UnwindFunctor(CallFrame*& callFrame, JSValue& exceptionValue, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler)
......@@ -627,9 +654,17 @@ NEVER_INLINE HandlerInfo* Interpreter::unwind(CallFrame*& callFrame, JSValue& ex
// We need to clear the exception and the exception stack here in order to see if a new exception happens.
// Afterwards, the values are put back to continue processing this error.
ClearExceptionScope scope(&callFrame->vm());
DebuggerCallFrame debuggerCallFrame(callFrame, exceptionValue);
bool hasHandler = codeBlock->handlerForBytecodeOffset(bytecodeOffset);
bool hasHandler;
if (isTermination)
hasHandler = false;
else {
GetExceptionHandlerFunctor functor;
callFrame->iterate(functor);
hasHandler = !!functor.handler();
}
debugger->exception(debuggerCallFrame, codeBlock->ownerExecutable()->sourceID(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), 0, hasHandler);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment