Bug 18626: SQUIRRELFISH: support the "slow script" dialog <https://bugs.webkit.org...
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 28 Jun 2008 04:02:03 +0000 (04:02 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 28 Jun 2008 04:02:03 +0000 (04:02 +0000)
<rdar://problem/5973931> Slow script dialog needs to be reimplemented for squirrelfish

Reviewed by Sam

Adds support for the slow script dialog in squirrelfish.  This requires the addition
of three new op codes, op_loop, op_loop_if_true, and op_loop_if_less which have the
same behaviour as their simple jump equivalents but have an additional time out check.

Additional assertions were added to other jump instructions to prevent accidentally
creating loops with jump types that do not support time out checks.

Sunspider does not report a regression, however this appears very sensitive to code
layout and hardware, so i would expect up to a 1% regression on other systems.

Part of this required moving the old timeout logic from JSGlobalObject and into Machine
which is the cause of a number of the larger diff blocks.

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

15 files changed:
JavaScriptCore/ChangeLog
JavaScriptCore/JavaScriptCore.exp
JavaScriptCore/VM/CodeBlock.cpp
JavaScriptCore/VM/CodeGenerator.cpp
JavaScriptCore/VM/ExceptionHelpers.cpp
JavaScriptCore/VM/ExceptionHelpers.h
JavaScriptCore/VM/LabelID.h
JavaScriptCore/VM/Machine.cpp
JavaScriptCore/VM/Machine.h
JavaScriptCore/VM/Opcode.cpp
JavaScriptCore/VM/Opcode.h
JavaScriptCore/kjs/JSGlobalObject.cpp
JavaScriptCore/kjs/JSGlobalObject.h
JavaScriptCore/kjs/JSObject.h
JavaScriptCore/kjs/interpreter.cpp

index 58377c7..2221bc1 100644 (file)
@@ -1,3 +1,58 @@
+2008-06-27  Oliver Hunt  <oliver@apple.com>
+
+        Reviewed by Sam.
+
+        Bug 18626: SQUIRRELFISH: support the "slow script" dialog <https://bugs.webkit.org/show_bug.cgi?id=18626>
+        <rdar://problem/5973931> Slow script dialog needs to be reimplemented for squirrelfish
+
+        Adds support for the slow script dialog in squirrelfish.  This requires the addition
+        of three new op codes, op_loop, op_loop_if_true, and op_loop_if_less which have the
+        same behaviour as their simple jump equivalents but have an additional time out check.
+
+        Additional assertions were added to other jump instructions to prevent accidentally
+        creating loops with jump types that do not support time out checks.
+
+        Sunspider does not report a regression, however this appears very sensitive to code
+        layout and hardware, so i would expect up to a 1% regression on other systems.
+
+        Part of this required moving the old timeout logic from JSGlobalObject and into Machine
+        which is the cause of a number of the larger diff blocks.
+
+        * JavaScriptCore.exp:
+        * VM/CodeBlock.cpp:
+        (KJS::CodeBlock::dump):
+        * VM/CodeGenerator.cpp:
+        (KJS::CodeGenerator::emitJumpIfTrue):
+        (KJS::CodeGenerator::emitJumpScopes):
+        * VM/ExceptionHelpers.cpp:
+        (KJS::InterruptedExecutionError::isWatchdogException):
+        (KJS::createInterruptedExecutionException):
+        * VM/ExceptionHelpers.h:
+        * VM/LabelID.h:
+        * VM/Machine.cpp:
+        (KJS::Machine::Machine):
+        (KJS::Machine::throwException):
+        (KJS::Machine::resetTimeoutCheck):
+        (KJS::getCurrentTime):
+        (KJS::Machine::checkTimeout):
+        (KJS::Machine::privateExecute):
+        * VM/Machine.h:
+        (KJS::Machine::setTimeoutTime):
+        (KJS::Machine::startTimeoutCheck):
+        (KJS::Machine::stopTimeoutCheck):
+        (KJS::Machine::initTimeout):
+        * VM/Opcode.cpp:
+        (KJS::):
+        * VM/Opcode.h:
+        * kjs/JSGlobalObject.cpp:
+        (KJS::JSGlobalObject::init):
+        (KJS::JSGlobalObject::setTimeoutTime):
+        (KJS::JSGlobalObject::startTimeoutCheck):
+        * kjs/JSGlobalObject.h:
+        * kjs/JSObject.h:
+        * kjs/interpreter.cpp:
+        (KJS::Interpreter::evaluate):
+
 2008-06-27  Jan Michael Alonzo  <jmalonzo@webkit.org>
 
         Gtk and Qt build fix: Remove RegisterFileStack from the build
index fb0dea7..6a0fcc7 100644 (file)
@@ -115,6 +115,7 @@ __ZN3KJS13jsOwnedStringEPNS_9ExecStateERKNS_7UStringE
 __ZN3KJS14JSGlobalObject10globalExecEv
 __ZN3KJS14JSGlobalObject12defineGetterEPNS_9ExecStateERKNS_10IdentifierEPNS_8JSObjectE
 __ZN3KJS14JSGlobalObject12defineSetterEPNS_9ExecStateERKNS_10IdentifierEPNS_8JSObjectE
+__ZN3KJS14JSGlobalObject14setTimeoutTimeEj
 __ZN3KJS14JSGlobalObject16stopTimeoutCheckEv
 __ZN3KJS14JSGlobalObject17putWithAttributesEPNS_9ExecStateERKNS_10IdentifierEPNS_7JSValueEj
 __ZN3KJS14JSGlobalObject17startTimeoutCheckEv
index e61ee9a..b9de195 100644 (file)
@@ -462,10 +462,19 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
             printf("[%4d] jmp\t\t %d(->%d)\n", location, offset, jumpTarget(begin, it, offset));
             break;
         }
+        case op_loop: {
+            int offset = (++it)->u.operand;
+            printf("[%4d] loop\t\t %d(->%d)\n", location, offset, jumpTarget(begin, it, offset));
+            break;
+        }
         case op_jtrue: {
             printConditionalJump(begin, it, location, "jtrue");
             break;
         }
+        case op_loop_if_true: {
+            printConditionalJump(begin, it, location, "loop_if_true");
+            break;
+        }
         case op_jfalse: {
             printConditionalJump(begin, it, location, "jfalse");
             break;
@@ -477,6 +486,13 @@ void CodeBlock::dump(ExecState* exec, const Vector<Instruction>::const_iterator&
             printf("[%4d] jless\t\t %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, jumpTarget(begin, it, offset));
             break;
         }
+        case op_loop_if_less: {
+            int r0 = (++it)->u.operand;
+            int r1 = (++it)->u.operand;
+            int offset = (++it)->u.operand;
+            printf("[%4d] loop_if_less %s, %s, %d(->%d)\n", location, registerName(r0).c_str(), registerName(r1).c_str(), offset, jumpTarget(begin, it, offset));
+            break;
+        }
         case op_new_func: {
             int r0 = (++it)->u.operand;
             int f0 = (++it)->u.operand;
index 65c3d59..b8f4527 100644 (file)
@@ -437,7 +437,7 @@ void CodeGenerator::rewindBinaryOp()
 
 PassRefPtr<LabelID> CodeGenerator::emitJump(LabelID* target)
 {
-    emitOpcode(op_jmp);
+    emitOpcode(target->isForwardLabel() ? op_jmp : op_loop);
     instructions().append(target->offsetFrom(instructions().size()));
     return target;
 }
@@ -453,7 +453,7 @@ PassRefPtr<LabelID> CodeGenerator::emitJumpIfTrue(RegisterID* cond, LabelID* tar
         
         if (cond->index() == dstIndex && !cond->refCount()) {
             rewindBinaryOp();
-            emitOpcode(op_jless);
+            emitOpcode(target->isForwardLabel() ? op_jless : op_loop_if_less);
             instructions().append(src1Index);
             instructions().append(src2Index);
             instructions().append(target->offsetFrom(instructions().size()));
@@ -461,7 +461,7 @@ PassRefPtr<LabelID> CodeGenerator::emitJumpIfTrue(RegisterID* cond, LabelID* tar
         }
     }
     
-    emitOpcode(op_jtrue);
+    emitOpcode(target->isForwardLabel() ? op_jtrue : op_loop_if_true);
     instructions().append(cond->index());
     instructions().append(target->offsetFrom(instructions().size()));
     return target;
@@ -469,6 +469,7 @@ PassRefPtr<LabelID> CodeGenerator::emitJumpIfTrue(RegisterID* cond, LabelID* tar
 
 PassRefPtr<LabelID> CodeGenerator::emitJumpIfFalse(RegisterID* cond, LabelID* target)
 {
+    ASSERT(target->isForwardLabel());
     emitOpcode(op_jfalse);
     instructions().append(cond->index());
     instructions().append(target->offsetFrom(instructions().size()));
@@ -1043,6 +1044,7 @@ PassRefPtr<LabelID> CodeGenerator::emitComplexJumpScopes(LabelID* target, Contro
 PassRefPtr<LabelID> CodeGenerator::emitJumpScopes(LabelID* target, int targetScopeDepth)
 {
     ASSERT(scopeDepth() - targetScopeDepth >= 0);
+    ASSERT(target->isForwardLabel());
 
     size_t scopeDelta = scopeDepth() - targetScopeDepth;
     ASSERT(scopeDelta <= m_scopeContextStack.size());
index f64fb9e..81d97d9 100644 (file)
@@ -45,6 +45,16 @@ static void substitute(UString& string, const UString& substring)
     string = newString;
 }
 
+class InterruptedExecutionError : public JSObject {
+public:
+    virtual bool isWatchdogException() const { return true; }
+};
+
+JSValue* createInterruptedExecutionException(ExecState* exec)
+{
+    return new (exec) InterruptedExecutionError;
+}
+
 JSValue* createError(ExecState* exec, ErrorType e, const char* msg)
 {
     return Error::create(exec, e, msg, -1, -1, 0);
index 5dcbc92..99846a3 100644 (file)
@@ -35,6 +35,7 @@ namespace KJS {
 
     class Node;
 
+    JSValue* createInterruptedExecutionException(ExecState* exec);
     JSValue* createStackOverflowError(ExecState*);
     JSValue* createUndefinedVariableError(ExecState*, const Identifier&);
     JSValue* createInvalidParamError(ExecState*, const char* op, JSValue*);
index 6edb5e2..575d48c 100644 (file)
@@ -98,6 +98,8 @@ namespace KJS {
             return m_refCount;
         }
 
+        bool isForwardLabel() const { return m_location == invalidLocation; }
+
     private:
         typedef Vector<int, 8> JumpVector;
 
index 2301e8f..3a420e3 100644 (file)
 #include "operations.h"
 #include "RegExpObject.h"
 
+#if HAVE(SYS_TIME_H)
+#include <sys/time.h>
+#endif
+
+#if PLATFORM(WIN_OS)
+#include <windows.h>
+#endif
+
+#if PLATFORM(QT)
+#include <QDateTime>
+#endif
+
 using namespace std;
 
 namespace KJS {
 
+// Default number of ticks before a timeout check should be done.
+static const int initialTickCountThreshold = 255;
+
+// Preferred number of milliseconds between each timeout check
+static const int preferredScriptCheckTimeInterval = 1000;
+
 #if HAVE(COMPUTED_GOTO)
 static void* op_throw_end_indirect;
 static void* op_call_indirect;
@@ -452,6 +470,11 @@ NEVER_INLINE JSValue* callEval(ExecState* exec, JSObject* thisObj, ScopeChainNod
 
 Machine::Machine()
     : m_reentryDepth(0)
+    , m_timeoutTime(0)
+    , m_timeAtLastCheckTimeout(0)
+    , m_timeExecuting(0)
+    , m_timeoutCheckCount(0)
+    , m_ticksUntilNextTimeoutCheck(initialTickCountThreshold)
 {
     privateExecute(InitializeAndReturn);
 }
@@ -591,6 +614,13 @@ NEVER_INLINE Instruction* Machine::throwException(ExecState* exec, JSValue* exce
             exception->put(exec, Identifier(exec, "line"), jsNumber(exec, codeBlock->lineNumberForVPC(vPC)));
             exception->put(exec, Identifier(exec, "sourceURL"), jsOwnedString(exec, codeBlock->ownerNode->sourceURL()));
         }
+
+        if (exception->isWatchdogException()) {
+            while (unwindCallFrame(exec, exceptionValue, registerBase, vPC, codeBlock, k, scopeChain, r)) {
+                 // Don't need handler checks or anything, we just want to unroll all the JS callframes possible.
+            }
+            return 0;
+        }
     }
 
     if (Debugger* debugger = exec->dynamicGlobalObject()->debugger()) {
@@ -847,6 +877,72 @@ NEVER_INLINE void Machine::debug(ExecState* exec, const Instruction* vPC, const
     }
 }
 
+void Machine::resetTimeoutCheck()
+{
+    m_ticksUntilNextTimeoutCheck = initialTickCountThreshold;
+    m_timeAtLastCheckTimeout = 0;
+    m_timeExecuting = 0;
+}
+
+// Returns the current time in milliseconds
+// It doesn't matter what "current time" is here, just as long as
+// it's possible to measure the time difference correctly.
+// In an ideal world this would be in DateMath or some such, but unfortunately
+// that's a regression.
+static inline unsigned getCurrentTime()
+{
+#if HAVE(SYS_TIME_H)
+    struct timeval tv;
+    gettimeofday(&tv, 0);
+    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
+#elif PLATFORM(QT)
+    QDateTime t = QDateTime::currentDateTime();
+    return t.toTime_t() * 1000 + t.time().msec();
+#elif PLATFORM(WIN_OS)
+    return timeGetTime();
+#else
+#error Platform does not have getCurrentTime function
+#endif
+}
+
+// We have to return a JSValue here, gcc seems to produce worse code if 
+// we attempt to return a bool
+ALWAYS_INLINE JSValue* Machine::checkTimeout(JSGlobalObject* globalObject)
+{
+    unsigned currentTime = getCurrentTime();
+    
+    if (!m_timeAtLastCheckTimeout) {
+        // Suspicious amount of looping in a script -- start timing it
+        m_timeAtLastCheckTimeout = currentTime;
+        return 0;
+    }
+    
+    unsigned timeDiff = currentTime - m_timeAtLastCheckTimeout;
+    
+    if (timeDiff == 0)
+        timeDiff = 1;
+    
+    m_timeExecuting += timeDiff;
+    m_timeAtLastCheckTimeout = currentTime;
+    
+    // Adjust the tick threshold so we get the next checkTimeout call in the interval specified in 
+    // preferredScriptCheckTimeInterval
+    m_ticksUntilNextTimeoutCheck = static_cast<unsigned>((static_cast<float>(preferredScriptCheckTimeInterval) / timeDiff) * m_ticksUntilNextTimeoutCheck);
+    // If the new threshold is 0 reset it to the default threshold. This can happen if the timeDiff is higher than the
+    // preferred script check time interval.
+    if (m_ticksUntilNextTimeoutCheck == 0)
+        m_ticksUntilNextTimeoutCheck = initialTickCountThreshold;
+    
+    if (m_timeoutTime && m_timeExecuting > m_timeoutTime) {
+        if (globalObject->shouldInterruptScript())
+            return jsNull(); // Appeasing GCC, all we need is a non-null js value.
+        
+        resetTimeoutCheck();
+    }
+    
+    return 0;
+}
+    
 JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFile* registerFile, Register* r, ScopeChainNode* scopeChain, CodeBlock* codeBlock, JSValue** exception)
 {
     // One-time initialization of our address tables. We have to put this code
@@ -874,7 +970,8 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
     Instruction* vPC = codeBlock->instructions.begin();
     JSValue** k = codeBlock->jsValues.data();
     Profiler** enabledProfilerReference = Profiler::enabledProfilerReference();
-    
+    unsigned tickCount = m_ticksUntilNextTimeoutCheck + 1;
+
 #define VM_CHECK_EXCEPTION() \
      do { \
         if (UNLIKELY(exec->hadException())) { \
@@ -887,6 +984,13 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
     OpcodeStats::resetLastInstruction();
 #endif
 
+#define CHECK_FOR_TIMEOUT() \
+    if (!--tickCount) { \
+        if ((exceptionValue = checkTimeout(exec->dynamicGlobalObject()))) \
+            goto vm_throw; \
+        tickCount = m_ticksUntilNextTimeoutCheck; \
+    }
+    
 #if HAVE(COMPUTED_GOTO)
     #define NEXT_OPCODE goto *vPC->u.opcode
 #if DUMP_OPCODE_STATS
@@ -1843,6 +1947,23 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         ++vPC;
         NEXT_OPCODE;
     }
+    BEGIN_OPCODE(op_loop) {
+        /* loop target(offset)
+         
+           Jumps unconditionally to offset target from the current
+           instruction.
+
+           Additionally this loop instruction may terminate JS execution is
+           the JS timeout is reached.
+         */
+#if DUMP_OPCODE_STATS
+        OpcodeStats::resetLastInstruction();
+#endif
+        int target = (++vPC)->u.operand;
+        CHECK_FOR_TIMEOUT();
+        vPC += target;
+        NEXT_OPCODE;
+    }
     BEGIN_OPCODE(op_jmp) {
         /* jmp target(offset)
 
@@ -1857,6 +1978,26 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         vPC += target;
         NEXT_OPCODE;
     }
+    BEGIN_OPCODE(op_loop_if_true) {
+        /* loop_if_true cond(r) target(offset)
+         
+           Jumps to offset target from the current instruction, if and
+           only if register cond converts to boolean as true.
+
+           Additionally this loop instruction may terminate JS execution is
+           the JS timeout is reached.
+         */
+        int cond = (++vPC)->u.operand;
+        int target = (++vPC)->u.operand;
+        if (r[cond].u.jsValue->toBoolean(exec)) {
+            vPC += target;
+            CHECK_FOR_TIMEOUT();
+            NEXT_OPCODE;
+        }
+        
+        ++vPC;
+        NEXT_OPCODE;
+    }
     BEGIN_OPCODE(op_jtrue) {
         /* jtrue cond(r) target(offset)
 
@@ -1889,6 +2030,33 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
         ++vPC;
         NEXT_OPCODE;
     }
+    BEGIN_OPCODE(op_loop_if_less) {
+        /* loop_if_less src1(r) src2(r) target(offset)
+
+           Checks whether register src1 is less than register src2, as
+           with the ECMAScript '<' operator, and then jumps to offset
+           target from the current instruction, if and only if the 
+           result of the comparison is true.
+
+           Additionally this loop instruction may terminate JS execution is
+           the JS timeout is reached.
+         */
+        JSValue* src1 = r[(++vPC)->u.operand].u.jsValue;
+        JSValue* src2 = r[(++vPC)->u.operand].u.jsValue;
+        int target = (++vPC)->u.operand;
+        
+        bool result = jsLess(exec, src1, src2);
+        VM_CHECK_EXCEPTION();
+        
+        if (result) {
+            vPC += target;
+            CHECK_FOR_TIMEOUT();
+            NEXT_OPCODE;
+        }
+        
+        ++vPC;
+        NEXT_OPCODE;
+    }
     BEGIN_OPCODE(op_jless) {
         /* jless src1(r) src2(r) target(offset)
 
@@ -2284,6 +2452,7 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
 
         JSPropertyNameIterator* it = r[iter].u.jsPropertyNameIterator;
         if (JSValue* temp = it->next(exec)) {
+            CHECK_FOR_TIMEOUT();
             r[dst].u.jsValue = temp;
             vPC += target;
             NEXT_OPCODE;
@@ -2475,6 +2644,11 @@ JSValue* Machine::privateExecute(ExecutionFlag flag, ExecState* exec, RegisterFi
     }
     vm_throw: {
         exec->clearException();
+        if (!tickCount) {
+            // The exceptionValue is a lie! (GCC produces bad code for reasons I 
+            // cannot fathom if we don't assign to the exceptionValue before branching)
+            exceptionValue = createInterruptedExecutionException(exec);
+        }
         handlerVPC = throwException(exec, exceptionValue, &registerBase, vPC, codeBlock, k, scopeChain, r);
         if (!handlerVPC) {
             *exception = exceptionValue;
index ba195b8..64ff28b 100644 (file)
@@ -42,6 +42,7 @@ namespace KJS {
     class FunctionBodyNode;
     class Instruction;
     class JSFunction;
+    class JSGlobalObject;
     class ProgramNode;
     class Register;
     class RegisterFile;
@@ -96,7 +97,27 @@ namespace KJS {
         JSValue* retrieveCaller(ExecState*, JSFunction*) const;
 
         void getFunctionAndArguments(Register** registerBase, Register* callFrame, JSFunction*&, Register*& argv, int& argc);
+        void setTimeoutTime(unsigned timeoutTime) { m_timeoutTime = timeoutTime; }
+        
+        void startTimeoutCheck()
+        {
+            if (!m_timeoutCheckCount)
+                resetTimeoutCheck();
+            
+            ++m_timeoutCheckCount;
+        }
+        
+        void stopTimeoutCheck()
+        {
+            --m_timeoutCheckCount;
+        }
 
+        inline void initTimeout()
+        {
+            resetTimeoutCheck();
+            m_timeoutTime = 0;
+            m_timeoutCheckCount = 0;
+        }
         void mark(Heap* heap) { m_registerFile.mark(heap); }
 
     private:
@@ -118,7 +139,16 @@ namespace KJS {
         void dumpCallFrame(const CodeBlock*, ScopeChainNode*, RegisterFile*, const Register*);
         void dumpRegisters(const CodeBlock*, RegisterFile*, const Register*);
 
+        JSValue* checkTimeout(JSGlobalObject*);
+        void resetTimeoutCheck();
+
         int m_reentryDepth;
+        unsigned m_timeoutTime;
+        unsigned m_timeAtLastCheckTimeout;
+        unsigned m_timeExecuting;
+        unsigned m_timeoutCheckCount;
+        unsigned m_ticksUntilNextTimeoutCheck;
+
         RegisterFile m_registerFile;
 
 #if HAVE(COMPUTED_GOTO)
index be125ed..a97ef8a 100644 (file)
@@ -105,6 +105,9 @@ static const char* opcodeNames[] = {
     "jfalse      ",
     "jless       ",
     "jmp_scopes  ",
+    "loop        ",
+    "loop_if_true",
+    "loop_if_less",
 
     "new_func    ",
     "new_func_exp",
index a913222..688cf0f 100644 (file)
@@ -97,6 +97,9 @@ namespace KJS {
         macro(op_jfalse) \
         macro(op_jless) \
         macro(op_jmp_scopes) \
+        macro(op_loop) \
+        macro(op_loop_if_true) \
+        macro(op_loop_if_less) \
         \
         macro(op_new_func) \
         macro(op_new_func_exp) \
index 5c89972..ea04d27 100644 (file)
 #include "ScopeChainMark.h"
 #include "string_object.h"
 
-#if HAVE(SYS_TIME_H)
-#include <sys/time.h>
-#endif
-
-#if PLATFORM(WIN_OS)
-#include <windows.h>
-#endif
-
-#if PLATFORM(QT)
-#include <QDateTime>
-#endif
-
 namespace KJS {
 
 // Default number of ticks before a timeout check should be done.
@@ -70,25 +58,6 @@ static inline void markIfNeeded(JSValue* v)
     if (v && !v->marked())
         v->mark();
 }
-    
-// Returns the current time in milliseconds
-// It doesn't matter what "current time" is here, just as long as
-// it's possible to measure the time difference correctly.
-static inline unsigned getCurrentTime()
-{
-#if HAVE(SYS_TIME_H)
-    struct timeval tv;
-    gettimeofday(&tv, 0);
-    return tv.tv_sec * 1000 + tv.tv_usec / 1000;
-#elif PLATFORM(QT)
-    QDateTime t = QDateTime::currentDateTime();
-    return t.toTime_t() * 1000 + t.time().msec();
-#elif PLATFORM(WIN_OS)
-    return timeGetTime();
-#else
-#error Platform does not have getCurrentTime function
-#endif
-}
 
 JSGlobalObject::~JSGlobalObject()
 {
@@ -131,13 +100,10 @@ void JSGlobalObject::init(JSObject* thisValue)
     } else
         headObject = d()->next = d()->prev = this;
 
-    resetTimeoutCheck();
-    d()->timeoutTime = 0;
-    d()->timeoutCheckCount = 0;
-
     d()->recursion = 0;
     d()->debugger = 0;
-    
+    globalData()->machine->initTimeout();
+
     d()->globalExec.set(new ExecState(this, thisValue, d()->globalScopeChain.node()));
 
     d()->pageGroupIdentifier = 0;
@@ -346,64 +312,19 @@ void JSGlobalObject::reset(JSValue* prototype)
     lastInPrototypeChain(this)->setPrototype(d()->objectPrototype);
 }
 
-void JSGlobalObject::startTimeoutCheck()
+void JSGlobalObject::setTimeoutTime(unsigned timeoutTime)
 {
-    if (!d()->timeoutCheckCount)
-        resetTimeoutCheck();
-    
-    ++d()->timeoutCheckCount;
+    globalData()->machine->setTimeoutTime(timeoutTime);
 }
 
-void JSGlobalObject::stopTimeoutCheck()
+void JSGlobalObject::startTimeoutCheck()
 {
-    --d()->timeoutCheckCount;
+    globalData()->machine->startTimeoutCheck();
 }
 
-void JSGlobalObject::resetTimeoutCheck()
+void JSGlobalObject::stopTimeoutCheck()
 {
-    d()->tickCount = 0;
-    d()->ticksUntilNextTimeoutCheck = initialTickCountThreshold;
-    d()->timeAtLastCheckTimeout = 0;
-    d()->timeExecuting = 0;
-}
-
-bool JSGlobalObject::checkTimeout()
-{    
-    d()->tickCount = 0;
-    
-    unsigned currentTime = getCurrentTime();
-
-    if (!d()->timeAtLastCheckTimeout) {
-        // Suspicious amount of looping in a script -- start timing it
-        d()->timeAtLastCheckTimeout = currentTime;
-        return false;
-    }
-
-    unsigned timeDiff = currentTime - d()->timeAtLastCheckTimeout;
-
-    if (timeDiff == 0)
-        timeDiff = 1;
-    
-    d()->timeExecuting += timeDiff;
-    d()->timeAtLastCheckTimeout = currentTime;
-    
-    // Adjust the tick threshold so we get the next checkTimeout call in the interval specified in 
-    // preferredScriptCheckTimeInterval
-    d()->ticksUntilNextTimeoutCheck = (unsigned)((float)preferredScriptCheckTimeInterval / timeDiff) * d()->ticksUntilNextTimeoutCheck;
-
-    // If the new threshold is 0 reset it to the default threshold. This can happen if the timeDiff is higher than the
-    // preferred script check time interval.
-    if (d()->ticksUntilNextTimeoutCheck == 0)
-        d()->ticksUntilNextTimeoutCheck = initialTickCountThreshold;
-
-    if (d()->timeoutTime && d()->timeExecuting > d()->timeoutTime) {
-        if (shouldInterruptScript())
-            return true;
-        
-        resetTimeoutCheck();
-    }
-    
-    return false;
+    globalData()->machine->stopTimeoutCheck();
 }
 
 void JSGlobalObject::mark()
index fb46e2b..2cb4560 100644 (file)
@@ -89,13 +89,6 @@ namespace KJS {
 
             int recursion;
 
-            unsigned timeoutTime;
-            unsigned timeAtLastCheckTimeout;
-            unsigned timeExecuting;
-            unsigned timeoutCheckCount;
-            unsigned tickCount;
-            unsigned ticksUntilNextTimeoutCheck;
-
             RegExpConstructor* regExpConstructor;
             ErrorConstructor* errorConstructor;
             NativeErrorConstructor* evalErrorConstructor;
@@ -202,10 +195,9 @@ namespace KJS {
         void setPageGroupIdentifier(unsigned value) { d()->pageGroupIdentifier = value; }
         unsigned pageGroupIdentifier() const { return d()->pageGroupIdentifier; }
 
-        void setTimeoutTime(unsigned timeoutTime) { d()->timeoutTime = timeoutTime; }
+        void setTimeoutTime(unsigned timeoutTime);
         void startTimeoutCheck();
         void stopTimeoutCheck();
-        bool timedOut();
 
         Debugger* debugger() const { return d()->debugger; }
         void setDebugger(Debugger* debugger) { d()->debugger = debugger; }
@@ -262,9 +254,6 @@ namespace KJS {
         };
         void addStaticGlobals(GlobalPropertyInfo*, int count);
 
-    private:
-        bool checkTimeout();
-        void resetTimeoutCheck();
     };
 
     inline void JSGlobalObject::addStaticGlobals(GlobalPropertyInfo* globals, int count)
@@ -298,16 +287,6 @@ namespace KJS {
         return symbolTableGet(propertyName, slot, slotIsWriteable);
     }
 
-    inline bool JSGlobalObject::timedOut()
-    {
-        d()->tickCount++;
-
-        if (d()->tickCount != d()->ticksUntilNextTimeoutCheck)
-            return false;
-
-        return checkTimeout();
-    }
-
     inline JSGlobalObject* ScopeChainNode::globalObject() const
     {
         JSGlobalObject* globalObject = static_cast<JSGlobalObject*>(bottom());
index 819d92c..4a6ee4a 100644 (file)
@@ -378,6 +378,8 @@ namespace KJS {
     virtual bool isGlobalObject() const { return false; }
     virtual bool isVariableObject() const { return false; }
 
+    virtual bool isWatchdogException() const { return false; }
+
   protected:
     PropertyMap _prop;
     bool getOwnPropertySlotForWrite(ExecState*, const Identifier&, PropertySlot&, bool& slotIsWriteable);
index 3f5143f..f6dc025 100644 (file)
@@ -81,7 +81,12 @@ Completion Interpreter::evaluate(ExecState* exec, ScopeChain& scopeChain, const
     JSValue* exception = 0;
     JSValue* result = exec->machine()->execute(programNode.get(), exec, scopeChain.node(), thisObj, &exception);
 
-    return exception ? Completion(Throw, exception) : Completion(Normal, result);
+    if (exception) {
+        if (exception->isObject() && static_cast<JSObject*>(exception)->isWatchdogException())
+            return Completion(Interrupted, result);
+        return Completion(Throw, exception);
+    }
+    return Completion(Normal, result);
 }
 
 static bool printExceptions = false;