Commit bfa44ad2 authored by andersca's avatar andersca

2006-06-17 Anders Carlsson <acarlsson@apple.com>

        Reviewed by Maciej and Geoff.

        http://bugzilla.opendarwin.org/show_bug.cgi?id=7080
        Provide some way to stop a JavaScript infinite loop
        
        * kjs/completion.h:
        (KJS::):
        Add Interrupted completion type.
        
        * kjs/function.cpp:
        (KJS::FunctionImp::callAsFunction):
        (KJS::GlobalFuncImp::callAsFunction):
        Only set the exception on the new ExecState if the current one has had one.
        
        * kjs/interpreter.cpp:
        (KJS::TimeoutChecker::startTimeoutCheck):
        (KJS::TimeoutChecker::stopTimeoutCheck):
        (KJS::TimeoutChecker::alarmHandler):
        (KJS::TimeoutChecker::pauseTimeoutCheck):
        (KJS::TimeoutChecker::resumeTimeoutCheck):
        New TimeoutChecker class which handles setting Interpreter::m_timedOut flag after a given
        period of time. This currently only works on Unix platforms where setitimer and signals are used.
        
        (KJS::Interpreter::Interpreter):
        Initialize new member variables.
        
        (KJS::Interpreter::~Interpreter):
        Destroy the timeout checker.
        
        (KJS::Interpreter::startTimeoutCheck):
        (KJS::Interpreter::stopTimeoutCheck):
        (KJS::Interpreter::pauseTimeoutCheck):
        (KJS::Interpreter::resumeTimeoutCheck):
        Call the timeout checker.
        
        (KJS::Interpreter::handleTimeout):
        Called on timeout. Resets the m_timedOut flag and calls shouldInterruptScript.
        
        * kjs/interpreter.h:
        (KJS::Interpreter::setTimeoutTime):
        New function for setting the timeout time.
        
        (KJS::Interpreter::shouldInterruptScript):
        New function. The idea is that this should be overridden by subclasses in order to for example
        pop up a dialog asking the user if the script should be interrupted.
        
        (KJS::Interpreter::checkTimeout):
        New function which checks the m_timedOut flag and calls handleTimeout if it's set.
        
        * kjs/nodes.cpp:
        (DoWhileNode::execute):
        (WhileNode::execute):
        (ForNode::execute):
        Call Interpreter::checkTimeout after each iteration of the loop.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@14893 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent d19cc9ef
2006-06-17 Anders Carlsson <acarlsson@apple.com>
Reviewed by Maciej and Geoff.
http://bugzilla.opendarwin.org/show_bug.cgi?id=7080
Provide some way to stop a JavaScript infinite loop
* kjs/completion.h:
(KJS::):
Add Interrupted completion type.
* kjs/function.cpp:
(KJS::FunctionImp::callAsFunction):
(KJS::GlobalFuncImp::callAsFunction):
Only set the exception on the new ExecState if the current one has had one.
* kjs/interpreter.cpp:
(KJS::TimeoutChecker::startTimeoutCheck):
(KJS::TimeoutChecker::stopTimeoutCheck):
(KJS::TimeoutChecker::alarmHandler):
(KJS::TimeoutChecker::pauseTimeoutCheck):
(KJS::TimeoutChecker::resumeTimeoutCheck):
New TimeoutChecker class which handles setting Interpreter::m_timedOut flag after a given
period of time. This currently only works on Unix platforms where setitimer and signals are used.
(KJS::Interpreter::Interpreter):
Initialize new member variables.
(KJS::Interpreter::~Interpreter):
Destroy the timeout checker.
(KJS::Interpreter::startTimeoutCheck):
(KJS::Interpreter::stopTimeoutCheck):
(KJS::Interpreter::pauseTimeoutCheck):
(KJS::Interpreter::resumeTimeoutCheck):
Call the timeout checker.
(KJS::Interpreter::handleTimeout):
Called on timeout. Resets the m_timedOut flag and calls shouldInterruptScript.
* kjs/interpreter.h:
(KJS::Interpreter::setTimeoutTime):
New function for setting the timeout time.
(KJS::Interpreter::shouldInterruptScript):
New function. The idea is that this should be overridden by subclasses in order to for example
pop up a dialog asking the user if the script should be interrupted.
(KJS::Interpreter::checkTimeout):
New function which checks the m_timedOut flag and calls handleTimeout if it's set.
* kjs/nodes.cpp:
(DoWhileNode::execute):
(WhileNode::execute):
(ForNode::execute):
Call Interpreter::checkTimeout after each iteration of the loop.
2006-06-15 Timothy Hatcher <timothy@apple.com>
Reviewed by Geoff and Darin.
......
......@@ -33,7 +33,7 @@ namespace KJS {
/**
* Completion types.
*/
enum ComplType { Normal, Break, Continue, ReturnValue, Throw };
enum ComplType { Normal, Break, Continue, ReturnValue, Throw, Interrupted };
/**
* Completion objects are used to convey the return status and value
......
......@@ -74,7 +74,8 @@ JSValue *FunctionImp::callAsFunction(ExecState* exec, JSObject* thisObj, const L
Context ctx(globalObj, exec->dynamicInterpreter(), thisObj, body.get(),
codeType(), exec->context(), this, &args);
ExecState newExec(exec->dynamicInterpreter(), &ctx);
newExec.setException(exec->exception()); // could be null
if (exec->hadException())
newExec.setException(exec->exception());
// assign user supplied arguments to parameters
processParameters(&newExec, args);
......@@ -808,7 +809,8 @@ JSValue *GlobalFuncImp::callAsFunction(ExecState *exec, JSObject */*thisObj*/, c
exec->context());
ExecState newExec(exec->dynamicInterpreter(), &ctx);
newExec.setException(exec->exception()); // could be null
if (exec->hadException())
newExec.setException(exec->exception());
// execute the code
progNode->processVarDecls(&newExec);
......
......@@ -50,12 +50,144 @@
#include "runtime.h"
#endif
#if HAVE(SYS_TIME_H)
#include <sys/time.h>
#endif
#include <assert.h>
#include <math.h>
#include <stdio.h>
namespace KJS {
class TimeoutChecker {
public:
void startTimeoutCheck(Interpreter*);
void stopTimeoutCheck(Interpreter*);
void pauseTimeoutCheck(Interpreter*);
void resumeTimeoutCheck(Interpreter*);
private:
#if HAVE(SYS_TIME_H)
static Interpreter* s_executingInterpreter;
static void alarmHandler(int);
Interpreter* m_oldInterpreter;
itimerval m_oldtv;
itimerval m_pausetv;
void (*m_oldAlarmHandler)(int);
#endif
};
#if HAVE(SYS_TIME_H)
Interpreter* TimeoutChecker::s_executingInterpreter = 0;
#endif
void TimeoutChecker::startTimeoutCheck(Interpreter *interpreter)
{
if (!interpreter->m_timeoutTime)
return;
interpreter->m_startTimeoutCheckCount++;
#if HAVE(SYS_TIME_H)
if (s_executingInterpreter == interpreter)
return;
// Block signals
m_oldAlarmHandler = signal(SIGALRM, SIG_IGN);
m_oldInterpreter = s_executingInterpreter;
s_executingInterpreter = interpreter;
itimerval tv = {
{ interpreter->m_timeoutTime / 1000, (interpreter->m_timeoutTime % 1000) * 1000 },
{ interpreter->m_timeoutTime / 1000, (interpreter->m_timeoutTime % 1000) * 1000 }
};
setitimer(ITIMER_REAL, &tv, &m_oldtv);
// Unblock signals
signal(SIGALRM, alarmHandler);
#endif
}
void TimeoutChecker::stopTimeoutCheck(Interpreter* interpreter)
{
if (!interpreter->m_timeoutTime)
return;
ASSERT(interpreter->m_startTimeoutCheckCount > 0);
interpreter->m_startTimeoutCheckCount--;
if (interpreter->m_startTimeoutCheckCount != 0)
return;
#if HAVE(SYS_TIME_H)
signal(SIGALRM, SIG_IGN);
s_executingInterpreter = m_oldInterpreter;
setitimer(ITIMER_REAL, &m_oldtv, 0L);
signal(SIGALRM, m_oldAlarmHandler);
#endif
}
#if HAVE(SYS_TIME_H)
void TimeoutChecker::alarmHandler(int)
{
s_executingInterpreter->m_timedOut = true;
}
#endif
void TimeoutChecker::pauseTimeoutCheck(Interpreter* interpreter)
{
ASSERT(interpreter == s_executingInterpreter);
#if HAVE(SYS_TIME_H)
void (*currentSignalHandler)(int);
// Block signal
currentSignalHandler = signal(SIGALRM, SIG_IGN);
if (currentSignalHandler != alarmHandler) {
signal(SIGALRM, currentSignalHandler);
return;
}
setitimer(ITIMER_REAL, &m_pausetv, 0L);
#endif
interpreter->m_pauseTimeoutCheckCount++;
}
void TimeoutChecker::resumeTimeoutCheck(Interpreter* interpreter)
{
ASSERT(interpreter == s_executingInterpreter);
interpreter->m_pauseTimeoutCheckCount--;
if (interpreter->m_pauseTimeoutCheckCount != 0)
return;
#if HAVE(SYS_TIME_H)
void (*currentSignalHandler)(int);
// Check so we have the right handler
currentSignalHandler = signal(SIGALRM, SIG_IGN);
if (currentSignalHandler != SIG_IGN) {
signal(SIGALRM, currentSignalHandler);
return;
}
setitimer(ITIMER_REAL, 0L, &m_pausetv);
// Unblock signal
currentSignalHandler = signal(SIGALRM, SIG_IGN);
#endif
}
Interpreter* Interpreter::s_hook = 0;
typedef HashMap<JSObject*, Interpreter*> InterpreterMap;
......@@ -66,19 +198,29 @@ static inline InterpreterMap &interpreterMap()
}
Interpreter::Interpreter(JSObject* globalObject)
: m_globalExec(this, 0)
: m_timeoutTime(0)
, m_globalExec(this, 0)
, m_globalObject(globalObject)
, m_argumentsPropertyName(&argumentsPropertyName)
, m_specialPrototypePropertyName(&specialPrototypePropertyName)
, m_timeoutChecker(0)
, m_timedOut(false)
, m_startTimeoutCheckCount(0)
, m_pauseTimeoutCheckCount(0)
{
init();
}
Interpreter::Interpreter()
: m_globalExec(this, 0)
: m_timeoutTime(0)
, m_globalExec(this, 0)
, m_globalObject(new JSObject())
, m_argumentsPropertyName(&argumentsPropertyName)
, m_specialPrototypePropertyName(&specialPrototypePropertyName)
, m_timeoutChecker(0)
, m_timedOut(false)
, m_startTimeoutCheckCount(0)
, m_pauseTimeoutCheckCount(0)
{
init();
}
......@@ -111,6 +253,11 @@ Interpreter::~Interpreter()
{
JSLock lock;
ASSERT (m_startTimeoutCheckCount == 0);
ASSERT (m_pauseTimeoutCheckCount == 0);
delete m_timeoutChecker;
if (m_debugger)
m_debugger->detach(this);
......@@ -620,6 +767,43 @@ void Interpreter::restoreBuiltins (const SavedBuiltins& builtins)
m_UriErrorPrototype = builtins._internal->m_UriErrorPrototype;
}
void Interpreter::startTimeoutCheck()
{
if (!m_timeoutChecker)
m_timeoutChecker = new TimeoutChecker;
m_timeoutChecker->startTimeoutCheck(this);
}
void Interpreter::stopTimeoutCheck()
{
ASSERT(m_timeoutChecker);
m_timeoutChecker->stopTimeoutCheck(this);
}
void Interpreter::pauseTimeoutCheck()
{
ASSERT(m_timeoutChecker);
m_timeoutChecker->pauseTimeoutCheck(this);
}
void Interpreter::resumeTimeoutCheck()
{
ASSERT(m_timeoutChecker);
m_timeoutChecker->resumeTimeoutCheck(this);
}
bool Interpreter::handleTimeout()
{
m_timedOut = false;
return shouldInterruptScript();
}
SavedBuiltins::SavedBuiltins() :
_internal(0)
{
......
......@@ -36,7 +36,8 @@ namespace KJS {
class RuntimeMethod;
class SavedBuiltins;
class ScopeChain;
class TimeoutChecker;
namespace Bindings {
class RootObject;
}
......@@ -49,6 +50,7 @@ namespace KJS {
*/
class Interpreter {
friend class Collector;
friend class TimeoutChecker;
public:
/**
* Creates a new interpreter. The supplied object will be used as the global
......@@ -322,7 +324,23 @@ namespace KJS {
Context* context() const { return m_context; }
static Interpreter* interpreterWithGlobalObject(JSObject*);
void setTimeoutTime(unsigned timeoutTime) { m_timeoutTime = timeoutTime; }
void startTimeoutCheck();
void stopTimeoutCheck();
void pauseTimeoutCheck();
void resumeTimeoutCheck();
bool checkTimeout();
protected:
virtual bool shouldInterruptScript() { return true; }
long m_timeoutTime;
private:
bool handleTimeout();
void init();
/**
......@@ -355,6 +373,12 @@ private:
Context* m_context;
CompatMode m_compatMode;
TimeoutChecker* m_timeoutChecker;
bool m_timedOut;
unsigned m_startTimeoutCheckCount;
unsigned m_pauseTimeoutCheckCount;
ProtectedPtr<JSObject> m_Object;
ProtectedPtr<JSObject> m_Function;
ProtectedPtr<JSObject> m_Array;
......@@ -390,6 +414,14 @@ private:
ProtectedPtr<JSObject> m_UriErrorPrototype;
};
inline bool Interpreter::checkTimeout()
{
if (!m_timedOut)
return false;
return handleTimeout();
}
} // namespace
#endif // _KJS_INTERPRETER_H_
......@@ -1712,6 +1712,10 @@ Completion DoWhileNode::execute(ExecState *exec)
exec->context()->pushIteration();
c = statement->execute(exec);
exec->context()->popIteration();
if (exec->dynamicInterpreter()->checkTimeout())
return Completion(Interrupted);
if (!((c.complType() == Continue) && ls.contains(c.target()))) {
if ((c.complType() == Break) && ls.contains(c.target()))
return Completion(Normal, 0);
......@@ -1756,6 +1760,10 @@ Completion WhileNode::execute(ExecState *exec)
exec->context()->pushIteration();
c = statement->execute(exec);
exec->context()->popIteration();
if (exec->dynamicInterpreter()->checkTimeout())
return Completion(Interrupted);
if (c.isValueCompletion())
value = c.value();
......@@ -1807,6 +1815,10 @@ Completion ForNode::execute(ExecState *exec)
if (c.complType() != Normal)
return c;
}
if (exec->dynamicInterpreter()->checkTimeout())
return Completion(Interrupted);
if (expr3) {
v = expr3->evaluate(exec);
KJS_CHECKEXCEPTION
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment