Source/JavaScriptCore: Add LLINT and baseline JIT support for timing out scripts.
https://bugs.webkit.org/show_bug.cgi?id=114577.
Reviewed by Geoffrey Garen.
Introduces the new Watchdog class which is used to track script
execution time, and initiate script termination if needed.
* API/JSContextRef.cpp:
(internalScriptTimeoutCallback):
(JSContextGroupSetExecutionTimeLimit):
(JSContextGroupClearExecutionTimeLimit):
* API/JSContextRefPrivate.h:
- Added new script execution time limit APIs.
* API/tests/testapi.c:
(currentCPUTime):
(shouldTerminateCallback):
(cancelTerminateCallback):
(extendTerminateCallback):
(main):
- Added new API tests for script execution time limit.
* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
* JavaScriptCore.xcodeproj/project.pbxproj:
* Target.pri:
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitLoopHint):
- loop hints are needed for the llint as well. Hence, it will be
emitted unconditionally.
* interpreter/Interpreter.cpp:
(JSC::Interpreter::addStackTraceIfNecessary):
(JSC::Interpreter::throwException):
(JSC::Interpreter::execute):
(JSC::Interpreter::executeCall):
(JSC::Interpreter::executeConstruct):
- Added checks for script termination before entering script code.
* jit/JIT.cpp:
(JSC::JIT::emitWatchdogTimerCheck):
* jit/JIT.h:
(JSC::JIT::emit_op_loop_hint):
* jit/JITStubs.cpp:
(JSC::DEFINE_STUB_FUNCTION(void, handle_watchdog_timer)):
* jit/JITStubs.h:
* llint/LLIntExceptions.cpp:
(JSC::LLInt::doThrow):
- Factored out some common code from returnToThrow() and callToThrow().
(JSC::LLInt::returnToThrow):
(JSC::LLInt::callToThrow):
* llint/LLIntSlowPaths.cpp:
(JSC::LLInt::LLINT_SLOW_PATH_DECL(slow_path_handle_watchdog_timer)):
* llint/LLIntSlowPaths.h:
* llint/LowLevelInterpreter.asm:
* llint/LowLevelInterpreter32_64.asm:
* llint/LowLevelInterpreter64.asm:
* runtime/ExceptionHelpers.cpp:
(JSC::throwTerminatedExecutionException):
- Also removed the now unused InterruptedExecutionException.
* runtime/ExceptionHelpers.h:
* runtime/JSGlobalData.cpp:
(JSC::JSGlobalData::JSGlobalData):
* runtime/JSGlobalData.h:
- Added watchdog, and removed the now obsolete Terminator.
* runtime/Terminator.h: Removed.
* runtime/Watchdog.cpp: Added.
(JSC::Watchdog::Watchdog):
(JSC::Watchdog::~Watchdog):
(JSC::Watchdog::setTimeLimit):
(JSC::Watchdog::didFire):
(JSC::Watchdog::isEnabled):
(JSC::Watchdog::fire):
(JSC::Watchdog::arm):
(JSC::Watchdog::disarm):
(JSC::Watchdog::startCountdownIfNeeded):
(JSC::Watchdog::startCountdown):
(JSC::Watchdog::stopCountdown):
(JSC::Watchdog::Scope::Scope):
(JSC::Watchdog::Scope::~Scope):
* runtime/Watchdog.h: Added.
(Watchdog):
(JSC::Watchdog::didFire):
(JSC::Watchdog::timerDidFireAddress):
(JSC::Watchdog::isArmed):
(Watchdog::Scope):
* runtime/WatchdogMac.cpp: Added.
(JSC::Watchdog::initTimer):
(JSC::Watchdog::destroyTimer):
(JSC::Watchdog::startTimer):
(JSC::Watchdog::stopTimer):
* runtime/WatchdogNone.cpp: Added.
(JSC::Watchdog::initTimer):
(JSC::Watchdog::destroyTimer):
(JSC::Watchdog::startTimer):
(JSC::Watchdog::stopTimer):
Source/WebCore: Add LLINT and baseline JIT support for timing out scripts.
https://bugs.webkit.org/show_bug.cgi?id=114577.
Reviewed by Geoffrey Garen.
Replaced use of the obsolete JSGlobalData.terminator methods with the
JSGlobalData.watchdog equivalents.
* bindings/js/JSEventListener.cpp:
(WebCore::JSEventListener::handleEvent):
* bindings/js/SerializedScriptValue.cpp:
(WebCore::SerializedScriptValue::maybeThrowExceptionIfSerializationFailed):
* bindings/js/WorkerScriptController.cpp:
(WebCore::WorkerScriptController::evaluate):
(WebCore::WorkerScriptController::scheduleExecutionTermination):
(WebCore::WorkerScriptController::isExecutionTerminating):
Source/WTF: Added currentCPUTime() and currentCPUTimeMS().
https://bugs.webkit.org/show_bug.cgi?id=114577.
Reviewed by Geoffrey Garen.
The currentCPUTime() implementation came from the old TimeoutChecker.cpp.
* wtf/CurrentTime.cpp:
(WTF::currentCPUTime):
(WTF::currentCPUTimeMS):
* wtf/CurrentTime.h:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@148639 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/API/JSContextRef.cpp b/Source/JavaScriptCore/API/JSContextRef.cpp
index bc21876..c59a00a 100644
--- a/Source/JavaScriptCore/API/JSContextRef.cpp
+++ b/Source/JavaScriptCore/API/JSContextRef.cpp
@@ -79,6 +79,30 @@
wtfThreadData().setCurrentIdentifierTable(savedIdentifierTable);
}
+static bool internalScriptTimeoutCallback(ExecState* exec, void* callbackPtr, void* callbackData)
+{
+ JSShouldTerminateCallback callback = reinterpret_cast<JSShouldTerminateCallback>(callbackPtr);
+ JSContextRef contextRef = toRef(exec);
+ return callback(contextRef, callbackData);
+}
+
+void JSContextGroupSetExecutionTimeLimit(JSContextGroupRef group, double limit, JSShouldTerminateCallback callback, void* callbackData)
+{
+ JSGlobalData& globalData = *toJS(group);
+ APIEntryShim entryShim(&globalData);
+ Watchdog& watchdog = globalData.watchdog;
+ void* callbackPtr = reinterpret_cast<void*>(callback);
+ watchdog.setTimeLimit(globalData, limit, internalScriptTimeoutCallback, callbackPtr, callbackData);
+}
+
+void JSContextGroupClearExecutionTimeLimit(JSContextGroupRef group)
+{
+ JSGlobalData& globalData = *toJS(group);
+ APIEntryShim entryShim(&globalData);
+ Watchdog& watchdog = globalData.watchdog;
+ watchdog.setTimeLimit(globalData, std::numeric_limits<double>::infinity());
+}
+
// From the API's perspective, a global context remains alive iff it has been JSGlobalContextRetained.
JSGlobalContextRef JSGlobalContextCreate(JSClassRef globalObjectClass)
diff --git a/Source/JavaScriptCore/API/JSContextRefPrivate.h b/Source/JavaScriptCore/API/JSContextRefPrivate.h
index 4f77aea..8d7684a 100644
--- a/Source/JavaScriptCore/API/JSContextRefPrivate.h
+++ b/Source/JavaScriptCore/API/JSContextRefPrivate.h
@@ -55,6 +55,54 @@
*/
JS_EXPORT JSStringRef JSContextCreateBacktrace(JSContextRef ctx, unsigned maxStackSize) AVAILABLE_IN_WEBKIT_VERSION_4_0;
+
+/*!
+@typedef JSShouldTerminateCallback
+@abstract The callback invoked when script execution has exceeded the allowed
+ time limit previously specified via JSContextGroupSetExecutionTimeLimit.
+@param ctx The execution context to use.
+@param context User specified context data previously passed to
+ JSContextGroupSetExecutionTimeLimit.
+@discussion If you named your function Callback, you would declare it like this:
+
+ bool Callback(JSContextRef ctx, void* context);
+
+ If you return true, the timed out script will terminate.
+ If you return false, the script will run for another period of the allowed
+ time limit specified via JSContextGroupSetExecutionTimeLimit.
+
+ Within this callback function, you may call JSContextGroupSetExecutionTimeLimit
+ to set a new time limit, or JSContextGroupClearExecutionTimeLimit to cancel the
+ timeout.
+*/
+typedef bool
+(*JSShouldTerminateCallback) (JSContextRef ctx, void* context);
+
+/*!
+@function
+@abstract Sets the script execution time limit.
+@param group The JavaScript context group that this time limit applies to.
+@param limit The time limit of allowed script execution time in seconds.
+@param callback The callback function that will be invoked when the time limit
+ has been reached. This will give you a chance to decide if you want to
+ terminate the script or not. If you pass a NULL callback, the script will be
+ terminated unconditionally when the time limit has been reached.
+@param context User data that you can provide to be passed back to you
+ in your callback.
+
+ In order to guarantee that the execution time limit will take effect, you will
+ need to call JSContextGroupSetExecutionTimeLimit before you start executing
+ any scripts.
+*/
+JS_EXPORT void JSContextGroupSetExecutionTimeLimit(JSContextGroupRef, double limit, JSShouldTerminateCallback, void* context) AVAILABLE_IN_WEBKIT_VERSION_4_0;
+
+/*!
+@function
+@abstract Clears the script execution time limit.
+@param group The JavaScript context group that the time limit is cleared on.
+*/
+JS_EXPORT void JSContextGroupClearExecutionTimeLimit(JSContextGroupRef) AVAILABLE_IN_WEBKIT_VERSION_4_0;
+
#ifdef __cplusplus
}
#endif
diff --git a/Source/JavaScriptCore/API/tests/testapi.c b/Source/JavaScriptCore/API/tests/testapi.c
index ac61f3a..f286a81 100644
--- a/Source/JavaScriptCore/API/tests/testapi.c
+++ b/Source/JavaScriptCore/API/tests/testapi.c
@@ -33,6 +33,12 @@
#include <wtf/Assertions.h>
#include <wtf/UnusedParam.h>
+#if PLATFORM(MAC) || PLATFORM(IOS)
+#include <mach/mach.h>
+#include <mach/mach_time.h>
+#include <sys/time.h>
+#endif
+
#if OS(WINDOWS)
#include <windows.h>
#endif
@@ -1045,6 +1051,56 @@
val.name = "something";
}
+#if PLATFORM(MAC) || PLATFORM(IOS)
+static double currentCPUTime()
+{
+ mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT;
+ thread_basic_info_data_t info;
+
+ /* Get thread information */
+ mach_port_t threadPort = mach_thread_self();
+ thread_info(threadPort, THREAD_BASIC_INFO, (thread_info_t)(&info), &infoCount);
+ mach_port_deallocate(mach_task_self(), threadPort);
+
+ double time = info.user_time.seconds + info.user_time.microseconds / 1000000.;
+ time += info.system_time.seconds + info.system_time.microseconds / 1000000.;
+
+ return time;
+}
+
+bool shouldTerminateCallbackWasCalled = false;
+static bool shouldTerminateCallback(JSContextRef ctx, void* context)
+{
+ UNUSED_PARAM(ctx);
+ UNUSED_PARAM(context);
+ shouldTerminateCallbackWasCalled = true;
+ return true;
+}
+
+bool cancelTerminateCallbackWasCalled = false;
+static bool cancelTerminateCallback(JSContextRef ctx, void* context)
+{
+ UNUSED_PARAM(ctx);
+ UNUSED_PARAM(context);
+ cancelTerminateCallbackWasCalled = true;
+ return false;
+}
+
+int extendTerminateCallbackCalled = 0;
+static bool extendTerminateCallback(JSContextRef ctx, void* context)
+{
+ UNUSED_PARAM(context);
+ extendTerminateCallbackCalled++;
+ if (extendTerminateCallbackCalled == 1) {
+ JSContextGroupRef contextGroup = JSContextGetGroup(ctx);
+ JSContextGroupSetExecutionTimeLimit(contextGroup, 2, extendTerminateCallback, 0);
+ return false;
+ }
+ return true;
+}
+#endif /* PLATFORM(MAC) || PLATFORM(IOS) */
+
+
int main(int argc, char* argv[])
{
#if OS(WINDOWS)
@@ -1692,6 +1748,95 @@
free(scriptUTF8);
}
+#if PLATFORM(MAC) || PLATFORM(IOS)
+ /* Test script timeout: */
+ JSContextGroupSetExecutionTimeLimit(contextGroup, 1, shouldTerminateCallback, 0);
+ {
+ const char* loopForeverScript = "startTime = Date.now(); try { while (true) { if (Date.now() - startTime > 5000) break; } } catch(e) { }";
+ JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+ double startTime;
+ double endTime;
+ exception = NULL;
+ startTime = currentCPUTime();
+ v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+ endTime = currentCPUTime();
+
+ if (((endTime - startTime) < 2) && shouldTerminateCallbackWasCalled)
+ printf("PASS: script timed out as expected.\n");
+ else {
+ if (!((endTime - startTime) < 2))
+ printf("FAIL: script did not timed out as expected.\n");
+ if (!shouldTerminateCallbackWasCalled)
+ printf("FAIL: script timeout callback was not called.\n");
+ failed = true;
+ }
+
+ if (exception)
+ printf("PASS: TerminationExecutionException was not catchable.\n");
+ else {
+ printf("FAIL: TerminationExecutionException was caught.\n");
+ failed = true;
+ }
+ }
+
+ /* Test script timeout cancellation: */
+ JSContextGroupSetExecutionTimeLimit(contextGroup, 1, cancelTerminateCallback, 0);
+ {
+ const char* loopForeverScript = "startTime = Date.now(); try { while (true) { if (Date.now() - startTime > 5000) break; } } catch(e) { }";
+ JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+ double startTime;
+ double endTime;
+ exception = NULL;
+ startTime = currentCPUTime();
+ v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+ endTime = currentCPUTime();
+
+ if (((endTime - startTime) >= 2) && cancelTerminateCallbackWasCalled && !exception)
+ printf("PASS: script timeout was cancelled as expected.\n");
+ else {
+ if (((endTime - startTime) < 2) || exception)
+ printf("FAIL: script timeout was not cancelled.\n");
+ if (!cancelTerminateCallbackWasCalled)
+ printf("FAIL: script timeout callback was not called.\n");
+ failed = true;
+ }
+ }
+
+ /* Test script timeout extension: */
+ JSContextGroupSetExecutionTimeLimit(contextGroup, 1, extendTerminateCallback, 0);
+ {
+ const char* loopForeverScript = "startTime = Date.now(); try { while (true) { if (Date.now() - startTime > 5000) break; } } catch(e) { }";
+ JSStringRef script = JSStringCreateWithUTF8CString(loopForeverScript);
+ double startTime;
+ double endTime;
+ double deltaTime;
+ exception = NULL;
+ startTime = currentCPUTime();
+ v = JSEvaluateScript(context, script, NULL, NULL, 1, &exception);
+ endTime = currentCPUTime();
+ deltaTime = endTime - startTime;
+
+ if ((deltaTime >= 3) && (deltaTime < 5) && (extendTerminateCallbackCalled == 2) && exception)
+ printf("PASS: script timeout was extended as expected.\n");
+ else {
+ if (deltaTime < 2)
+ printf("FAIL: script timeout was not extended as expected.\n");
+ else if (deltaTime >= 5)
+ printf("FAIL: script did not timeout.\n");
+
+ if (extendTerminateCallbackCalled < 1)
+ printf("FAIL: script timeout callback was not called.\n");
+ if (extendTerminateCallbackCalled < 2)
+ printf("FAIL: script timeout callback was not called after timeout extension.\n");
+
+ if (!exception)
+ printf("FAIL: TerminationExecutionException was caught during timeout extension test.\n");
+
+ failed = true;
+ }
+ }
+#endif /* PLATFORM(MAC) || PLATFORM(IOS) */
+
// Clear out local variables pointing at JSObjectRefs to allow their values to be collected
function = NULL;
v = NULL;
diff --git a/Source/JavaScriptCore/CMakeLists.txt b/Source/JavaScriptCore/CMakeLists.txt
index faa02f5..d85c8d6 100644
--- a/Source/JavaScriptCore/CMakeLists.txt
+++ b/Source/JavaScriptCore/CMakeLists.txt
@@ -305,6 +305,8 @@
runtime/StructureRareData.cpp
runtime/StructureChain.cpp
runtime/SymbolTable.cpp
+ runtime/Watchdog.cpp
+ runtime/WatchdogNone.cpp
tools/CodeProfile.cpp
tools/CodeProfiling.cpp
diff --git a/Source/JavaScriptCore/ChangeLog b/Source/JavaScriptCore/ChangeLog
index 0d9de1c..43e817c 100644
--- a/Source/JavaScriptCore/ChangeLog
+++ b/Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,102 @@
+2013-04-17 Mark Lam <mark.lam@apple.com>
+
+ Add LLINT and baseline JIT support for timing out scripts.
+ https://bugs.webkit.org/show_bug.cgi?id=114577.
+
+ Reviewed by Geoffrey Garen.
+
+ Introduces the new Watchdog class which is used to track script
+ execution time, and initiate script termination if needed.
+
+ * API/JSContextRef.cpp:
+ (internalScriptTimeoutCallback):
+ (JSContextGroupSetExecutionTimeLimit):
+ (JSContextGroupClearExecutionTimeLimit):
+ * API/JSContextRefPrivate.h:
+ - Added new script execution time limit APIs.
+ * API/tests/testapi.c:
+ (currentCPUTime):
+ (shouldTerminateCallback):
+ (cancelTerminateCallback):
+ (extendTerminateCallback):
+ (main):
+ - Added new API tests for script execution time limit.
+ * CMakeLists.txt:
+ * GNUmakefile.list.am:
+ * JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+ * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * Target.pri:
+ * bytecompiler/BytecodeGenerator.cpp:
+ (JSC::BytecodeGenerator::emitLoopHint):
+ - loop hints are needed for the llint as well. Hence, it will be
+ emitted unconditionally.
+ * interpreter/Interpreter.cpp:
+ (JSC::Interpreter::addStackTraceIfNecessary):
+ (JSC::Interpreter::throwException):
+ (JSC::Interpreter::execute):
+ (JSC::Interpreter::executeCall):
+ (JSC::Interpreter::executeConstruct):
+ - Added checks for script termination before entering script code.
+ * jit/JIT.cpp:
+ (JSC::JIT::emitWatchdogTimerCheck):
+ * jit/JIT.h:
+ (JSC::JIT::emit_op_loop_hint):
+ * jit/JITStubs.cpp:
+ (JSC::DEFINE_STUB_FUNCTION(void, handle_watchdog_timer)):
+ * jit/JITStubs.h:
+ * llint/LLIntExceptions.cpp:
+ (JSC::LLInt::doThrow):
+ - Factored out some common code from returnToThrow() and callToThrow().
+ (JSC::LLInt::returnToThrow):
+ (JSC::LLInt::callToThrow):
+ * llint/LLIntSlowPaths.cpp:
+ (JSC::LLInt::LLINT_SLOW_PATH_DECL(slow_path_handle_watchdog_timer)):
+ * llint/LLIntSlowPaths.h:
+ * llint/LowLevelInterpreter.asm:
+ * llint/LowLevelInterpreter32_64.asm:
+ * llint/LowLevelInterpreter64.asm:
+ * runtime/ExceptionHelpers.cpp:
+ (JSC::throwTerminatedExecutionException):
+ - Also removed the now unused InterruptedExecutionException.
+ * runtime/ExceptionHelpers.h:
+ * runtime/JSGlobalData.cpp:
+ (JSC::JSGlobalData::JSGlobalData):
+ * runtime/JSGlobalData.h:
+ - Added watchdog, and removed the now obsolete Terminator.
+ * runtime/Terminator.h: Removed.
+ * runtime/Watchdog.cpp: Added.
+ (JSC::Watchdog::Watchdog):
+ (JSC::Watchdog::~Watchdog):
+ (JSC::Watchdog::setTimeLimit):
+ (JSC::Watchdog::didFire):
+ (JSC::Watchdog::isEnabled):
+ (JSC::Watchdog::fire):
+ (JSC::Watchdog::arm):
+ (JSC::Watchdog::disarm):
+ (JSC::Watchdog::startCountdownIfNeeded):
+ (JSC::Watchdog::startCountdown):
+ (JSC::Watchdog::stopCountdown):
+ (JSC::Watchdog::Scope::Scope):
+ (JSC::Watchdog::Scope::~Scope):
+ * runtime/Watchdog.h: Added.
+ (Watchdog):
+ (JSC::Watchdog::didFire):
+ (JSC::Watchdog::timerDidFireAddress):
+ (JSC::Watchdog::isArmed):
+ (Watchdog::Scope):
+ * runtime/WatchdogMac.cpp: Added.
+ (JSC::Watchdog::initTimer):
+ (JSC::Watchdog::destroyTimer):
+ (JSC::Watchdog::startTimer):
+ (JSC::Watchdog::stopTimer):
+ * runtime/WatchdogNone.cpp: Added.
+ (JSC::Watchdog::initTimer):
+ (JSC::Watchdog::destroyTimer):
+ (JSC::Watchdog::startTimer):
+ (JSC::Watchdog::stopTimer):
+
2013-04-14 Roger Fong <roger_fong@apple.com>
Unreviewed. VS2010 Windows build fix.
diff --git a/Source/JavaScriptCore/GNUmakefile.list.am b/Source/JavaScriptCore/GNUmakefile.list.am
index 8fc4c74..8cc9dc1 100644
--- a/Source/JavaScriptCore/GNUmakefile.list.am
+++ b/Source/JavaScriptCore/GNUmakefile.list.am
@@ -782,10 +782,12 @@
Source/JavaScriptCore/runtime/StructureTransitionTable.h \
Source/JavaScriptCore/runtime/SymbolTable.cpp \
Source/JavaScriptCore/runtime/SymbolTable.h \
- Source/JavaScriptCore/runtime/Terminator.h \
Source/JavaScriptCore/runtime/Tracing.h \
Source/JavaScriptCore/runtime/TypedArrayDescriptor.h \
Source/JavaScriptCore/runtime/Uint16WithFraction.h \
+ Source/JavaScriptCore/runtime/Watchdog.cpp \
+ Source/JavaScriptCore/runtime/Watchdog.h \
+ Source/JavaScriptCore/runtime/WatchdogNone.cpp \
Source/JavaScriptCore/runtime/WeakGCMap.h \
Source/JavaScriptCore/runtime/WeakRandom.h \
Source/JavaScriptCore/runtime/WriteBarrier.h \
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj
index 08cdd38..6bb0374 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj
+++ b/Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.vcproj
@@ -1370,11 +1370,19 @@
>
</File>
<File
- RelativePath="..\..\runtime\Terminator.h"
+ RelativePath="..\..\runtime\TypedArrayDescriptor.h"
>
</File>
<File
- RelativePath="..\..\runtime\TypedArrayDescriptor.h"
+ RelativePath="..\..\runtime\Watchdog.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\..\runtime\Watchdog.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\runtime\WatchdogNone.cpp"
>
</File>
<File
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
index 1e74b87..f4c630f 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
@@ -370,6 +370,8 @@
<ClCompile Include="..\runtime\StructureChain.cpp" />
<ClCompile Include="..\runtime\StructureRareData.cpp" />
<ClCompile Include="..\runtime\SymbolTable.cpp" />
+ <ClCompile Include="..\runtime\Watchdog.cpp" />
+ <ClCompile Include="..\runtime\WatchdogNone.cpp" />
<ClCompile Include="..\tools\CodeProfile.cpp" />
<ClCompile Include="..\tools\CodeProfiling.cpp" />
<ClCompile Include="..\yarr\YarrCanonicalizeUCS2.cpp" />
@@ -752,10 +754,10 @@
<ClInclude Include="..\runtime\StructureRareDataInlines.h" />
<ClInclude Include="..\runtime\StructureTransitionTable.h" />
<ClInclude Include="..\runtime\SymbolTable.h" />
- <ClInclude Include="..\runtime\Terminator.h" />
<ClInclude Include="..\runtime\Tracing.h" />
<ClInclude Include="..\runtime\TypedArrayDescriptor.h" />
<ClInclude Include="..\runtime\Uint16WithFraction.h" />
+ <ClInclude Include="..\runtime\Watchdog.h" />
<ClInclude Include="..\runtime\WeakGCMap.h" />
<ClInclude Include="..\runtime\WeakRandom.h" />
<ClInclude Include="..\runtime\WriteBarrier.h" />
diff --git a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
index 153f046..bb5a67e 100644
--- a/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
+++ b/Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj.filters
@@ -717,6 +717,12 @@
<ClCompile Include="..\runtime\SymbolTable.cpp">
<Filter>runtime</Filter>
</ClCompile>
+ <ClCompile Include="..\runtime\Watchdog.cpp">
+ <Filter>runtime</Filter>
+ </ClCompile>
+ <ClCompile Include="..\runtime\WatchdogNone.cpp">
+ <Filter>runtime</Filter>
+ </ClCompile>
<ClCompile Include="..\tools\CodeProfile.cpp">
<Filter>tools</Filter>
</ClCompile>
@@ -1790,9 +1796,6 @@
<ClInclude Include="..\runtime\SymbolTable.h">
<Filter>runtime</Filter>
</ClInclude>
- <ClInclude Include="..\runtime\Terminator.h">
- <Filter>runtime</Filter>
- </ClInclude>
<ClInclude Include="..\runtime\Tracing.h">
<Filter>runtime</Filter>
</ClInclude>
@@ -1802,6 +1805,9 @@
<ClInclude Include="..\runtime\Uint16WithFraction.h">
<Filter>runtime</Filter>
</ClInclude>
+ <ClInclude Include="..\runtime\Watchdog.h">
+ <Filter>runtime</Filter>
+ </ClInclude>
<ClInclude Include="..\runtime\WeakGCMap.h">
<Filter>runtime</Filter>
</ClInclude>
diff --git a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
index 82da87c..5326a0e 100644
--- a/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
+++ b/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
@@ -660,7 +660,6 @@
969A07990ED1D3AE00F1F681 /* Instruction.h in Headers */ = {isa = PBXBuildFile; fileRef = 969A07930ED1D3AE00F1F681 /* Instruction.h */; settings = {ATTRIBUTES = (Private, ); }; };
969A079A0ED1D3AE00F1F681 /* Opcode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 969A07940ED1D3AE00F1F681 /* Opcode.cpp */; };
969A079B0ED1D3AE00F1F681 /* Opcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 969A07950ED1D3AE00F1F681 /* Opcode.h */; settings = {ATTRIBUTES = (Private, ); }; };
- 971EDEA61169E0D3005E4262 /* Terminator.h in Headers */ = {isa = PBXBuildFile; fileRef = 97F6903A1169DF7F00A6BB46 /* Terminator.h */; settings = {ATTRIBUTES = (Private, ); }; };
978801401471AD920041B016 /* JSDateMath.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9788FC221471AD0C0068CE2D /* JSDateMath.cpp */; };
978801411471AD920041B016 /* JSDateMath.h in Headers */ = {isa = PBXBuildFile; fileRef = 9788FC231471AD0C0068CE2D /* JSDateMath.h */; settings = {ATTRIBUTES = (Private, ); }; };
A1712B3B11C7B212007A5315 /* RegExpCache.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A1712B3A11C7B212007A5315 /* RegExpCache.cpp */; };
@@ -871,6 +870,9 @@
FE4A331F15BD2E07006F54F3 /* VMInspector.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE4A331D15BD2E07006F54F3 /* VMInspector.cpp */; };
FE4A332015BD2E07006F54F3 /* VMInspector.h in Headers */ = {isa = PBXBuildFile; fileRef = FE4A331E15BD2E07006F54F3 /* VMInspector.h */; settings = {ATTRIBUTES = (Private, ); }; };
FED287B215EC9A5700DA8161 /* LLIntOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = FED287B115EC9A5700DA8161 /* LLIntOpcode.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ FED94F2E171E3E2300BE77A4 /* Watchdog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FED94F2B171E3E2300BE77A4 /* Watchdog.cpp */; };
+ FED94F2F171E3E2300BE77A4 /* Watchdog.h in Headers */ = {isa = PBXBuildFile; fileRef = FED94F2C171E3E2300BE77A4 /* Watchdog.h */; settings = {ATTRIBUTES = (Private, ); };};
+ FED94F30171E3E2300BE77A4 /* WatchdogMac.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FED94F2D171E3E2300BE77A4 /* WatchdogMac.cpp */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -1582,7 +1584,6 @@
969A09220ED1E09C00F1F681 /* Completion.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Completion.cpp; sourceTree = "<group>"; };
9788FC221471AD0C0068CE2D /* JSDateMath.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDateMath.cpp; sourceTree = "<group>"; };
9788FC231471AD0C0068CE2D /* JSDateMath.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSDateMath.h; sourceTree = "<group>"; };
- 97F6903A1169DF7F00A6BB46 /* Terminator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Terminator.h; sourceTree = "<group>"; };
A1712B3A11C7B212007A5315 /* RegExpCache.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RegExpCache.cpp; sourceTree = "<group>"; };
A1712B3E11C7B228007A5315 /* RegExpCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegExpCache.h; sourceTree = "<group>"; };
A1712B4011C7B235007A5315 /* RegExpKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RegExpKey.h; sourceTree = "<group>"; };
@@ -1813,6 +1814,9 @@
FE4A331D15BD2E07006F54F3 /* VMInspector.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = VMInspector.cpp; sourceTree = "<group>"; };
FE4A331E15BD2E07006F54F3 /* VMInspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VMInspector.h; sourceTree = "<group>"; };
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>"; };
+ FED94F2D171E3E2300BE77A4 /* WatchdogMac.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WatchdogMac.cpp; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -2594,11 +2598,13 @@
BC9041470EB9250900FE26FA /* StructureTransitionTable.h */,
0F919D2715856770004A4E7D /* SymbolTable.cpp */,
14A396A60CD2933100B5B4FF /* SymbolTable.h */,
- 97F6903A1169DF7F00A6BB46 /* Terminator.h */,
5D53726D0E1C546B0021E549 /* Tracing.d */,
5D53726E0E1C54880021E549 /* Tracing.h */,
0FEB3ECB16237F4700AB67AD /* TypedArrayDescriptor.h */,
866739D113BFDE710023D87C /* Uint16WithFraction.h */,
+ FED94F2B171E3E2300BE77A4 /* Watchdog.cpp */,
+ FED94F2C171E3E2300BE77A4 /* Watchdog.h */,
+ FED94F2D171E3E2300BE77A4 /* WatchdogMac.cpp */,
14BFCE6810CDB1FC00364CCE /* WeakGCMap.h */,
1420BE7A10AA6DDB00F455D2 /* WeakRandom.h */,
A7DCB77912E3D90500911940 /* WriteBarrier.h */,
@@ -3094,6 +3100,7 @@
0FC097A2146B28CC00CF2442 /* DFGThunks.h in Headers */,
0F3B3A2C15475002003ED0FF /* DFGValidate.h in Headers */,
0F2BDC471522802500CD8910 /* DFGValueRecoveryOverride.h in Headers */,
+ FED94F2F171E3E2300BE77A4 /* Watchdog.h in Headers */,
0F2BDC481522802900CD8910 /* DFGValueSource.h in Headers */,
0F620174143FCD330068B77C /* DFGVariableAccessData.h in Headers */,
0FDDBFB61666EEDA00C55FEF /* DFGVariableAccessDataDump.h in Headers */,
@@ -3376,7 +3383,6 @@
BC9041480EB9250900FE26FA /* StructureTransitionTable.h in Headers */,
BC18C46B0E16F5CD00B34460 /* SymbolTable.h in Headers */,
A784A26411D16622005776AC /* SyntaxChecker.h in Headers */,
- 971EDEA61169E0D3005E4262 /* Terminator.h in Headers */,
0F572D4F16879FDD00E57FBD /* ThunkGenerator.h in Headers */,
A7386556118697B400540279 /* ThunkGenerators.h in Headers */,
141448CD13A1783700F5BA1A /* TinyBloomFilter.h in Headers */,
@@ -4023,6 +4029,7 @@
0FF60AC316740F8800029779 /* ReduceWhitespace.cpp in Sources */,
14280841107EC0930013E7B2 /* RegExp.cpp in Sources */,
A1712B3B11C7B212007A5315 /* RegExpCache.cpp in Sources */,
+ FED94F30171E3E2300BE77A4 /* WatchdogMac.cpp in Sources */,
8642C510151C06A90046D4EF /* RegExpCachedResult.cpp in Sources */,
14280842107EC0930013E7B2 /* RegExpConstructor.cpp in Sources */,
8642C512151C083D0046D4EF /* RegExpMatchesArray.cpp in Sources */,
@@ -4060,6 +4067,7 @@
0FF42732158EBD58004CB9FF /* UDis86Disassembler.cpp in Sources */,
A76F279415F13C9600517D67 /* UnlinkedCodeBlock.cpp in Sources */,
FE4A331F15BD2E07006F54F3 /* VMInspector.cpp in Sources */,
+ FED94F2E171E3E2300BE77A4 /* Watchdog.cpp in Sources */,
0FC81516140511B500CFA603 /* VTableSpectrum.cpp in Sources */,
0F919D2515853CE0004A4E7D /* Watchpoint.cpp in Sources */,
14E84F9E14EE1ACC00D6D5D4 /* WeakBlock.cpp in Sources */,
diff --git a/Source/JavaScriptCore/Target.pri b/Source/JavaScriptCore/Target.pri
index 300be75..c74bc62 100644
--- a/Source/JavaScriptCore/Target.pri
+++ b/Source/JavaScriptCore/Target.pri
@@ -324,6 +324,8 @@
runtime/Structure.cpp \
runtime/StructureRareData.cpp \
runtime/SymbolTable.cpp \
+ runtime/Watchdog.cpp \
+ runtime/WatchdogNone.cpp \
tools/CodeProfile.cpp \
tools/CodeProfiling.cpp \
yarr/YarrJIT.cpp \
diff --git a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
index b4a7e9f..206e2c4 100644
--- a/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
+++ b/Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
@@ -655,9 +655,7 @@
void BytecodeGenerator::emitLoopHint()
{
-#if ENABLE(DFG_JIT)
emitOpcode(op_loop_hint);
-#endif
}
void BytecodeGenerator::retrieveLastBinaryOp(int& dstIndex, int& src1Index, int& src2Index)
diff --git a/Source/JavaScriptCore/interpreter/Interpreter.cpp b/Source/JavaScriptCore/interpreter/Interpreter.cpp
index e955428..2c939b2 100644
--- a/Source/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/Source/JavaScriptCore/interpreter/Interpreter.cpp
@@ -767,7 +767,7 @@
JSObject* errorObject = asObject(error);
JSGlobalObject* globalObject = 0;
- if (isTerminatedExecutionException(error) || isInterruptedExecutionException(error))
+ if (isTerminatedExecutionException(error))
globalObject = globalData->dynamicGlobalObject;
else
globalObject = errorObject->globalObject();
@@ -788,7 +788,7 @@
NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset)
{
CodeBlock* codeBlock = callFrame->codeBlock();
- bool isInterrupt = false;
+ bool isTermination = false;
ASSERT(!exceptionValue.isEmpty());
ASSERT(!exceptionValue.isCell() || exceptionValue.asCell());
@@ -810,7 +810,7 @@
addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source());
}
- isInterrupt = isInterruptedExecutionException(exception) || isTerminatedExecutionException(exception);
+ isTermination = isTerminatedExecutionException(exception);
} else {
if (!callFrame->globalData().exceptionStack.size()) {
Vector<StackFrame> stack;
@@ -827,7 +827,7 @@
// Calculate an exception handler vPC, unwinding call frames as necessary.
HandlerInfo* handler = 0;
- while (isInterrupt || !(handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset))) {
+ while (isTermination || !(handler = codeBlock->handlerForBytecodeOffset(bytecodeOffset))) {
if (!unwindCallFrame(callFrame, exceptionValue, bytecodeOffset, codeBlock)) {
if (LegacyProfiler* profiler = callFrame->globalData().enabledProfiler())
profiler->exceptionUnwind(callFrame);
@@ -1030,15 +1030,17 @@
// Execute the code:
JSValue result;
- {
+ if (LIKELY(!globalData.watchdog.didFire(newCallFrame))) {
SamplingTool::CallRecord callRecord(m_sampler.get());
+ Watchdog::Scope watchdogScope(globalData.watchdog);
#if ENABLE(LLINT_C_LOOP)
result = LLInt::CLoop::execute(newCallFrame, llint_program_prologue);
#elif ENABLE(JIT)
result = program->generatedJITCode().execute(&m_stack, newCallFrame, &globalData);
#endif // ENABLE(JIT)
- }
+ } else
+ result = throwTerminatedExecutionException(newCallFrame);
if (LegacyProfiler* profiler = globalData.enabledProfiler())
profiler->didExecute(callFrame, program->sourceURL(), program->lineNo());
@@ -1100,8 +1102,9 @@
profiler->willExecute(callFrame, function);
JSValue result;
- {
+ if (LIKELY(!globalData.watchdog.didFire(newCallFrame))) {
SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSCall);
+ Watchdog::Scope watchdogScope(globalData.watchdog);
// Execute the code:
if (isJSCall) {
@@ -1112,7 +1115,8 @@
#endif // ENABLE(JIT)
} else
result = JSValue::decode(callData.native.function(newCallFrame));
- }
+ } else
+ result = throwTerminatedExecutionException(newCallFrame);
if (LegacyProfiler* profiler = globalData.enabledProfiler())
profiler->didExecute(callFrame, function);
@@ -1175,8 +1179,9 @@
profiler->willExecute(callFrame, constructor);
JSValue result;
- {
+ if (LIKELY(!globalData.watchdog.didFire(newCallFrame))) {
SamplingTool::CallRecord callRecord(m_sampler.get(), !isJSConstruct);
+ Watchdog::Scope watchdogScope(globalData.watchdog);
// Execute the code.
if (isJSConstruct) {
@@ -1185,10 +1190,10 @@
#elif ENABLE(JIT)
result = constructData.js.functionExecutable->generatedJITCodeForConstruct().execute(&m_stack, newCallFrame, &globalData);
#endif // ENABLE(JIT)
- } else {
+ } else
result = JSValue::decode(constructData.native.function(newCallFrame));
- }
- }
+ } else
+ result = throwTerminatedExecutionException(newCallFrame);
if (LegacyProfiler* profiler = globalData.enabledProfiler())
profiler->didExecute(callFrame, constructor);
@@ -1272,15 +1277,17 @@
// Execute the code:
JSValue result;
- {
+ if (LIKELY(!globalData.watchdog.didFire(closure.newCallFrame))) {
SamplingTool::CallRecord callRecord(m_sampler.get());
-
+ Watchdog::Scope watchdogScope(globalData.watchdog);
+
#if ENABLE(LLINT_C_LOOP)
result = LLInt::CLoop::execute(closure.newCallFrame, llint_function_for_call_prologue);
#elif ENABLE(JIT)
result = closure.functionExecutable->generatedJITCodeForCall().execute(&m_stack, closure.newCallFrame, &globalData);
#endif // ENABLE(JIT)
- }
+ } else
+ result = throwTerminatedExecutionException(closure.newCallFrame);
if (LegacyProfiler* profiler = globalData.enabledProfiler())
profiler->didExecute(closure.oldCallFrame, closure.function);
@@ -1368,15 +1375,17 @@
// Execute the code:
JSValue result;
- {
+ if (LIKELY(!globalData.watchdog.didFire(newCallFrame))) {
SamplingTool::CallRecord callRecord(m_sampler.get());
-
+ Watchdog::Scope watchdogScope(globalData.watchdog);
+
#if ENABLE(LLINT_C_LOOP)
result = LLInt::CLoop::execute(newCallFrame, llint_eval_prologue);
#elif ENABLE(JIT)
result = eval->generatedJITCode().execute(&m_stack, newCallFrame, &globalData);
#endif // ENABLE(JIT)
- }
+ } else
+ result = throwTerminatedExecutionException(newCallFrame);
if (LegacyProfiler* profiler = globalData.enabledProfiler())
profiler->didExecute(callFrame, eval->sourceURL(), eval->lineNo());
diff --git a/Source/JavaScriptCore/jit/JIT.cpp b/Source/JavaScriptCore/jit/JIT.cpp
index b83393a..f05eb26 100644
--- a/Source/JavaScriptCore/jit/JIT.cpp
+++ b/Source/JavaScriptCore/jit/JIT.cpp
@@ -118,6 +118,17 @@
}
#endif
+void JIT::emitWatchdogTimerCheck()
+{
+ if (!m_globalData->watchdog.isEnabled())
+ return;
+
+ Jump skipCheck = branchTest8(Zero, AbsoluteAddress(m_globalData->watchdog.timerDidFireAddress()));
+ JITStubCall stubCall(this, cti_handle_watchdog_timer);
+ stubCall.call();
+ skipCheck.link(this);
+}
+
#define NEXT_OPCODE(name) \
m_bytecodeOffset += OPCODE_LENGTH(name); \
break;
diff --git a/Source/JavaScriptCore/jit/JIT.h b/Source/JavaScriptCore/jit/JIT.h
index d9c4436..e9cd1c8 100644
--- a/Source/JavaScriptCore/jit/JIT.h
+++ b/Source/JavaScriptCore/jit/JIT.h
@@ -858,7 +858,8 @@
#else
void emitOptimizationCheck(OptimizationCheckKind) { }
#endif
-
+ void emitWatchdogTimerCheck();
+
#ifndef NDEBUG
void printBytecodeOperandTypes(unsigned src1, unsigned src2);
#endif
@@ -945,6 +946,7 @@
inline void JIT::emit_op_loop_hint(Instruction*)
{
+ emitWatchdogTimerCheck();
emitOptimizationCheck(LoopOptimizationCheck);
}
diff --git a/Source/JavaScriptCore/jit/JITStubs.cpp b/Source/JavaScriptCore/jit/JITStubs.cpp
index 1712e5e..c6d48b8 100644
--- a/Source/JavaScriptCore/jit/JITStubs.cpp
+++ b/Source/JavaScriptCore/jit/JITStubs.cpp
@@ -1375,6 +1375,18 @@
return JSValue::encode(result);
}
+DEFINE_STUB_FUNCTION(void, handle_watchdog_timer)
+{
+ STUB_INIT_STACK_FRAME(stackFrame);
+ CallFrame* callFrame = stackFrame.callFrame;
+ JSGlobalData* globalData = stackFrame.globalData;
+ if (UNLIKELY(globalData->watchdog.didFire(callFrame))) {
+ globalData->exception = createTerminatedExecutionException(globalData);
+ VM_THROW_EXCEPTION_AT_END();
+ return;
+ }
+}
+
DEFINE_STUB_FUNCTION(void*, stack_check)
{
STUB_INIT_STACK_FRAME(stackFrame);
diff --git a/Source/JavaScriptCore/jit/JITStubs.h b/Source/JavaScriptCore/jit/JITStubs.h
index 5240485..2ea4fe5 100644
--- a/Source/JavaScriptCore/jit/JITStubs.h
+++ b/Source/JavaScriptCore/jit/JITStubs.h
@@ -406,6 +406,7 @@
int JIT_STUB cti_op_jgreatereq(STUB_ARGS_DECLARATION) WTF_INTERNAL;
int JIT_STUB cti_op_jtrue(STUB_ARGS_DECLARATION) WTF_INTERNAL;
void* JIT_STUB cti_op_load_varargs(STUB_ARGS_DECLARATION) WTF_INTERNAL;
+void JIT_STUB cti_handle_watchdog_timer(STUB_ARGS_DECLARATION) WTF_INTERNAL;
int JIT_STUB cti_has_property(STUB_ARGS_DECLARATION) WTF_INTERNAL;
void JIT_STUB cti_op_debug(STUB_ARGS_DECLARATION) WTF_INTERNAL;
void JIT_STUB cti_op_end(STUB_ARGS_DECLARATION) WTF_INTERNAL;
diff --git a/Source/JavaScriptCore/llint/LLIntExceptions.cpp b/Source/JavaScriptCore/llint/LLIntExceptions.cpp
index 0192070..cc8bd29 100644
--- a/Source/JavaScriptCore/llint/LLIntExceptions.cpp
+++ b/Source/JavaScriptCore/llint/LLIntExceptions.cpp
@@ -65,29 +65,31 @@
return LLInt::exceptionInstructions();
}
-Instruction* returnToThrow(ExecState* exec, Instruction* pc)
+static void doThrow(ExecState* exec, Instruction* pc)
{
JSGlobalData* globalData = &exec->globalData();
NativeCallFrameTracer tracer(globalData, exec);
-#if LLINT_SLOW_PATH_TRACING
- dataLog("Throwing exception ", globalData->exception, " (returnToThrow).\n");
-#endif
fixupPCforExceptionIfNeeded(exec);
genericThrow(globalData, exec, globalData->exception, pc - exec->codeBlock()->instructions().begin());
-
+}
+
+Instruction* returnToThrow(ExecState* exec, Instruction* pc)
+{
+#if LLINT_SLOW_PATH_TRACING
+ JSGlobalData* globalData = &exec->globalData();
+ dataLog("Throwing exception ", globalData->exception, " (returnToThrow).\n");
+#endif
+ doThrow(exec, pc);
return LLInt::exceptionInstructions();
}
void* callToThrow(ExecState* exec, Instruction* pc)
{
- JSGlobalData* globalData = &exec->globalData();
- NativeCallFrameTracer tracer(globalData, exec);
#if LLINT_SLOW_PATH_TRACING
+ JSGlobalData* globalData = &exec->globalData();
dataLog("Throwing exception ", globalData->exception, " (callToThrow).\n");
#endif
- fixupPCforExceptionIfNeeded(exec);
- genericThrow(globalData, exec, globalData->exception, pc - exec->codeBlock()->instructions().begin());
-
+ doThrow(exec, pc);
return LLInt::getCodePtr(llint_throw_during_call_trampoline);
}
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
index eba07a4..efe1792 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.cpp
@@ -1628,6 +1628,14 @@
LLINT_THROW(createTypeError(exec, LLINT_OP_C(1).jsValue().toString(exec)->value(exec)));
}
+LLINT_SLOW_PATH_DECL(slow_path_handle_watchdog_timer)
+{
+ LLINT_BEGIN_NO_SET_PC();
+ if (UNLIKELY(globalData.watchdog.didFire(exec)))
+ LLINT_THROW(createTerminatedExecutionException(&globalData));
+ LLINT_RETURN_TWO(0, exec);
+}
+
LLINT_SLOW_PATH_DECL(slow_path_debug)
{
LLINT_BEGIN();
diff --git a/Source/JavaScriptCore/llint/LLIntSlowPaths.h b/Source/JavaScriptCore/llint/LLIntSlowPaths.h
index b373794..b3974f3 100644
--- a/Source/JavaScriptCore/llint/LLIntSlowPaths.h
+++ b/Source/JavaScriptCore/llint/LLIntSlowPaths.h
@@ -204,6 +204,7 @@
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_push_name_scope);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_throw);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_throw_static_error);
+LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_handle_watchdog_timer);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_debug);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_profile_will_call);
LLINT_SLOW_PATH_HIDDEN_DECL(slow_path_profile_did_call);
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
index 4758f12..341b3b6 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
@@ -1057,9 +1057,15 @@
_llint_op_loop_hint:
traceExecution()
+ loadp JITStackFrame::globalData[sp], t1
+ loadb JSGlobalData::watchdog+Watchdog::m_timerDidFire[t1], t0
+ btbnz t0, .handleWatchdogTimer
+.afterWatchdogTimerCheck:
checkSwitchToJITForLoop()
dispatch(1)
-
+.handleWatchdogTimer:
+ callWatchdogTimerHandler()
+ jmp .afterWatchdogTimerCheck
_llint_op_switch_string:
traceExecution()
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
index 6fe54b3..34fca19 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
@@ -185,6 +185,14 @@
action(t0)
end
+macro callWatchdogTimerHandler()
+ storei PC, ArgumentCount + TagOffset[cfr]
+ cCall2(_llint_slow_path_handle_watchdog_timer, cfr, PC)
+ move t1, cfr
+ btpnz t0, _llint_throw_from_slow_path_trampoline
+ loadi ArgumentCount + TagOffset[cfr], PC
+end
+
macro checkSwitchToJITForLoop()
checkSwitchToJIT(
1,
diff --git a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
index d69febf..d3c333f 100644
--- a/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
+++ b/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
@@ -126,6 +126,16 @@
action(t0)
end
+macro callWatchdogTimerHandler()
+ storei PC, ArgumentCount + TagOffset[cfr]
+ prepareStateForCCall()
+ cCall2(_llint_slow_path_handle_watchdog_timer, cfr, PC)
+ move t1, cfr
+ btpnz t0, _llint_throw_from_slow_path_trampoline
+ move t3, PB
+ loadi ArgumentCount + TagOffset[cfr], PC
+end
+
macro checkSwitchToJITForLoop()
checkSwitchToJIT(
1,
diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
index 0dd4f07..4e945c0 100644
--- a/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
+++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.cpp
@@ -41,33 +41,6 @@
namespace JSC {
-ASSERT_HAS_TRIVIAL_DESTRUCTOR(InterruptedExecutionError);
-
-const ClassInfo InterruptedExecutionError::s_info = { "InterruptedExecutionError", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(InterruptedExecutionError) };
-
-JSValue InterruptedExecutionError::defaultValue(const JSObject*, ExecState* exec, PreferredPrimitiveType hint)
-{
- if (hint == PreferString)
- return jsNontrivialString(exec, String(ASCIILiteral("JavaScript execution exceeded timeout.")));
- return JSValue(QNaN);
-}
-
-JSObject* createInterruptedExecutionException(JSGlobalData* globalData)
-{
- return InterruptedExecutionError::create(*globalData);
-}
-
-bool isInterruptedExecutionException(JSObject* object)
-{
- return object->inherits(&InterruptedExecutionError::s_info);
-}
-
-bool isInterruptedExecutionException(JSValue value)
-{
- return value.inherits(&InterruptedExecutionError::s_info);
-}
-
-
ASSERT_HAS_TRIVIAL_DESTRUCTOR(TerminatedExecutionError);
const ClassInfo TerminatedExecutionError::s_info = { "TerminatedExecutionError", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(TerminatedExecutionError) };
@@ -168,4 +141,10 @@
return throwError(exec, createStackOverflowError(exec));
}
+JSObject* throwTerminatedExecutionException(ExecState* exec)
+{
+ Interpreter::ErrorHandlingMode mode(exec);
+ return throwError(exec, createTerminatedExecutionException(&exec->globalData()));
+}
+
} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/ExceptionHelpers.h b/Source/JavaScriptCore/runtime/ExceptionHelpers.h
index d2daaa0..14aa3b4 100644
--- a/Source/JavaScriptCore/runtime/ExceptionHelpers.h
+++ b/Source/JavaScriptCore/runtime/ExceptionHelpers.h
@@ -33,10 +33,6 @@
namespace JSC {
-JS_EXPORT_PRIVATE JSObject* createInterruptedExecutionException(JSGlobalData*);
-bool isInterruptedExecutionException(JSObject*);
-bool isInterruptedExecutionException(JSValue);
-
JSObject* createTerminatedExecutionException(JSGlobalData*);
bool isTerminatedExecutionException(JSObject*);
JS_EXPORT_PRIVATE bool isTerminatedExecutionException(JSValue);
@@ -53,35 +49,9 @@
JSObject* throwOutOfMemoryError(ExecState*);
JSObject* throwStackOverflowError(ExecState*);
+JSObject* throwTerminatedExecutionException(ExecState*);
-class InterruptedExecutionError : public JSNonFinalObject {
-private:
- InterruptedExecutionError(JSGlobalData& globalData)
- : JSNonFinalObject(globalData, globalData.interruptedExecutionErrorStructure.get())
- {
- }
-
- static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType);
-
-public:
- typedef JSNonFinalObject Base;
-
- static InterruptedExecutionError* create(JSGlobalData& globalData)
- {
- InterruptedExecutionError* error = new (NotNull, allocateCell<InterruptedExecutionError>(globalData.heap)) InterruptedExecutionError(globalData);
- error->finishCreation(globalData);
- return error;
- }
-
- static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
- {
- return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
- }
-
- static const ClassInfo s_info;
-};
-
class TerminatedExecutionError : public JSNonFinalObject {
private:
TerminatedExecutionError(JSGlobalData& globalData)
diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.cpp b/Source/JavaScriptCore/runtime/JSGlobalData.cpp
index cdc42ab..2e7240b 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalData.cpp
+++ b/Source/JavaScriptCore/runtime/JSGlobalData.cpp
@@ -204,7 +204,6 @@
structureStructure.set(*this, Structure::createStructure(*this));
structureRareDataStructure.set(*this, StructureRareData::createStructure(*this, 0, jsNull()));
debuggerActivationStructure.set(*this, DebuggerActivation::createStructure(*this, 0, jsNull()));
- interruptedExecutionErrorStructure.set(*this, InterruptedExecutionError::createStructure(*this, 0, jsNull()));
terminatedExecutionErrorStructure.set(*this, TerminatedExecutionError::createStructure(*this, 0, jsNull()));
stringStructure.set(*this, JSString::createStructure(*this, 0, jsNull()));
notAnObjectStructure.set(*this, JSNotAnObject::createStructure(*this, 0, jsNull()));
diff --git a/Source/JavaScriptCore/runtime/JSGlobalData.h b/Source/JavaScriptCore/runtime/JSGlobalData.h
index efac642..edd067e 100644
--- a/Source/JavaScriptCore/runtime/JSGlobalData.h
+++ b/Source/JavaScriptCore/runtime/JSGlobalData.h
@@ -46,9 +46,9 @@
#include "PrototypeMap.h"
#include "SmallStrings.h"
#include "Strong.h"
-#include "Terminator.h"
#include "ThunkGenerators.h"
#include "TypedArrayDescriptor.h"
+#include "Watchdog.h"
#include "WeakRandom.h"
#include <wtf/BumpPointerAllocator.h>
#include <wtf/Forward.h>
@@ -211,6 +211,7 @@
GlobalDataType globalDataType;
ClientData* clientData;
ExecState* topCallFrame;
+ Watchdog watchdog;
const HashTable* arrayConstructorTable;
const HashTable* arrayPrototypeTable;
@@ -233,7 +234,6 @@
Strong<Structure> structureStructure;
Strong<Structure> structureRareDataStructure;
Strong<Structure> debuggerActivationStructure;
- Strong<Structure> interruptedExecutionErrorStructure;
Strong<Structure> terminatedExecutionErrorStructure;
Strong<Structure> stringStructure;
Strong<Structure> notAnObjectStructure;
@@ -325,8 +325,6 @@
#endif
NativeExecutable* getHostFunction(NativeFunction, NativeFunction constructor);
- Terminator terminator;
-
JSValue exception;
RefCountedArray<StackFrame> exceptionStack;
diff --git a/Source/JavaScriptCore/runtime/Terminator.h b/Source/JavaScriptCore/runtime/Terminator.h
deleted file mode 100644
index 6b0f236..0000000
--- a/Source/JavaScriptCore/runtime/Terminator.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2010 Google 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.
- * 3. Neither the name of Google Inc. ("Google") nor the names of
- * its contributors may be used to endorse or promote products derived
- * from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE 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 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 Terminator_h
-#define Terminator_h
-
-namespace JSC {
-
-class Terminator {
-public:
- Terminator() : m_shouldTerminate(false) { }
-
- void terminateSoon() { m_shouldTerminate = true; }
- bool shouldTerminate() const { return m_shouldTerminate; }
-
-private:
- bool m_shouldTerminate;
-};
-
-} // namespace JSC
-
-#endif // Terminator_h
diff --git a/Source/JavaScriptCore/runtime/Watchdog.cpp b/Source/JavaScriptCore/runtime/Watchdog.cpp
new file mode 100644
index 0000000..823904b
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/Watchdog.cpp
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2013 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. ``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
+ * 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 "Watchdog.h"
+
+#include "CallFrame.h"
+#include <wtf/CurrentTime.h>
+#include <wtf/MathExtras.h>
+
+namespace JSC {
+
+static const double noLimit = std::numeric_limits<double>::infinity();
+
+Watchdog::Watchdog()
+ : m_timerDidFire(false)
+ , m_didFire(false)
+ , m_limit(noLimit)
+ , m_startTime(0)
+ , m_elapsedTime(0)
+ , m_reentryCount(0)
+ , m_isStopped(true)
+ , m_callback(0)
+ , m_callbackData1(0)
+ , m_callbackData2(0)
+{
+ initTimer();
+}
+
+Watchdog::~Watchdog()
+{
+ ASSERT(!isArmed());
+ stopCountdown();
+ destroyTimer();
+}
+
+void Watchdog::setTimeLimit(JSGlobalData& globalData, double limit,
+ ShouldTerminateCallback callback, void* data1, void* data2)
+{
+ bool wasEnabled = isEnabled();
+
+ if (!m_isStopped)
+ stopCountdown();
+
+ m_didFire = false; // Reset the watchdog.
+
+ m_limit = limit;
+ m_callback = callback;
+ m_callbackData1 = data1;
+ m_callbackData2 = data2;
+
+ // If this is the first time that timeout is being enabled, then any
+ // previously JIT compiled code will not have the needed polling checks.
+ // Hence, we need to flush all the pre-existing compiled code.
+ //
+ // However, if the timeout is already enabled, and we're just changing the
+ // timeout value, then any existing JITted code will have the appropriate
+ // polling checks. Hence, there is no need to re-do this flushing.
+ if (!wasEnabled) {
+ // For now, we only support this feature when the DFG JIT is disabled.
+ Options::useDFGJIT() = false;
+
+ // And if we've previously compiled any functions, we need to deopt
+ // them because they don't habe the needed polling checks yet.
+ globalData.releaseExecutableMemory();
+ }
+
+ startCountdownIfNeeded();
+}
+
+bool Watchdog::didFire(ExecState* exec)
+{
+ if (m_didFire)
+ return true;
+
+ if (!m_timerDidFire)
+ return false;
+ m_timerDidFire = false;
+ stopCountdown();
+
+ double currentTime = currentCPUTime();
+ double deltaTime = currentTime - m_startTime;
+ double totalElapsedTime = m_elapsedTime + deltaTime;
+ if (totalElapsedTime > m_limit) {
+ // Case 1: the allowed CPU time has elapsed.
+
+ // If m_callback is not set, then we terminate by default.
+ // Else, we let m_callback decide if we should terminate or not.
+ bool needsTermination = !m_callback
+ || m_callback(exec, m_callbackData1, m_callbackData2);
+ if (needsTermination) {
+ m_didFire = true;
+ return true;
+ }
+
+ // The m_callback may have set a new limit. So, we may need to restart
+ // the countdown.
+ startCountdownIfNeeded();
+
+ } else {
+ // Case 2: the allowed CPU time has NOT elapsed.
+
+ // Tell the timer to alarm us again when it thinks we've reached the
+ // end of the allowed time.
+ double remainingTime = m_limit - totalElapsedTime;
+ m_elapsedTime = totalElapsedTime;
+ m_startTime = currentTime;
+ startCountdown(remainingTime);
+ }
+
+ return false;
+}
+
+bool Watchdog::isEnabled()
+{
+ return (m_limit != noLimit);
+}
+
+void Watchdog::fire()
+{
+ m_didFire = true;
+}
+
+void Watchdog::arm()
+{
+ m_reentryCount++;
+ if (m_reentryCount == 1)
+ startCountdownIfNeeded();
+}
+
+void Watchdog::disarm()
+{
+ ASSERT(m_reentryCount > 0);
+ if (m_reentryCount == 1)
+ stopCountdown();
+ m_reentryCount--;
+}
+
+void Watchdog::startCountdownIfNeeded()
+{
+ if (!m_isStopped)
+ return; // Already started.
+
+ if (!isArmed())
+ return; // Not executing JS script. No need to start.
+
+ if (isEnabled()) {
+ m_elapsedTime = 0;
+ m_startTime = currentCPUTime();
+ startCountdown(m_limit);
+ }
+}
+
+void Watchdog::startCountdown(double limit)
+{
+ ASSERT(m_isStopped);
+ m_isStopped = false;
+ startTimer(limit);
+}
+
+void Watchdog::stopCountdown()
+{
+ if (m_isStopped)
+ return;
+ stopTimer();
+ m_isStopped = true;
+}
+
+Watchdog::Scope::Scope(Watchdog& watchdog)
+ : m_watchdog(watchdog)
+{
+ m_watchdog.arm();
+}
+
+Watchdog::Scope::~Scope()
+{
+ m_watchdog.disarm();
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/Watchdog.h b/Source/JavaScriptCore/runtime/Watchdog.h
new file mode 100644
index 0000000..4bb18d1
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/Watchdog.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2013 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. ``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
+ * 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 Watchdog_h
+#define Watchdog_h
+
+#if PLATFORM(MAC) || PLATFORM(IOS)
+#include <dispatch/dispatch.h>
+#endif
+
+namespace JSC {
+
+class ExecState;
+class JSGlobalData;
+
+class Watchdog {
+public:
+ class Scope;
+
+ Watchdog();
+ ~Watchdog();
+
+ typedef bool (*ShouldTerminateCallback)(ExecState*, void* data1, void* data2);
+ void setTimeLimit(JSGlobalData&, double seconds, ShouldTerminateCallback = 0, void* data1 = 0, void* data2 = 0);
+
+ // This version of didFire() will check the elapsed CPU time and call the
+ // callback (if needed) to determine if the watchdog should fire.
+ bool didFire(ExecState*);
+
+ bool isEnabled();
+
+ // This version of didFire() is a more efficient version for when we want
+ // to know if the watchdog has fired in the past, and not whether it should
+ // fire right now.
+ JS_EXPORT_PRIVATE bool didFire() { return m_didFire; }
+ JS_EXPORT_PRIVATE void fire();
+
+ void* timerDidFireAddress() { return &m_timerDidFire; }
+
+private:
+ void arm();
+ void disarm();
+ void startCountdownIfNeeded();
+ void startCountdown(double limit);
+ void stopCountdown();
+ bool isArmed() { return !!m_reentryCount; }
+
+ // Platform specific timer implementation:
+ void initTimer();
+ void destroyTimer();
+ void startTimer(double limit);
+ void stopTimer();
+
+ // m_timerDidFire (above) indicates whether the timer fired. The Watchdog
+ // still needs to check if the allowed CPU time has elapsed. If so, then
+ // the Watchdog fires and m_didFire will be set.
+ // NOTE: m_timerDidFire is only set by the platform specific timer
+ // (probably from another thread) but is only cleared in the script thread.
+ bool m_timerDidFire;
+ bool m_didFire;
+
+ // All time units are in seconds.
+ double m_limit;
+ double m_startTime;
+ double m_elapsedTime;
+
+ int m_reentryCount;
+ bool m_isStopped;
+
+ ShouldTerminateCallback m_callback;
+ void* m_callbackData1;
+ void* m_callbackData2;
+
+#if PLATFORM(MAC) || PLATFORM(IOS)
+ dispatch_queue_t m_queue;
+ dispatch_source_t m_timer;
+#endif
+
+ friend class Watchdog::Scope;
+ friend class LLIntOffsetsExtractor;
+};
+
+class Watchdog::Scope {
+public:
+ Scope(Watchdog&);
+ ~Scope();
+
+private:
+ Watchdog& m_watchdog;
+};
+
+} // namespace JSC
+
+#endif // Watchdog_h
diff --git a/Source/JavaScriptCore/runtime/WatchdogMac.cpp b/Source/JavaScriptCore/runtime/WatchdogMac.cpp
new file mode 100644
index 0000000..ca474df
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/WatchdogMac.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2013 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. ``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
+ * 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 "Watchdog.h"
+
+namespace JSC {
+
+void Watchdog::initTimer()
+{
+ m_queue = 0;
+ m_timer = 0;
+}
+
+void Watchdog::destroyTimer()
+{
+ ASSERT(!m_timer);
+ if (m_queue)
+ dispatch_release(m_queue);
+}
+
+void Watchdog::startTimer(double limit)
+{
+ ASSERT(!m_timer);
+ if (!m_queue)
+ m_queue = dispatch_queue_create("jsc.watchdog.queue", 0);
+ m_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, m_queue);
+
+ dispatch_source_set_timer(m_timer,
+ dispatch_time(DISPATCH_TIME_NOW, limit * NSEC_PER_SEC),
+ DISPATCH_TIME_FOREVER, 0);
+
+ dispatch_source_set_event_handler(m_timer, ^{
+ m_timerDidFire = true;
+ });
+
+ dispatch_resume(m_timer);
+}
+
+void Watchdog::stopTimer()
+{
+ ASSERT(m_queue);
+ dispatch_sync(m_queue, ^{
+ dispatch_source_cancel(m_timer);
+ });
+ dispatch_release(m_timer);
+ m_timer = 0;
+}
+
+} // namespace JSC
diff --git a/Source/JavaScriptCore/runtime/WatchdogNone.cpp b/Source/JavaScriptCore/runtime/WatchdogNone.cpp
new file mode 100644
index 0000000..615314b
--- /dev/null
+++ b/Source/JavaScriptCore/runtime/WatchdogNone.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 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. ``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
+ * 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 "Watchdog.h"
+
+namespace JSC {
+
+// This is a stub for platforms that have not implemented this functionality.
+// In this case, the platform timer here never fires.
+
+void Watchdog::initTimer()
+{
+}
+
+void Watchdog::destroyTimer()
+{
+}
+
+void Watchdog::startTimer(double)
+{
+}
+
+void Watchdog::stopTimer()
+{
+}
+
+} // namespace JSC