StackOverflow stack unwinding should stop at native frames.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Sep 2015 01:03:45 +0000 (01:03 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 5 Sep 2015 01:03:45 +0000 (01:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148749

Reviewed by Michael Saboff.

In the present code, after ping-pong'ing back and forth between native and JS
code a few times, if we have a stack overflow on re-entry into the VM to run
JS code's whose stack frame would overflow the JS stack, the code will end up
unwinding past the native function that is making the call to re-enter the VM.
As a result, any clean up code (e.g. destructors for stack variables) in the
skipped native function frame (and its chain of native function callers) will
not be called.

This patch is based on the Michael Saboff's fix of this issue landed on the
jsc-tailcall branch: http://trac.webkit.org/changeset/188555

We now check for the case where there are no JS frames to unwind since the
last native frame, and treat the exception as an unhandled exception.  The
native function is responsible for further propagating the exception if needed.

Other supporting work:
1. Remove vm->vmEntryFrameForThrow.  It should always be the same as
   vm->topVMEntryFrame.
2. Change operationThrowStackOverflowError() to use the throwStackOverflowError()
   helper function instead of rolling its own.
3. In the LLINT vm entry, set vm->topVMEntryFrame as soon as the entry frame is
   fully initialized (instead of waiting).  With this, we can always reliably
   tell which VMEntryFrame is on top.
4. Added a test that exercises this edge case.  The test should not hang or crash.

* API/tests/PingPongStackOverflowTest.cpp: Added.
(PingPongStackOverflowObject_hasInstance):
(testPingPongStackOverflow):
* API/tests/PingPongStackOverflowTest.h: Added.
* API/tests/testapi.c:
(main):
* JavaScriptCore.xcodeproj/project.pbxproj:
* interpreter/Interpreter.cpp:
(JSC::unwindCallFrame):
(JSC::getStackFrameCodeType):
(JSC::UnwindFunctor::UnwindFunctor):
(JSC::UnwindFunctor::operator()):
(JSC::Interpreter::unwind):
* interpreter/Interpreter.h:
(JSC::NativeCallFrameTracer::NativeCallFrameTracer):
(JSC::NativeCallFrameTracerWithRestore::NativeCallFrameTracerWithRestore):
(JSC::NativeCallFrameTracerWithRestore::~NativeCallFrameTracerWithRestore):
(JSC::Interpreter::sampler):
* jit/CCallHelpers.h:
(JSC::CCallHelpers::jumpToExceptionHandler):
* jit/JITExceptions.cpp:
(JSC::genericUnwind):
* jit/JITExceptions.h:
* jit/JITOpcodes.cpp:
(JSC::JIT::emit_op_catch):
* jit/JITOpcodes32_64.cpp:
(JSC::JIT::emit_op_catch):
* jit/JITOperations.cpp:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/VM.h:
(JSC::VM::exceptionOffset):
(JSC::VM::callFrameForThrowOffset):
(JSC::VM::vmEntryFrameForThrowOffset): Deleted.
(JSC::VM::topVMEntryFrameOffset): Deleted.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@189411 268f45cc-cd09-0410-ab3c-d52691b4dbfc

17 files changed:
Source/JavaScriptCore/API/tests/PingPongStackOverflowTest.cpp [new file with mode: 0644]
Source/JavaScriptCore/API/tests/PingPongStackOverflowTest.h [new file with mode: 0644]
Source/JavaScriptCore/API/tests/testapi.c
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/interpreter/CallFrame.h
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/interpreter/Interpreter.h
Source/JavaScriptCore/jit/CCallHelpers.h
Source/JavaScriptCore/jit/JITExceptions.cpp
Source/JavaScriptCore/jit/JITExceptions.h
Source/JavaScriptCore/jit/JITOpcodes.cpp
Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
Source/JavaScriptCore/jit/JITOperations.cpp
Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
Source/JavaScriptCore/runtime/VM.h

diff --git a/Source/JavaScriptCore/API/tests/PingPongStackOverflowTest.cpp b/Source/JavaScriptCore/API/tests/PingPongStackOverflowTest.cpp
new file mode 100644 (file)
index 0000000..33f0772
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#include "config.h"
+#include "PingPongStackOverflowTest.h"
+
+#include "InitializeThreading.h"
+#include "JSContextRefPrivate.h"
+#include "JavaScriptCore.h"
+#include "Options.h"
+#include <wtf/text/StringBuilder.h>
+
+using JSC::Options;
+
+static JSGlobalContextRef context = nullptr;
+static int nativeRecursionCount = 0;
+
+static bool PingPongStackOverflowObject_hasInstance(JSContextRef context, JSObjectRef constructor, JSValueRef possibleValue, JSValueRef* exception)
+{
+    UNUSED_PARAM(context);
+    UNUSED_PARAM(constructor);
+
+    JSStringRef hasInstanceName = JSStringCreateWithUTF8CString("hasInstance");
+    JSValueRef hasInstance = JSObjectGetProperty(context, constructor, hasInstanceName, exception);
+    JSStringRelease(hasInstanceName);
+    if (!hasInstance)
+        return false;
+
+    int countAtEntry = nativeRecursionCount++;
+
+    JSValueRef result = 0;
+    if (nativeRecursionCount < 100) {
+        JSObjectRef function = JSValueToObject(context, hasInstance, exception);
+        result = JSObjectCallAsFunction(context, function, constructor, 1, &possibleValue, exception);
+    } else {
+        StringBuilder builder;
+        builder.append("dummy.valueOf([0]");
+        for (int i = 1; i < 35000; i++) {
+            builder.append(", [");
+            builder.appendNumber(i);
+            builder.append("]");
+        }
+        builder.append(");");
+
+        JSStringRef script = JSStringCreateWithUTF8CString(builder.toString().utf8().data());
+        result = JSEvaluateScript(context, script, NULL, NULL, 1, exception);
+        JSStringRelease(script);
+    }
+
+    --nativeRecursionCount;
+    if (nativeRecursionCount != countAtEntry)
+        printf("    ERROR: PingPongStackOverflow test saw a recursion count mismatch\n");
+
+    return result && JSValueToBoolean(context, result);
+}
+
+JSClassDefinition PingPongStackOverflowObject_definition = {
+    0,
+    kJSClassAttributeNone,
+    
+    "PingPongStackOverflowObject",
+    NULL,
+    
+    NULL,
+    NULL,
+    
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    PingPongStackOverflowObject_hasInstance,
+    NULL,
+};
+
+static JSClassRef PingPongStackOverflowObject_class(JSContextRef context)
+{
+    UNUSED_PARAM(context);
+    
+    static JSClassRef jsClass;
+    if (!jsClass)
+        jsClass = JSClassCreate(&PingPongStackOverflowObject_definition);
+    
+    return jsClass;
+}
+
+// This tests tests a stack overflow on VM reentry into a JS function from a native function
+// after ping-pong'ing back and forth between JS and native functions multiple times.
+// This test should not hang or crash.
+int testPingPongStackOverflow()
+{
+    bool failed = false;
+
+    JSC::initializeThreading();
+    Options::initialize(); // Ensure options is initialized first.
+
+    auto origReservedZoneSize = Options::reservedZoneSize();
+    auto origErrorModeReservedZoneSize = Options::errorModeReservedZoneSize();
+    auto origUseLLInt = Options::useLLInt();
+    auto origMaxPerThreadStackUsage = Options::maxPerThreadStackUsage();
+
+    Options::reservedZoneSize() = 128 * KB;
+    Options::errorModeReservedZoneSize() = 64 * KB;
+#if ENABLE(JIT)
+    // Normally, we want to disable the LLINT to force the use of JITted code which is necessary for
+    // reproducing the regression in https://bugs.webkit.org/show_bug.cgi?id=148749. However, we only
+    // want to do this if the LLINT isn't the only available execution engine.
+    Options::useLLInt() = false;
+#endif
+
+    const char* scriptString =
+        "var count = 0;" \
+        "PingPongStackOverflowObject.hasInstance = function f() {" \
+        "    return (undefined instanceof PingPongStackOverflowObject);" \
+        "};" \
+        "PingPongStackOverflowObject.__proto__ = undefined;" \
+        "undefined instanceof PingPongStackOverflowObject;";
+
+    JSValueRef scriptResult = nullptr;
+    JSValueRef exception = nullptr;
+    JSStringRef script = JSStringCreateWithUTF8CString(scriptString);
+
+    nativeRecursionCount = 0;
+    context = JSGlobalContextCreateInGroup(nullptr, nullptr);
+
+    JSObjectRef globalObject = JSContextGetGlobalObject(context);
+    ASSERT(JSValueIsObject(context, globalObject));
+
+    JSObjectRef PingPongStackOverflowObject = JSObjectMake(context, PingPongStackOverflowObject_class(context), NULL);
+    JSStringRef PingPongStackOverflowObjectString = JSStringCreateWithUTF8CString("PingPongStackOverflowObject");
+    JSObjectSetProperty(context, globalObject, PingPongStackOverflowObjectString, PingPongStackOverflowObject, kJSPropertyAttributeNone, NULL);
+    JSStringRelease(PingPongStackOverflowObjectString);
+
+    unsigned stackSize = 32 * KB;
+    Options::maxPerThreadStackUsage() = stackSize + Options::reservedZoneSize();
+
+    exception = nullptr;
+    scriptResult = JSEvaluateScript(context, script, nullptr, nullptr, 1, &exception);
+
+    if (!exception) {
+        printf("FAIL: PingPongStackOverflowError not thrown in PingPongStackOverflow test\n");
+        failed = true;
+    } else if (nativeRecursionCount) {
+        printf("FAIL: Unbalanced native recursion count: %d in PingPongStackOverflow test\n", nativeRecursionCount);
+        failed = true;
+    } else {
+        printf("PASS: PingPongStackOverflow test.\n");
+    }
+
+    Options::reservedZoneSize() = origReservedZoneSize;
+    Options::errorModeReservedZoneSize() = origErrorModeReservedZoneSize;
+    Options::useLLInt() = origUseLLInt;
+    Options::maxPerThreadStackUsage() = origMaxPerThreadStackUsage;
+
+    return failed;
+}
diff --git a/Source/JavaScriptCore/API/tests/PingPongStackOverflowTest.h b/Source/JavaScriptCore/API/tests/PingPongStackOverflowTest.h
new file mode 100644 (file)
index 0000000..7f5911b
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+#ifndef PingPongStackOverflowTest_h
+#define PingPongStackOverflowTest_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int testPingPongStackOverflow();
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* PingPongStackOverflowTest_h */
index 81cbc40..cbf9daf 100644 (file)
@@ -41,9 +41,9 @@
 
 #include "CompareAndSwapTest.h"
 #include "CustomGlobalObjectClassTest.h"
-#include "GlobalContextWithFinalizerTest.h"
-
 #include "ExecutionTimeLimitTest.h"
+#include "GlobalContextWithFinalizerTest.h"
+#include "PingPongStackOverflowTest.h"
 
 #if JSC_OBJC_API_ENABLED
 void testObjectiveCAPI(void);
@@ -1893,6 +1893,7 @@ int main(int argc, char* argv[])
 
     failed = testExecutionTimeLimit() || failed;
     failed = testGlobalContextWithFinalizer() || failed;
+    failed = testPingPongStackOverflow() || failed;
 
     // Clear out local variables pointing at JSObjectRefs to allow their values to be collected
     function = NULL;
index ca225d1..89396a6 100644 (file)
@@ -1,3 +1,71 @@
+2015-09-04  Mark Lam  <mark.lam@apple.com>
+
+        StackOverflow stack unwinding should stop at native frames.
+        https://bugs.webkit.org/show_bug.cgi?id=148749
+
+        Reviewed by Michael Saboff.
+
+        In the present code, after ping-pong'ing back and forth between native and JS
+        code a few times, if we have a stack overflow on re-entry into the VM to run
+        JS code's whose stack frame would overflow the JS stack, the code will end up
+        unwinding past the native function that is making the call to re-enter the VM.
+        As a result, any clean up code (e.g. destructors for stack variables) in the
+        skipped native function frame (and its chain of native function callers) will
+        not be called.
+
+        This patch is based on the Michael Saboff's fix of this issue landed on the
+        jsc-tailcall branch: http://trac.webkit.org/changeset/188555
+
+        We now check for the case where there are no JS frames to unwind since the
+        last native frame, and treat the exception as an unhandled exception.  The
+        native function is responsible for further propagating the exception if needed.
+
+        Other supporting work:
+        1. Remove vm->vmEntryFrameForThrow.  It should always be the same as
+           vm->topVMEntryFrame.
+        2. Change operationThrowStackOverflowError() to use the throwStackOverflowError()
+           helper function instead of rolling its own.
+        3. In the LLINT vm entry, set vm->topVMEntryFrame as soon as the entry frame is
+           fully initialized (instead of waiting).  With this, we can always reliably
+           tell which VMEntryFrame is on top.
+        4. Added a test that exercises this edge case.  The test should not hang or crash.
+
+        * API/tests/PingPongStackOverflowTest.cpp: Added.
+        (PingPongStackOverflowObject_hasInstance):
+        (testPingPongStackOverflow):
+        * API/tests/PingPongStackOverflowTest.h: Added.
+        * API/tests/testapi.c:
+        (main):
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * interpreter/Interpreter.cpp:
+        (JSC::unwindCallFrame):
+        (JSC::getStackFrameCodeType):
+        (JSC::UnwindFunctor::UnwindFunctor):
+        (JSC::UnwindFunctor::operator()):
+        (JSC::Interpreter::unwind):
+        * interpreter/Interpreter.h:
+        (JSC::NativeCallFrameTracer::NativeCallFrameTracer):
+        (JSC::NativeCallFrameTracerWithRestore::NativeCallFrameTracerWithRestore):
+        (JSC::NativeCallFrameTracerWithRestore::~NativeCallFrameTracerWithRestore):
+        (JSC::Interpreter::sampler):
+        * jit/CCallHelpers.h:
+        (JSC::CCallHelpers::jumpToExceptionHandler):
+        * jit/JITExceptions.cpp:
+        (JSC::genericUnwind):
+        * jit/JITExceptions.h:
+        * jit/JITOpcodes.cpp:
+        (JSC::JIT::emit_op_catch):
+        * jit/JITOpcodes32_64.cpp:
+        (JSC::JIT::emit_op_catch):
+        * jit/JITOperations.cpp:
+        * llint/LowLevelInterpreter32_64.asm:
+        * llint/LowLevelInterpreter64.asm:
+        * runtime/VM.h:
+        (JSC::VM::exceptionOffset):
+        (JSC::VM::callFrameForThrowOffset):
+        (JSC::VM::vmEntryFrameForThrowOffset): Deleted.
+        (JSC::VM::topVMEntryFrameOffset): Deleted.
+
 2015-09-04  Sukolsak Sakshuwong  <sukolsak@gmail.com>
 
         Implement the division and modulo instructions in WebAssembly
index 04a123f..0f68a9f 100644 (file)
                FE68C6381B90DE0B0042BCB3 /* MacroAssemblerPrinter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE68C6351B90DDD90042BCB3 /* MacroAssemblerPrinter.cpp */; };
                FE7BA60F1A1A7CEC00F1F7B4 /* HeapVerifier.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE7BA60D1A1A7CEC00F1F7B4 /* HeapVerifier.cpp */; };
                FE7BA6101A1A7CEC00F1F7B4 /* HeapVerifier.h in Headers */ = {isa = PBXBuildFile; fileRef = FE7BA60E1A1A7CEC00F1F7B4 /* HeapVerifier.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               FE7C41961B97FC4B00F4D598 /* PingPongStackOverflowTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEDA50D41B97F442009A3B4F /* PingPongStackOverflowTest.cpp */; };
                FEA08620182B7A0400F6D851 /* Breakpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = FEA0861E182B7A0400F6D851 /* Breakpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FEA08621182B7A0400F6D851 /* DebuggerPrimitives.h in Headers */ = {isa = PBXBuildFile; fileRef = FEA0861F182B7A0400F6D851 /* DebuggerPrimitives.h */; settings = {ATTRIBUTES = (Private, ); }; };
                FEB51F6C1A97B688001F921C /* Regress141809.mm in Sources */ = {isa = PBXBuildFile; fileRef = FEB51F6B1A97B688001F921C /* Regress141809.mm */; };
                FED287B115EC9A5700DA8161 /* LLIntOpcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LLIntOpcode.h; path = llint/LLIntOpcode.h; sourceTree = "<group>"; };
                FED94F2B171E3E2300BE77A4 /* Watchdog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Watchdog.cpp; sourceTree = "<group>"; };
                FED94F2C171E3E2300BE77A4 /* Watchdog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Watchdog.h; sourceTree = "<group>"; };
+               FEDA50D41B97F442009A3B4F /* PingPongStackOverflowTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = PingPongStackOverflowTest.cpp; path = API/tests/PingPongStackOverflowTest.cpp; sourceTree = "<group>"; };
+               FEDA50D51B97F4D9009A3B4F /* PingPongStackOverflowTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PingPongStackOverflowTest.h; path = API/tests/PingPongStackOverflowTest.h; sourceTree = "<group>"; };
                FEF040501AAE662D00BD28B0 /* CompareAndSwapTest.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CompareAndSwapTest.cpp; path = API/tests/CompareAndSwapTest.cpp; sourceTree = "<group>"; };
                FEF040521AAEC4ED00BD28B0 /* CompareAndSwapTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CompareAndSwapTest.h; path = API/tests/CompareAndSwapTest.h; sourceTree = "<group>"; };
                FEF6835A174343CC00A32E25 /* JITStubsARM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITStubsARM.h; sourceTree = "<group>"; };
                                FE0D4A081ABA2437002F54BF /* GlobalContextWithFinalizerTest.h */,
                                C2181FC018A948FB0025A235 /* JSExportTests.h */,
                                C2181FC118A948FB0025A235 /* JSExportTests.mm */,
+                               FEDA50D41B97F442009A3B4F /* PingPongStackOverflowTest.cpp */,
+                               FEDA50D51B97F4D9009A3B4F /* PingPongStackOverflowTest.h */,
                                65570F581AA4C00A009B3C23 /* Regress141275.h */,
                                65570F591AA4C00A009B3C23 /* Regress141275.mm */,
                                FEB51F6A1A97B688001F921C /* Regress141809.h */,
                        isa = PBXSourcesBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               FE7C41961B97FC4B00F4D598 /* PingPongStackOverflowTest.cpp in Sources */,
                                FE0D4A091ABA2437002F54BF /* GlobalContextWithFinalizerTest.cpp in Sources */,
                                65570F5A1AA4C3EA009B3C23 /* Regress141275.mm in Sources */,
                                C29ECB031804D0ED00D2CBB4 /* CurrentThisInsideBlockGetterTest.mm in Sources */,
index 2075c87..9e5b55f 100644 (file)
@@ -111,6 +111,7 @@ namespace JSC  {
         CallFrame& operator=(const Register& r) { *static_cast<Register*>(this) = r; return *this; }
 
         CallFrame* callerFrame() const { return static_cast<CallFrame*>(callerFrameOrVMEntryFrame()); }
+        void* callerFrameOrVMEntryFrame() const { return callerFrameAndPC().callerFrame; }
 
         JS_EXPORT_PRIVATE CallFrame* callerFrame(VMEntryFrame*&);
 
@@ -275,8 +276,6 @@ namespace JSC  {
             return argIndex;
         }
 
-        void* callerFrameOrVMEntryFrame() const { return callerFrameAndPC().callerFrame; }
-
         CallerFrameAndPC& callerFrameAndPC() { return *reinterpret_cast<CallerFrameAndPC*>(this); }
         const CallerFrameAndPC& callerFrameAndPC() const { return *reinterpret_cast<const CallerFrameAndPC*>(this); }
 
index 4b0f12c..b3727f4 100644 (file)
@@ -631,9 +631,8 @@ private:
 
 class UnwindFunctor {
 public:
-    UnwindFunctor(VMEntryFrame*& vmEntryFrame, CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler)
-        : m_vmEntryFrame(vmEntryFrame)
-        , m_callFrame(callFrame)
+    UnwindFunctor(CallFrame*& callFrame, bool isTermination, CodeBlock*& codeBlock, HandlerInfo*& handler)
+        : m_callFrame(callFrame)
         , m_isTermination(isTermination)
         , m_codeBlock(codeBlock)
         , m_handler(handler)
@@ -643,7 +642,6 @@ public:
     StackVisitor::Status operator()(StackVisitor& visitor)
     {
         VM& vm = m_callFrame->vm();
-        m_vmEntryFrame = visitor->vmEntryFrame();
         m_callFrame = visitor->callFrame();
         m_codeBlock = visitor->codeBlock();
         unsigned bytecodeOffset = visitor->bytecodeOffset();
@@ -661,15 +659,21 @@ public:
     }
 
 private:
-    VMEntryFrame*& m_vmEntryFrame;
     CallFrame*& m_callFrame;
     bool m_isTermination;
     CodeBlock*& m_codeBlock;
     HandlerInfo*& m_handler;
 };
 
-NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallFrame*& callFrame, Exception* exception)
+NEVER_INLINE HandlerInfo* Interpreter::unwind(VM& vm, CallFrame*& callFrame, Exception* exception, UnwindStart unwindStart)
 {
+    if (unwindStart == UnwindFromCallerFrame) {
+        if (callFrame->callerFrameOrVMEntryFrame() == vm.topVMEntryFrame)
+            return nullptr;
+
+        callFrame = callFrame->callerFrame();
+    }
+
     CodeBlock* codeBlock = callFrame->codeBlock();
     bool isTermination = false;
 
@@ -684,13 +688,13 @@ NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallF
     if (exceptionValue.isObject())
         isTermination = isTerminatedExecutionException(exception);
 
-    ASSERT(callFrame->vm().exception() && callFrame->vm().exception()->stack().size());
+    ASSERT(vm.exception() && vm.exception()->stack().size());
 
     Debugger* debugger = callFrame->vmEntryGlobalObject()->debugger();
     if (debugger && debugger->needsExceptionCallbacks() && !exception->didNotifyInspectorOfThrow()) {
         // We need to clear the exception here in order to see if a new exception happens.
         // Afterwards, the values are put back to continue processing this error.
-        SuspendExceptionScope scope(&callFrame->vm());
+        SuspendExceptionScope scope(&vm);
         // This code assumes that if the debugger is enabled then there is no inlining.
         // If that assumption turns out to be false then we'll ignore the inlined call
         // frames.
@@ -713,13 +717,11 @@ NEVER_INLINE HandlerInfo* Interpreter::unwind(VMEntryFrame*& vmEntryFrame, CallF
     exception->setDidNotifyInspectorOfThrow();
 
     // Calculate an exception handler vPC, unwinding call frames as necessary.
-    HandlerInfo* handler = 0;
-    VM& vm = callFrame->vm();
-    ASSERT(callFrame == vm.topCallFrame);
-    UnwindFunctor functor(vmEntryFrame, callFrame, isTermination, codeBlock, handler);
+    HandlerInfo* handler = nullptr;
+    UnwindFunctor functor(callFrame, isTermination, codeBlock, handler);
     callFrame->iterate(functor);
     if (!handler)
-        return 0;
+        return nullptr;
 
     if (LegacyProfiler* profiler = vm.enabledProfiler())
         profiler->exceptionUnwind(callFrame);
index 4fefaa6..a96efb7 100644 (file)
@@ -67,6 +67,8 @@ namespace JSC {
     struct Instruction;
     struct ProtoCallFrame;
 
+    enum UnwindStart { UnwindFromCurrentFrame, UnwindFromCallerFrame };
+
     enum DebugHookID {
         WillExecuteProgram,
         DidExecuteProgram,
@@ -145,6 +147,7 @@ namespace JSC {
         {
             ASSERT(vm);
             ASSERT(callFrame);
+            ASSERT(callFrame < vm->topVMEntryFrame);
             vm->topCallFrame = callFrame;
         }
     };
@@ -222,7 +225,7 @@ namespace JSC {
         
         SamplingTool* sampler() { return m_sampler.get(); }
 
-        NEVER_INLINE HandlerInfo* unwind(VMEntryFrame*&, CallFrame*&, Exception*);
+        NEVER_INLINE HandlerInfo* unwind(VM&, CallFrame*&, Exception*, UnwindStart);
         NEVER_INLINE void debug(CallFrame*, DebugHookID);
         JSString* stackTraceAsString(ExecState*, Vector<StackFrame>);
 
index 9bdefaf..b790e12 100644 (file)
@@ -2078,7 +2078,6 @@ public:
     void jumpToExceptionHandler()
     {
         // genericUnwind() leaves the handler CallFrame* in vm->callFrameForThrow,
-        // the topVMEntryFrame for the handler in vm->vmEntryFrameForThrow,
         // and the address of the handler in vm->targetMachinePCForThrow.
         loadPtr(&vm()->targetMachinePCForThrow, GPRInfo::regT1);
         jump(GPRInfo::regT1);
index 4f6d9e8..d634416 100644 (file)
@@ -40,7 +40,7 @@
 
 namespace JSC {
 
-void genericUnwind(VM* vm, ExecState* callFrame)
+void genericUnwind(VM* vm, ExecState* callFrame, UnwindStart unwindStart)
 {
     if (Options::breakOnThrow()) {
         dataLog("In call frame ", RawPointer(callFrame), " for code block ", *callFrame->codeBlock(), "\n");
@@ -49,8 +49,7 @@ void genericUnwind(VM* vm, ExecState* callFrame)
     
     Exception* exception = vm->exception();
     RELEASE_ASSERT(exception);
-    VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
-    HandlerInfo* handler = vm->interpreter->unwind(vmEntryFrame, callFrame, exception); // This may update vmEntryFrame and callFrame.
+    HandlerInfo* handler = vm->interpreter->unwind(*vm, callFrame, exception, unwindStart); // This may update callFrame.
 
     void* catchRoutine;
     Instruction* catchPCForInterpreter = 0;
@@ -64,7 +63,6 @@ void genericUnwind(VM* vm, ExecState* callFrame)
     } else
         catchRoutine = LLInt::getCodePtr(handleUncaughtException);
     
-    vm->vmEntryFrameForThrow = vmEntryFrame;
     vm->callFrameForThrow = callFrame;
     vm->targetMachinePCForThrow = catchRoutine;
     vm->targetInterpreterPCForThrow = catchPCForInterpreter;
index 43b92e7..3ccac84 100644 (file)
@@ -26,6 +26,7 @@
 #ifndef JITExceptions_h
 #define JITExceptions_h
 
+#include "Interpreter.h"
 #include "JSCJSValue.h"
 
 namespace JSC {
@@ -33,7 +34,7 @@ namespace JSC {
 class ExecState;
 class VM;
 
-void genericUnwind(VM*, ExecState*);
+void genericUnwind(VM*, ExecState*, UnwindStart = UnwindFromCurrentFrame);
 
 } // namespace JSC
 
index 29eb64a..a2953e9 100644 (file)
@@ -501,8 +501,6 @@ void JIT::emit_op_catch(Instruction* currentInstruction)
     
     move(TrustedImmPtr(m_vm), regT3);
     load64(Address(regT3, VM::callFrameForThrowOffset()), callFrameRegister);
-    load64(Address(regT3, VM::vmEntryFrameForThrowOffset()), regT0);
-    store64(regT0, Address(regT3, VM::topVMEntryFrameOffset()));
 
     addPtr(TrustedImm32(stackPointerOffsetFor(codeBlock()) * sizeof(Register)), callFrameRegister, stackPointerRegister);
 
index b421a54..fd79478 100644 (file)
@@ -803,8 +803,6 @@ void JIT::emit_op_catch(Instruction* currentInstruction)
     move(TrustedImmPtr(m_vm), regT3);
     // operationThrow returns the callFrame for the handler.
     load32(Address(regT3, VM::callFrameForThrowOffset()), callFrameRegister);
-    load32(Address(regT3, VM::vmEntryFrameForThrowOffset()), regT0);
-    store32(regT0, Address(regT3, VM::topVMEntryFrameOffset()));
 
     addPtr(TrustedImm32(stackPointerOffsetFor(codeBlock()) * sizeof(Register)), callFrameRegister, stackPointerRegister);
 
index be3a9b0..d7dc508 100644 (file)
@@ -91,8 +91,7 @@ void JIT_OPERATION operationThrowStackOverflowError(ExecState* exec, CodeBlock*
         callerFrame = exec;
 
     NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
-    ErrorHandlingScope errorScope(*vm);
-    vm->throwException(callerFrame, createStackOverflowError(callerFrame));
+    throwStackOverflowError(callerFrame);
 }
 
 #if ENABLE(WEBASSEMBLY)
@@ -111,13 +110,12 @@ void JIT_OPERATION operationThrowDivideError(ExecState* exec)
 int32_t JIT_OPERATION operationCallArityCheck(ExecState* exec)
 {
     VM* vm = &exec->vm();
-    VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
-    CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
-
     JSStack& stack = vm->interpreter->stack();
 
     int32_t missingArgCount = CommonSlowPaths::arityCheckFor(exec, &stack, CodeForCall);
     if (missingArgCount < 0) {
+        VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
+        CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
         NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
         throwStackOverflowError(callerFrame);
     }
@@ -128,13 +126,12 @@ int32_t JIT_OPERATION operationCallArityCheck(ExecState* exec)
 int32_t JIT_OPERATION operationConstructArityCheck(ExecState* exec)
 {
     VM* vm = &exec->vm();
-    VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
-    CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
-
     JSStack& stack = vm->interpreter->stack();
 
     int32_t missingArgCount = CommonSlowPaths::arityCheckFor(exec, &stack, CodeForConstruct);
     if (missingArgCount < 0) {
+        VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
+        CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
         NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
         throwStackOverflowError(callerFrame);
     }
@@ -2036,7 +2033,7 @@ void JIT_OPERATION operationThrow(ExecState* exec, EncodedJSValue encodedExcepti
     JSValue exceptionValue = JSValue::decode(encodedExceptionValue);
     vm->throwException(exec, exceptionValue);
 
-    // Results stored out-of-band in vm.targetMachinePCForThrow, vm.callFrameForThrow & vm.vmEntryFrameForThrow
+    // Results stored out-of-band in vm.targetMachinePCForThrow & vm.callFrameForThrow
     genericUnwind(vm, exec);
 }
 
@@ -2082,12 +2079,8 @@ void JIT_OPERATION lookupExceptionHandler(VM* vm, ExecState* exec)
 
 void JIT_OPERATION lookupExceptionHandlerFromCallerFrame(VM* vm, ExecState* exec)
 {
-    VMEntryFrame* vmEntryFrame = vm->topVMEntryFrame;
-    CallFrame* callerFrame = exec->callerFrame(vmEntryFrame);
-    ASSERT(callerFrame);
-
-    NativeCallFrameTracerWithRestore tracer(vm, vmEntryFrame, callerFrame);
-    genericUnwind(vm, callerFrame);
+    NativeCallFrameTracer tracer(vm, exec);
+    genericUnwind(vm, exec, UnwindFromCallerFrame);
     ASSERT(vm->targetMachinePCForThrow);
 }
 
index 4d3dc87..082b173 100644 (file)
@@ -127,6 +127,7 @@ macro doVMEntry(makeCall)
     storep t4, VMEntryRecord::m_prevTopCallFrame[sp]
     loadp VM::topVMEntryFrame[vm], t4
     storep t4, VMEntryRecord::m_prevTopVMEntryFrame[sp]
+    storep cfr, VM::topVMEntryFrame[vm]
 
     # Align stack pointer
     if X86_WIN
@@ -235,7 +236,6 @@ macro doVMEntry(makeCall)
 
 .copyArgsDone:
     storep sp, VM::topCallFrame[vm]
-    storep cfr, VM::topVMEntryFrame[vm]
 
     makeCall(entry, t3, t4)
 
@@ -1879,8 +1879,6 @@ _llint_op_catch:
     andp MarkedBlockMask, t3
     loadp MarkedBlock::m_weakSet + WeakSet::m_vm[t3], t3
     loadp VM::callFrameForThrow[t3], cfr
-    loadp VM::vmEntryFrameForThrow[t3], t0
-    storep t0, VM::topVMEntryFrame[t3]
     restoreStackPointerAfterCall()
 
     loadi VM::targetInterpreterPCForThrow[t3], PC
index f80fb6f..f9a0308 100644 (file)
@@ -131,6 +131,7 @@ macro doVMEntry(makeCall)
     storep t4, VMEntryRecord::m_prevTopCallFrame[sp]
     loadp VM::topVMEntryFrame[vm], t4
     storep t4, VMEntryRecord::m_prevTopVMEntryFrame[sp]
+    storep cfr, VM::topVMEntryFrame[vm]
 
     loadi ProtoCallFrame::paddedArgCount[protoCallFrame], t4
     addp CallFrameHeaderSlots, t4, t4
@@ -213,7 +214,6 @@ macro doVMEntry(makeCall)
     else
         storep sp, VM::topCallFrame[vm]
     end
-    storep cfr, VM::topVMEntryFrame[vm]
 
     move TagTypeNumber, tagTypeNumber
     addp TagBitTypeOther, tagTypeNumber, tagMask
@@ -1775,8 +1775,6 @@ _llint_op_catch:
     andp MarkedBlockMask, t3
     loadp MarkedBlock::m_weakSet + WeakSet::m_vm[t3], t3
     loadp VM::callFrameForThrow[t3], cfr
-    loadp VM::vmEntryFrameForThrow[t3], t0
-    storep t0, VM::topVMEntryFrame[t3]
     restoreStackPointerAfterCall()
 
     loadp CodeBlock[cfr], PB
index 84a535a..2eb13d7 100644 (file)
@@ -377,16 +377,6 @@ public:
         return OBJECT_OFFSETOF(VM, m_exception);
     }
 
-    static ptrdiff_t vmEntryFrameForThrowOffset()
-    {
-        return OBJECT_OFFSETOF(VM, vmEntryFrameForThrow);
-    }
-
-    static ptrdiff_t topVMEntryFrameOffset()
-    {
-        return OBJECT_OFFSETOF(VM, topVMEntryFrame);
-    }
-
     static ptrdiff_t callFrameForThrowOffset()
     {
         return OBJECT_OFFSETOF(VM, callFrameForThrow);
@@ -451,7 +441,6 @@ public:
     JSValue hostCallReturnValue;
     unsigned varargsLength;
     ExecState* newCallFrameReturnValue;
-    VMEntryFrame* vmEntryFrameForThrow;
     ExecState* callFrameForThrow;
     void* targetMachinePCForThrow;
     Instruction* targetInterpreterPCForThrow;