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);