JSMainThreadExecState::call() should clear exceptions before returning.
authormark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 Apr 2014 20:24:56 +0000 (20:24 +0000)
committermark.lam@apple.com <mark.lam@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 11 Apr 2014 20:24:56 +0000 (20:24 +0000)
<https://webkit.org/b/131530>

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

Added a version of JSC::call() that return any uncaught exception instead
of leaving it pending in the VM.

As part of this change, I updated various parts of the code base to use the
new API as needed.

* bindings/ScriptFunctionCall.cpp:
(Deprecated::ScriptFunctionCall::call):
- ScriptFunctionCall::call() is only used by the inspector to inject scripts.
  The injected scripts that will include Inspector scripts that should catch
  and handle any exceptions that were thrown.  We should not be seeing any
  exceptions returned from this call.  However, we do have checks for
  exceptions in case there are bugs in the Inspector scripts which allowed
  the exception to leak through.  Hence, it is proper to clear the exception
  here, and only record the fact that an exception was seen (if present).

* bindings/ScriptFunctionCall.h:
* inspector/InspectorEnvironment.h:
* runtime/CallData.cpp:
(JSC::call):
* runtime/CallData.h:

Source/WebCore:

Test: fast/dom/regress-131530.html

Previously, JSMainThreadExecState::call() did not clear any pending
exceptions in the VM before returning.  On returning, the
JSMainThreadExecState destructor may re-enter the VM to notify
MutationObservers.  This may result in a crash because the VM expects
exceptions to be cleared at entry.

We now change JSMainThreadExecState::call() to return the exception
(if present) via an argument, and clear it from the VM before returning.

As part of this change, I updated various parts of the code base to use the
new API as needed.

* bindings/js/JSCallbackData.cpp:
(WebCore::JSCallbackData::invokeCallback):
* bindings/js/JSCustomXPathNSResolver.cpp:
(WebCore::JSCustomXPathNSResolver::lookupNamespaceURI):
* bindings/js/JSDOMGlobalObjectTask.cpp:
- Assert that there's no unhandled exception after the Microtask returns.
  See comment for WebCore::JSMainThreadExecState::runTask below for more
  details.

* bindings/js/JSErrorHandler.cpp:
(WebCore::JSErrorHandler::handleEvent):
* bindings/js/JSEventListener.cpp:
(WebCore::JSEventListener::handleEvent):
* bindings/js/JSHTMLDocumentCustom.cpp:
(WebCore::JSHTMLDocument::open):
- Document.open() cannot be the first function on the JS stack.  Hence,
  there is no need to use JSMainThreadExecState to call into the VM, as
  this is only needed to catch the event of returning from the first
  function for the purpose of notifying MutationObservers.  Change to
  call JSC::call() directly.

* bindings/js/JSMainThreadExecState.cpp:
(WebCore::functionCallHandlerFromAnyThread):
* bindings/js/JSMainThreadExecState.h:
(WebCore::JSMainThreadExecState::call):
(WebCore::JSMainThreadExecState::evaluate):
- Remove the explicitly acquisition of the JSLock here because we now
  acquire the JSLock as part of the JSMainThreadExecState instance.
(WebCore::JSMainThreadExecState::runTask):
- Added an assert to verify that the task does not return with an
  unhandled exception.  Currently, the only Microtask in use is for the
  Promise implementation, which will eat the exception before returning.
  This assertion is added here to verify that this contract does not
  inadvertantly change in the future.
(WebCore::JSMainThreadExecState::JSMainThreadExecState):
- Now acquires the JSLock as well since by definition, we're only
  instantiating the JSMainThreadExecState because we're about to enter
  the VM.

* bindings/js/JSMutationCallback.cpp:
(WebCore::JSMutationCallback::call):
* bindings/js/JSNodeFilterCondition.cpp:
(WebCore::JSNodeFilterCondition::acceptNode):
- acceptNode() is only used in the TreeWalker and NodeIterator APIs which
  cannot be the first function on the JS stack.  Hence, we should call
  JSC::call() directly instead of going through JSMainThreadExecState.

* bindings/js/ScheduledAction.cpp:
(WebCore::ScheduledAction::executeFunctionInContext):
* bindings/objc/WebScriptObject.mm:
(WebCore::addExceptionToConsole):
(-[WebScriptObject callWebScriptMethod:withArguments:]):

LayoutTests:

* fast/dom/regress-131530-expected.txt: Added.
* fast/dom/regress-131530.html: Added.

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

22 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/regress-131530-expected.txt [new file with mode: 0644]
LayoutTests/fast/dom/regress-131530.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bindings/ScriptFunctionCall.cpp
Source/JavaScriptCore/bindings/ScriptFunctionCall.h
Source/JavaScriptCore/inspector/InspectorEnvironment.h
Source/JavaScriptCore/runtime/CallData.cpp
Source/JavaScriptCore/runtime/CallData.h
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSCallbackData.cpp
Source/WebCore/bindings/js/JSCustomXPathNSResolver.cpp
Source/WebCore/bindings/js/JSDOMGlobalObjectTask.cpp
Source/WebCore/bindings/js/JSErrorHandler.cpp
Source/WebCore/bindings/js/JSEventListener.cpp
Source/WebCore/bindings/js/JSHTMLDocumentCustom.cpp
Source/WebCore/bindings/js/JSMainThreadExecState.cpp
Source/WebCore/bindings/js/JSMainThreadExecState.h
Source/WebCore/bindings/js/JSMutationCallback.cpp
Source/WebCore/bindings/js/JSNodeFilterCondition.cpp
Source/WebCore/bindings/js/ScheduledAction.cpp
Source/WebCore/bindings/objc/WebScriptObject.mm

index d956841..8038edd 100644 (file)
@@ -1,3 +1,13 @@
+2014-04-11  Mark Lam  <mark.lam@apple.com>
+
+        JSMainThreadExecState::call() should clear exceptions before returning.
+        <https://webkit.org/b/131530>
+
+        Reviewed by Geoffrey Garen.
+
+        * fast/dom/regress-131530-expected.txt: Added.
+        * fast/dom/regress-131530.html: Added.
+
 2014-04-11  Carlos Alberto Lopez Perez  <clopez@igalia.com>
 
         [GTK] Unreviewed GTK gardening.
diff --git a/LayoutTests/fast/dom/regress-131530-expected.txt b/LayoutTests/fast/dom/regress-131530-expected.txt
new file mode 100644 (file)
index 0000000..81e8556
--- /dev/null
@@ -0,0 +1,5 @@
+CONSOLE MESSAGE: line 5: Exception to trigger unwinding in MutationObserver
+CONSOLE MESSAGE: Pending exception before MutationObservers are called.
+Regression test for https://webkit.org/b/131530. This test should not crash.
+
+Mutate that node
diff --git a/LayoutTests/fast/dom/regress-131530.html b/LayoutTests/fast/dom/regress-131530.html
new file mode 100644 (file)
index 0000000..6aecbb9
--- /dev/null
@@ -0,0 +1,29 @@
+<head>
+<script>
+var observer = new MutationObserver(function(mutations) {
+    function foo() {
+        throw "Exception to trigger unwinding in MutationObserver";
+    }
+    mutations.forEach(function(mutation) {
+        foo();
+    });
+});
+
+function test()
+{
+    if (window.testRunner)
+        testRunner.dumpAsText();
+
+    var node = document.getElementById('res');
+    var config = { attribute: true, childList: true, characterData: true };
+    observer.observe(node, config);
+
+    node.innerText += "Mutate that node";
+    throw "Pending exception before MutationObservers are called.";
+}
+</script>
+</head>
+<body onload="test();">
+<p>Regression test for https://webkit.org/b/131530. This test should not crash.<form>
+<div id="res"></div>
+</body>
index a822034..847dc18 100644 (file)
@@ -1,3 +1,32 @@
+2014-04-11  Mark Lam  <mark.lam@apple.com>
+
+        JSMainThreadExecState::call() should clear exceptions before returning.
+        <https://webkit.org/b/131530>
+
+        Reviewed by Geoffrey Garen.
+
+        Added a version of JSC::call() that return any uncaught exception instead
+        of leaving it pending in the VM.
+
+        As part of this change, I updated various parts of the code base to use the
+        new API as needed.
+
+        * bindings/ScriptFunctionCall.cpp:
+        (Deprecated::ScriptFunctionCall::call):
+        - ScriptFunctionCall::call() is only used by the inspector to inject scripts.
+          The injected scripts that will include Inspector scripts that should catch
+          and handle any exceptions that were thrown.  We should not be seeing any
+          exceptions returned from this call.  However, we do have checks for
+          exceptions in case there are bugs in the Inspector scripts which allowed
+          the exception to leak through.  Hence, it is proper to clear the exception
+          here, and only record the fact that an exception was seen (if present).
+
+        * bindings/ScriptFunctionCall.h:
+        * inspector/InspectorEnvironment.h:
+        * runtime/CallData.cpp:
+        (JSC::call):
+        * runtime/CallData.h:
+
 2014-04-11  Oliver Hunt  <oliver@apple.com>
 
         Add BuiltinLog function to make debugging builtins easier
index 5b58814..2df7876 100644 (file)
@@ -133,12 +133,13 @@ Deprecated::ScriptValue ScriptFunctionCall::call(bool& hadException)
         return Deprecated::ScriptValue();
 
     JSValue result;
+    JSValue exception;
     if (m_callHandler)
-        result = m_callHandler(m_exec, function, callType, callData, thisObject, m_arguments);
+        result = m_callHandler(m_exec, function, callType, callData, thisObject, m_arguments, &exception);
     else
-        result = JSC::call(m_exec, function, callType, callData, thisObject, m_arguments);
+        result = JSC::call(m_exec, function, callType, callData, thisObject, m_arguments, &exception);
 
-    if (m_exec->hadException()) {
+    if (exception) {
         hadException = true;
         return Deprecated::ScriptValue();
     }
index 04b2afe..7c3e78c 100644 (file)
@@ -71,7 +71,7 @@ private:
 
 class JS_EXPORT_PRIVATE ScriptFunctionCall : public ScriptCallArgumentHandler {
 public:
-    typedef JSC::JSValue (*ScriptFunctionCallHandler)(JSC::ExecState* exec, JSC::JSValue functionObject, JSC::CallType callType, const JSC::CallData& callData, JSC::JSValue thisValue, const JSC::ArgList& args);
+    typedef JSC::JSValue (*ScriptFunctionCallHandler)(JSC::ExecState* exec, JSC::JSValue functionObject, JSC::CallType callType, const JSC::CallData& callData, JSC::JSValue thisValue, const JSC::ArgList& args, JSC::JSValue* exception);
     ScriptFunctionCall(const ScriptObject& thisObject, const String& name, ScriptFunctionCallHandler handler = nullptr);
     ScriptValue call(bool& hadException);
     ScriptValue call();
index 26092d1..588d7a1 100644 (file)
@@ -34,7 +34,7 @@ class SourceCode;
 
 namespace Inspector {
 
-typedef JSC::JSValue (*InspectorFunctionCallHandler)(JSC::ExecState* exec, JSC::JSValue functionObject, JSC::CallType callType, const JSC::CallData& callData, JSC::JSValue thisValue, const JSC::ArgList& args);
+typedef JSC::JSValue (*InspectorFunctionCallHandler)(JSC::ExecState* exec, JSC::JSValue functionObject, JSC::CallType callType, const JSC::CallData& callData, JSC::JSValue thisValue, const JSC::ArgList& args, JSC::JSValue* exception);
 typedef JSC::JSValue (*InspectorEvaluateHandler)(JSC::ExecState*, const JSC::SourceCode&, JSC::JSValue thisValue, JSC::JSValue* exception);
 
 class InspectorEnvironment {
index c52b567..6d00109 100644 (file)
@@ -39,4 +39,17 @@ JSValue call(ExecState* exec, JSValue functionObject, CallType callType, const C
     return exec->interpreter()->executeCall(exec, asObject(functionObject), callType, callData, thisValue, args);
 }
 
+JSValue call(ExecState* exec, JSValue functionObject, CallType callType, const CallData& callData, JSValue thisValue, const ArgList& args, JSValue* exception)
+{
+    JSValue result = call(exec, functionObject, callType, callData, thisValue, args);
+    if (exec->hadException()) {
+        if (exception)
+            *exception = exec->exception();
+        exec->clearException();
+        return jsUndefined();
+    }
+    RELEASE_ASSERT(result);
+    return result;
+}
+
 } // namespace JSC
index f9d41e8..b6edd37 100644 (file)
@@ -58,6 +58,7 @@ union CallData {
 };
 
 JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&);
+JS_EXPORT_PRIVATE JSValue call(ExecState*, JSValue functionObject, CallType, const CallData&, JSValue thisValue, const ArgList&, JSValue* exception);
 
 } // namespace JSC
 
index fdc9d4d..f40130a 100644 (file)
@@ -1,3 +1,77 @@
+2014-04-11  Mark Lam  <mark.lam@apple.com>
+
+        JSMainThreadExecState::call() should clear exceptions before returning.
+        <https://webkit.org/b/131530>
+
+        Reviewed by Geoffrey Garen.
+
+        Test: fast/dom/regress-131530.html
+
+        Previously, JSMainThreadExecState::call() did not clear any pending
+        exceptions in the VM before returning.  On returning, the
+        JSMainThreadExecState destructor may re-enter the VM to notify
+        MutationObservers.  This may result in a crash because the VM expects
+        exceptions to be cleared at entry.
+
+        We now change JSMainThreadExecState::call() to return the exception
+        (if present) via an argument, and clear it from the VM before returning.
+
+        As part of this change, I updated various parts of the code base to use the
+        new API as needed.
+
+        * bindings/js/JSCallbackData.cpp:
+        (WebCore::JSCallbackData::invokeCallback):
+        * bindings/js/JSCustomXPathNSResolver.cpp:
+        (WebCore::JSCustomXPathNSResolver::lookupNamespaceURI):
+        * bindings/js/JSDOMGlobalObjectTask.cpp:
+        - Assert that there's no unhandled exception after the Microtask returns.
+          See comment for WebCore::JSMainThreadExecState::runTask below for more
+          details.
+
+        * bindings/js/JSErrorHandler.cpp:
+        (WebCore::JSErrorHandler::handleEvent):
+        * bindings/js/JSEventListener.cpp:
+        (WebCore::JSEventListener::handleEvent):
+        * bindings/js/JSHTMLDocumentCustom.cpp:
+        (WebCore::JSHTMLDocument::open):
+        - Document.open() cannot be the first function on the JS stack.  Hence,
+          there is no need to use JSMainThreadExecState to call into the VM, as
+          this is only needed to catch the event of returning from the first
+          function for the purpose of notifying MutationObservers.  Change to
+          call JSC::call() directly.
+
+        * bindings/js/JSMainThreadExecState.cpp:
+        (WebCore::functionCallHandlerFromAnyThread):
+        * bindings/js/JSMainThreadExecState.h:
+        (WebCore::JSMainThreadExecState::call):
+        (WebCore::JSMainThreadExecState::evaluate):
+        - Remove the explicitly acquisition of the JSLock here because we now
+          acquire the JSLock as part of the JSMainThreadExecState instance.
+        (WebCore::JSMainThreadExecState::runTask):
+        - Added an assert to verify that the task does not return with an
+          unhandled exception.  Currently, the only Microtask in use is for the
+          Promise implementation, which will eat the exception before returning.
+          This assertion is added here to verify that this contract does not
+          inadvertantly change in the future.
+        (WebCore::JSMainThreadExecState::JSMainThreadExecState):
+        - Now acquires the JSLock as well since by definition, we're only
+          instantiating the JSMainThreadExecState because we're about to enter
+          the VM.
+
+        * bindings/js/JSMutationCallback.cpp:
+        (WebCore::JSMutationCallback::call):
+        * bindings/js/JSNodeFilterCondition.cpp:
+        (WebCore::JSNodeFilterCondition::acceptNode):
+        - acceptNode() is only used in the TreeWalker and NodeIterator APIs which
+          cannot be the first function on the JS stack.  Hence, we should call
+          JSC::call() directly instead of going through JSMainThreadExecState.
+
+        * bindings/js/ScheduledAction.cpp:
+        (WebCore::ScheduledAction::executeFunctionInContext):
+        * bindings/objc/WebScriptObject.mm:
+        (WebCore::addExceptionToConsole):
+        (-[WebScriptObject callWebScriptMethod:withArguments:]):
+
 2014-04-11  Brian J. Burg  <burg@cs.washington.edu>
 
         Web Replay: CodeGeneratorJS should guard includes of replay-related headers
index 3be5e20..e1d725f 100644 (file)
@@ -73,14 +73,15 @@ JSValue JSCallbackData::invokeCallback(JSValue thisValue, MarkedArgumentBuffer&
 
     InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(context, callType, callData);
 
+    JSValue exception;
     JSValue result = context->isDocument()
-        ? JSMainThreadExecState::call(exec, function, callType, callData, thisValue, args)
-        : JSC::call(exec, function, callType, callData, thisValue, args);
+        ? JSMainThreadExecState::call(exec, function, callType, callData, thisValue, args, &exception)
+        : JSC::call(exec, function, callType, callData, thisValue, args, &exception);
 
     InspectorInstrumentation::didCallFunction(cookie, context);
 
-    if (exec->hadException()) {
-        reportCurrentException(exec);
+    if (exception) {
+        reportException(exec, exception);
         if (raisedException)
             *raisedException = true;
         return result;
index 0f52afa..fb2e641 100644 (file)
@@ -92,11 +92,12 @@ String JSCustomXPathNSResolver::lookupNamespaceURI(const String& prefix)
     MarkedArgumentBuffer args;
     args.append(jsStringWithCache(exec, prefix));
 
-    JSValue retval = JSMainThreadExecState::call(exec, function, callType, callData, m_customResolver.get(), args);
+    JSValue exception;
+    JSValue retval = JSMainThreadExecState::call(exec, function, callType, callData, m_customResolver.get(), args, &exception);
 
     String result;
-    if (exec->hadException())
-        reportCurrentException(exec);
+    if (exception)
+        reportException(exec, exception);
     else {
         if (!retval.isUndefinedOrNull())
             result = retval.toString(exec)->value(exec);
index 57291cc..9cca89b 100644 (file)
@@ -64,6 +64,7 @@ public:
             JSMainThreadExecState::runTask(exec, *m_task.get());
         else
             m_task->run(exec);
+        ASSERT(!exec->hadException());
     }
 
 private:
index d92c1b8..ea51f79 100644 (file)
@@ -98,14 +98,15 @@ void JSErrorHandler::handleEvent(ScriptExecutionContext* scriptExecutionContext,
         VM& vm = globalObject->vm();
         VMEntryScope entryScope(vm, vm.entryScope ? vm.entryScope->globalObject() : globalObject);
 
+        JSValue exception;
         JSValue returnValue = scriptExecutionContext->isDocument()
-            ? JSMainThreadExecState::call(exec, jsFunction, callType, callData, globalObject, args)
-            : JSC::call(exec, jsFunction, callType, callData, globalObject, args);
+            ? JSMainThreadExecState::call(exec, jsFunction, callType, callData, globalObject, args, &exception)
+            : JSC::call(exec, jsFunction, callType, callData, globalObject, args, &exception);
 
         globalObject->setCurrentEvent(savedEvent);
 
-        if (exec->hadException())
-            reportCurrentException(exec);
+        if (exception)
+            reportException(exec, exception);
         else {
             if (returnValue.isTrue())
                 event->preventDefault();
index 960b67e..6f35366 100644 (file)
@@ -122,9 +122,10 @@ void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext
         InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(scriptExecutionContext, callType, callData);
 
         JSValue thisValue = handleEventFunction == jsFunction ? toJS(exec, globalObject, event->currentTarget()) : jsFunction;
+        JSValue exception;
         JSValue retval = scriptExecutionContext->isDocument()
-            ? JSMainThreadExecState::call(exec, handleEventFunction, callType, callData, thisValue, args)
-            : JSC::call(exec, handleEventFunction, callType, callData, thisValue, args);
+            ? JSMainThreadExecState::call(exec, handleEventFunction, callType, callData, thisValue, args, &exception)
+            : JSC::call(exec, handleEventFunction, callType, callData, thisValue, args, &exception);
 
         InspectorInstrumentation::didCallFunction(cookie, scriptExecutionContext);
 
@@ -136,9 +137,9 @@ void JSEventListener::handleEvent(ScriptExecutionContext* scriptExecutionContext
                 toWorkerGlobalScope(scriptExecutionContext)->script()->forbidExecution();
         }
 
-        if (exec->hadException()) {
+        if (exception) {
             event->target()->uncaughtExceptionInEventHandler();
-            reportCurrentException(exec);
+            reportException(exec, exception);
         } else {
             if (!retval.isUndefinedOrNull() && event->isBeforeUnloadEvent())
                 toBeforeUnloadEvent(event)->setReturnValue(retval.toString(exec)->value(exec));
index 174e5f7..b6ebc4c 100644 (file)
@@ -113,7 +113,7 @@ JSValue JSHTMLDocument::open(ExecState* exec)
                 CallType callType = ::getCallData(function, callData);
                 if (callType == CallTypeNone)
                     return throwTypeError(exec);
-                return JSMainThreadExecState::call(exec, function, callType, callData, wrapper, ArgList(exec));
+                return JSC::call(exec, function, callType, callData, wrapper, ArgList(exec));
             }
         }
         return jsUndefined();
index 779b96a..06e92f2 100644 (file)
@@ -46,11 +46,11 @@ void JSMainThreadExecState::didLeaveScriptContext()
     MutationObserver::deliverAllMutations();
 }
 
-JSC::JSValue functionCallHandlerFromAnyThread(JSC::ExecState* exec, JSC::JSValue functionObject, JSC::CallType callType, const JSC::CallData& callData, JSC::JSValue thisValue, const JSC::ArgList& args)
+JSC::JSValue functionCallHandlerFromAnyThread(JSC::ExecState* exec, JSC::JSValue functionObject, JSC::CallType callType, const JSC::CallData& callData, JSC::JSValue thisValue, const JSC::ArgList& args, JSC::JSValue* exception)
 {
     if (isMainThread())
-        return JSMainThreadExecState::call(exec, functionObject, callType, callData, thisValue, args);
-    return JSC::call(exec, functionObject, callType, callData, thisValue, args);
+        return JSMainThreadExecState::call(exec, functionObject, callType, callData, thisValue, args, exception);
+    return JSC::call(exec, functionObject, callType, callData, thisValue, args, exception);
 }
 
 JSC::JSValue evaluateHandlerFromAnyThread(JSC::ExecState* exec, const JSC::SourceCode& source, JSC::JSValue thisValue, JSC::JSValue* exception)
index 29dd971..c157b0f 100644 (file)
@@ -50,16 +50,15 @@ public:
         return s_mainThreadState;
     };
     
-    static JSC::JSValue call(JSC::ExecState* exec, JSC::JSValue functionObject, JSC::CallType callType, const JSC::CallData& callData, JSC::JSValue thisValue, const JSC::ArgList& args)
+    static JSC::JSValue call(JSC::ExecState* exec, JSC::JSValue functionObject, JSC::CallType callType, const JSC::CallData& callData, JSC::JSValue thisValue, const JSC::ArgList& args, JSC::JSValue* exception)
     {
         JSMainThreadExecState currentState(exec);
-        return JSC::call(exec, functionObject, callType, callData, thisValue, args);
+        return JSC::call(exec, functionObject, callType, callData, thisValue, args, exception);
     };
 
     static JSC::JSValue evaluate(JSC::ExecState* exec, const JSC::SourceCode& source, JSC::JSValue thisValue, JSC::JSValue* exception)
     {
         JSMainThreadExecState currentState(exec);
-        JSC::JSLockHolder lock(exec);
         return JSC::evaluate(exec, source, thisValue, exception);
     };
 
@@ -74,6 +73,7 @@ public:
 private:
     explicit JSMainThreadExecState(JSC::ExecState* exec)
         : m_previousState(s_mainThreadState)
+        , m_lock(exec)
     {
         ASSERT(isMainThread());
         s_mainThreadState = exec;
@@ -82,6 +82,7 @@ private:
     ~JSMainThreadExecState()
     {
         ASSERT(isMainThread());
+        ASSERT(!s_mainThreadState->hadException());
 
         bool didExitJavaScript = s_mainThreadState && !m_previousState;
 
@@ -93,6 +94,7 @@ private:
 
     static JSC::ExecState* s_mainThreadState;
     JSC::ExecState* m_previousState;
+    JSC::JSLockHolder m_lock;
 
     static void didLeaveScriptContext();
 };
@@ -119,7 +121,7 @@ private:
     JSC::ExecState* m_previousState;
 };
 
-JSC::JSValue functionCallHandlerFromAnyThread(JSC::ExecState*, JSC::JSValue functionObject, JSC::CallType callType, const JSC::CallData& callData, JSC::JSValue thisValue, const JSC::ArgList& args);
+JSC::JSValue functionCallHandlerFromAnyThread(JSC::ExecState*, JSC::JSValue functionObject, JSC::CallType, const JSC::CallData&, JSC::JSValue thisValue, const JSC::ArgList& args, JSC::JSValue* exception);
 JSC::JSValue evaluateHandlerFromAnyThread(JSC::ExecState*, const JSC::SourceCode&, JSC::JSValue thisValue, JSC::JSValue* exception);
 
 } // namespace WebCore
index c26f38a..7eb357a 100644 (file)
@@ -87,12 +87,13 @@ void JSMutationCallback::call(const Vector<RefPtr<MutationRecord>>& mutations, M
 
     InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(context, callType, callData);
 
-    JSMainThreadExecState::call(exec, callback, callType, callData, jsObserver, args);
+    JSValue exception;
+    JSMainThreadExecState::call(exec, callback, callType, callData, jsObserver, args, &exception);
 
     InspectorInstrumentation::didCallFunction(cookie, context);
 
-    if (exec->hadException())
-        reportCurrentException(exec);
+    if (exception)
+        reportException(exec, exception);
 }
 
 } // namespace WebCore
index d1b4721..71726bb 100644 (file)
@@ -67,7 +67,7 @@ short JSNodeFilterCondition::acceptNode(JSC::ExecState* exec, Node* filterNode)
     if (exec->hadException())
         return NodeFilter::FILTER_REJECT;
 
-    JSValue result = JSMainThreadExecState::call(exec, filter, callType, callData, m_filter.get(), args);
+    JSValue result = JSC::call(exec, filter, callType, callData, m_filter.get(), args);
     if (exec->hadException())
         return NodeFilter::FILTER_REJECT;
 
index eb0e31a..f596371 100644 (file)
@@ -99,15 +99,16 @@ void ScheduledAction::executeFunctionInContext(JSGlobalObject* globalObject, JSV
 
     InspectorInstrumentationCookie cookie = JSMainThreadExecState::instrumentFunctionCall(context, callType, callData);
 
+    JSValue exception;
     if (context->isDocument())
-        JSMainThreadExecState::call(exec, m_function.get(), callType, callData, thisValue, args);
+        JSMainThreadExecState::call(exec, m_function.get(), callType, callData, thisValue, args, &exception);
     else
-        JSC::call(exec, m_function.get(), callType, callData, thisValue, args);
+        JSC::call(exec, m_function.get(), callType, callData, thisValue, args, &exception);
 
     InspectorInstrumentation::didCallFunction(cookie, context);
 
-    if (exec->hadException())
-        reportCurrentException(exec);
+    if (exception)
+        reportException(exec, exception);
 }
 
 void ScheduledAction::execute(Document* document)
index 0427cb6..c5cf37e 100644 (file)
@@ -120,12 +120,19 @@ id createJSWrapper(JSC::JSObject* object, PassRefPtr<JSC::Bindings::RootObject>
     return [[[WebScriptObject alloc] _initWithJSObject:object originRootObject:origin rootObject:root] autorelease];
 }
 
-static void addExceptionToConsole(ExecState* exec)
+static void addExceptionToConsole(ExecState* exec, JSC::JSValue& exception)
 {
     JSDOMWindow* window = asJSDOMWindow(exec->vmEntryGlobalObject());
-    if (!window || !exec->hadException())
+    if (!window || !exception)
         return;
-    reportCurrentException(exec);
+    reportException(exec, exception);
+}
+
+static void addExceptionToConsole(ExecState* exec)
+{
+    JSC::JSValue exception = exec->exception();
+    exec->clearException();
+    addExceptionToConsole(exec, exception);
 }
 
 } // namespace WebCore
@@ -334,12 +341,12 @@ static void getListFromNSArray(ExecState *exec, NSArray *array, RootObject* root
     if (![self _isSafeScript])
         return nil;
 
-    JSC::JSValue result = JSMainThreadExecState::call(exec, function, callType, callData, [self _imp], argList);
+    JSC::JSValue exception;
+    JSC::JSValue result = JSMainThreadExecState::call(exec, function, callType, callData, [self _imp], argList, &exception);
 
-    if (exec->hadException()) {
-        addExceptionToConsole(exec);
+    if (exception) {
+        addExceptionToConsole(exec, exception);
         result = jsUndefined();
-        exec->clearException();
     }
 
     // Convert and return the result of the function call.