Fixed erroneous line number for LLint frame when throwing exceptions.
https://bugs.webkit.org/show_bug.cgi?id=94051.
Patch by Mark Lam <mark.lam@apple.com> on 2012-08-20
Reviewed by Filip Pizlo.
For LLInt frames, before throwing an exception, adjust the PC from the
return PC back to the call PC if we are indeed at a call site.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::adjustPCIfAtCallSite):
(JSC):
(JSC::CodeBlock::bytecodeOffset):
* bytecode/CodeBlock.h:
(CodeBlock):
* llint/LLIntExceptions.cpp:
(JSC::LLInt::fixupPCforExceptionIfNeeded):
(LLInt):
(JSC::LLInt::interpreterThrowInCaller):
(JSC::LLInt::returnToThrow):
(JSC::LLInt::callToThrow):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@126093 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 00ef64f..bd68e5e 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,26 @@
+2012-08-20 Mark Lam <mark.lam@apple.com>
+
+ Fixed erroneous line number for LLint frame when throwing exceptions.
+ https://bugs.webkit.org/show_bug.cgi?id=94051.
+
+ Reviewed by Filip Pizlo.
+
+ For LLInt frames, before throwing an exception, adjust the PC from the
+ return PC back to the call PC if we are indeed at a call site.
+
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::adjustPCIfAtCallSite):
+ (JSC):
+ (JSC::CodeBlock::bytecodeOffset):
+ * bytecode/CodeBlock.h:
+ (CodeBlock):
+ * llint/LLIntExceptions.cpp:
+ (JSC::LLInt::fixupPCforExceptionIfNeeded):
+ (LLInt):
+ (JSC::LLInt::interpreterThrowInCaller):
+ (JSC::LLInt::returnToThrow):
+ (JSC::LLInt::callToThrow):
+
2012-08-20 Filip Pizlo <fpizlo@apple.com>
fast/js/dfg-peephole-compare-final-object-to-final-object-or-other-when-both-proven-final-object.html on 32-bit
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.cpp b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
index 7ae3a3e..2ea969f 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -2580,6 +2580,60 @@
m_incomingCalls.begin()->unlink(*m_globalData, repatchBuffer);
}
+#if ENABLE(LLINT)
+Instruction* CodeBlock::adjustPCIfAtCallSite(Instruction* potentialReturnPC)
+{
+ ASSERT(potentialReturnPC);
+
+ unsigned returnPCOffset = potentialReturnPC - instructions().begin();
+ Instruction* adjustedPC;
+ unsigned opcodeLength;
+
+ // If we are at a callsite, the LLInt stores the PC after the call
+ // instruction rather than the PC of the call instruction. This requires
+ // some correcting. If so, we can rely on the fact that the preceding
+ // instruction must be one of the call instructions, so either it's a
+ // call_varargs or it's a call, construct, or eval.
+ //
+ // If we are not at a call site, then we need to guard against the
+ // possibility of peeking past the start of the bytecode range for this
+ // codeBlock. Hence, we do a bounds check before we peek at the
+ // potential "preceding" instruction.
+ // The bounds check is done by comparing the offset of the potential
+ // returnPC with the length of the opcode. If there is room for a call
+ // instruction before the returnPC, then the offset of the returnPC must
+ // be greater than the size of the call opcode we're looking for.
+
+ // The determination of the call instruction present (if we are at a
+ // callsite) depends on the following assumptions. So, assert that
+ // they are still true:
+ ASSERT(OPCODE_LENGTH(op_call_varargs) <= OPCODE_LENGTH(op_call));
+ ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct));
+ ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_call_eval));
+
+ // Check for the case of a preceeding op_call_varargs:
+ opcodeLength = OPCODE_LENGTH(op_call_varargs);
+ adjustedPC = potentialReturnPC - opcodeLength;
+ if ((returnPCOffset >= opcodeLength)
+ && (adjustedPC->u.pointer == bitwise_cast<void*>(llint_op_call_varargs))) {
+ return adjustedPC;
+ }
+
+ // Check for the case of the other 3 call instructions:
+ opcodeLength = OPCODE_LENGTH(op_call);
+ adjustedPC = potentialReturnPC - opcodeLength;
+ if ((returnPCOffset >= opcodeLength)
+ && (adjustedPC->u.pointer == bitwise_cast<void*>(llint_op_call)
+ || adjustedPC->u.pointer == bitwise_cast<void*>(llint_op_construct)
+ || adjustedPC->u.pointer == bitwise_cast<void*>(llint_op_call_eval))) {
+ return adjustedPC;
+ }
+
+ // Not a call site. No need to adjust PC. Just return the original.
+ return potentialReturnPC;
+}
+#endif
+
unsigned CodeBlock::bytecodeOffset(ExecState* exec, ReturnAddressPtr returnAddress)
{
#if ENABLE(LLINT)
@@ -2590,28 +2644,8 @@
ASSERT(JITCode::isBaselineCode(getJITType()));
Instruction* instruction = exec->currentVPC();
ASSERT(instruction);
-
- // The LLInt stores the PC after the call instruction rather than the PC of
- // the call instruction. This requires some correcting. We rely on the fact
- // that the preceding instruction must be one of the call instructions, so
- // either it's a call_varargs or it's a call, construct, or eval.
- ASSERT(OPCODE_LENGTH(op_call_varargs) <= OPCODE_LENGTH(op_call));
- ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_construct));
- ASSERT(OPCODE_LENGTH(op_call) == OPCODE_LENGTH(op_call_eval));
- if (instruction[-OPCODE_LENGTH(op_call_varargs)].u.pointer == bitwise_cast<void*>(llint_op_call_varargs)) {
- // We know that the preceding instruction must be op_call_varargs because there is no way that
- // the pointer to the call_varargs could be an operand to the call.
- instruction -= OPCODE_LENGTH(op_call_varargs);
- ASSERT(instruction[-OPCODE_LENGTH(op_call)].u.pointer != bitwise_cast<void*>(llint_op_call)
- && instruction[-OPCODE_LENGTH(op_call)].u.pointer != bitwise_cast<void*>(llint_op_construct)
- && instruction[-OPCODE_LENGTH(op_call)].u.pointer != bitwise_cast<void*>(llint_op_call_eval));
- } else {
- // Must be that the last instruction was some op_call.
- ASSERT(instruction[-OPCODE_LENGTH(op_call)].u.pointer == bitwise_cast<void*>(llint_op_call)
- || instruction[-OPCODE_LENGTH(op_call)].u.pointer == bitwise_cast<void*>(llint_op_construct)
- || instruction[-OPCODE_LENGTH(op_call)].u.pointer == bitwise_cast<void*>(llint_op_call_eval));
- instruction -= OPCODE_LENGTH(op_call);
- }
+
+ instruction = adjustPCIfAtCallSite(instruction);
return bytecodeOffset(instruction);
}
diff --git a/Source/JavaScriptCore/bytecode/CodeBlock.h b/Source/JavaScriptCore/bytecode/CodeBlock.h
index d72276b..a8b2a58 100644
--- a/Source/JavaScriptCore/bytecode/CodeBlock.h
+++ b/Source/JavaScriptCore/bytecode/CodeBlock.h
@@ -230,6 +230,9 @@
return *(binarySearch<MethodCallLinkInfo, unsigned, getMethodCallLinkInfoBytecodeIndex>(m_methodCallLinkInfos.begin(), m_methodCallLinkInfos.size(), bytecodeIndex));
}
+#if ENABLE(LLINT)
+ Instruction* adjustPCIfAtCallSite(Instruction*);
+#endif
unsigned bytecodeOffset(ExecState*, ReturnAddressPtr);
unsigned bytecodeOffsetForCallAtIndex(unsigned index)
diff --git a/Source/JavaScriptCore/llint/LLIntExceptions.cpp b/Source/JavaScriptCore/llint/LLIntExceptions.cpp
index 20b0db3..a915c42 100644
--- a/Source/JavaScriptCore/llint/LLIntExceptions.cpp
+++ b/Source/JavaScriptCore/llint/LLIntExceptions.cpp
@@ -37,6 +37,14 @@
namespace JSC { namespace LLInt {
+static void fixupPCforExceptionIfNeeded(ExecState* exec)
+{
+ CodeBlock* codeBlock = exec->codeBlock();
+ ASSERT(!!codeBlock);
+ Instruction* pc = exec->currentVPC();
+ exec->setCurrentVPC(codeBlock->adjustPCIfAtCallSite(pc));
+}
+
void interpreterThrowInCaller(ExecState* exec, ReturnAddressPtr pc)
{
JSGlobalData* globalData = &exec->globalData();
@@ -44,6 +52,7 @@
#if LLINT_SLOW_PATH_TRACING
dataLog("Throwing exception %s.\n", globalData->exception.description());
#endif
+ fixupPCforExceptionIfNeeded(exec);
genericThrow(
globalData, exec, globalData->exception,
exec->codeBlock()->bytecodeOffset(exec, pc));
@@ -61,6 +70,7 @@
#if LLINT_SLOW_PATH_TRACING
dataLog("Throwing exception %s (returnToThrow).\n", globalData->exception.description());
#endif
+ fixupPCforExceptionIfNeeded(exec);
genericThrow(globalData, exec, globalData->exception, pc - exec->codeBlock()->instructions().begin());
return globalData->llintData.exceptionInstructions();
@@ -73,6 +83,7 @@
#if LLINT_SLOW_PATH_TRACING
dataLog("Throwing exception %s (callToThrow).\n", globalData->exception.description());
#endif
+ fixupPCforExceptionIfNeeded(exec);
genericThrow(globalData, exec, globalData->exception, pc - exec->codeBlock()->instructions().begin());
return bitwise_cast<void*>(&llint_throw_during_call_trampoline);