Remove the parent pointer from ExecutionContext

It's cleaner to have an explicit stack of contexts
in the Engine, esp. as the global context can get
pushed onto the stack several times. This avoids
an ugly hack in eval() where we created a 'copy'
of the global context.

Change-Id: I3936443fba6c1829a60a8e0e9a106ec75293274f
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
Lars Knoll 2013-03-14 14:03:04 +01:00 committed by Simon Hausmann
parent 5aa49a600c
commit b0bdbbd223
7 changed files with 95 additions and 46 deletions

View File

@ -176,7 +176,6 @@ unsigned int ExecutionContext::variableCount() const
void ExecutionContext::init(ExecutionEngine *eng)
{
engine = eng;
parent = 0;
outer = 0;
thisObject = eng->globalObject;
@ -197,12 +196,11 @@ void ExecutionContext::init(ExecutionEngine *eng)
void ExecutionContext::init(ExecutionContext *p, Object *with)
{
engine = p->engine;
parent = p;
outer = p;
thisObject = p->thisObject;
function = 0;
lookups = parent->lookups;
lookups = p->lookups;
arguments = 0;
argumentCount = 0;
@ -218,12 +216,11 @@ void ExecutionContext::init(ExecutionContext *p, Object *with)
void ExecutionContext::initForCatch(ExecutionContext *p, String *exceptionVarName, const Value &exceptionValue)
{
engine = p->engine;
parent = p;
outer = p;
thisObject = p->thisObject;
function = 0;
lookups = parent->lookups;
lookups = p->lookups;
arguments = 0;
argumentCount = 0;
locals = 0;
@ -523,13 +520,10 @@ void ExecutionContext::throwURIError(Value msg)
throwError(Value::fromObject(engine->newURIErrorObject(this, msg)));
}
void ExecutionContext::initCallContext(ExecutionContext *parent)
void ExecutionContext::initCallContext(ExecutionEngine *engine)
{
engine = parent->engine;
assert(engine->current == parent);
this->parent = parent;
this->engine = engine;
outer = function->scope;
engine->current = this;
exceptionVarName = 0;
exceptionValue = Value::undefinedValue();

View File

@ -77,7 +77,6 @@ struct Q_V4_EXPORT DiagnosticMessage
struct ExecutionContext
{
ExecutionEngine *engine;
ExecutionContext *parent;
ExecutionContext *outer;
Value thisObject;
@ -111,7 +110,7 @@ struct ExecutionContext
Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const;
bool deleteBinding(ExecutionContext *ctx, String *name);
void initCallContext(ExecutionContext *parent);
void initCallContext(QQmlJS::VM::ExecutionEngine *engine);
void wireUpPrototype();

View File

@ -66,6 +66,9 @@ namespace VM {
ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
: memoryManager(new QQmlJS::VM::MemoryManager)
, contextStack(0)
, contextStackPosition(0)
, contextStackSize(0)
, debugger(0)
, globalObject(Value::nullValue())
, globalCode(0)
@ -107,9 +110,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
emptyClass = new InternalClass(this);
arrayClass = emptyClass->addMember(id_length);
rootContext = new ExecutionContext();
rootContext->init(this);
current = rootContext;
initRootContext();
objectPrototype = new (memoryManager) ObjectPrototype(this);
stringPrototype = new (memoryManager) StringPrototype(rootContext);
@ -239,49 +240,98 @@ ExecutionEngine::~ExecutionEngine()
{
delete globalObject.asObject();
delete rootContext;
delete [] contextStack;
UnwindHelper::deregisterFunctions(functions);
qDeleteAll(functions);
delete memoryManager;
}
void ExecutionEngine::initRootContext()
{
ensureContextStackSize();
rootContext = new ExecutionContext();
rootContext->init(this);
current = rootContext;
contextStack[0] = rootContext;
}
void ExecutionEngine::ensureContextStackSize()
{
if (contextStackPosition < contextStackSize - 1)
return;
const int stackSize = qMax(32, 2*contextStackSize);
ExecutionContext **newStack = new ExecutionContext *[stackSize];
if (contextStack)
memcpy(newStack, contextStack, contextStackSize*sizeof(ExecutionContext *));
memset(newStack + contextStackSize, 0, (stackSize - contextStackSize)*sizeof(ExecutionContext *));
contextStackSize = stackSize;
contextStack = newStack;
}
ExecutionContext *ExecutionEngine::newWithContext(Object *with)
{
ExecutionContext *withCtx = new ExecutionContext();
withCtx->init(current, with);
current = withCtx;
return withCtx;
ensureContextStackSize();
assert(contextStack[contextStackPosition + 1] == 0);
ExecutionContext *ctx = new ExecutionContext();
ctx->init(current, with);
current = ctx;
contextStack[++contextStackPosition] = current;
return current;
}
ExecutionContext *ExecutionEngine::newCatchContext(String *exceptionVarName, const Value &exceptionValue)
{
ExecutionContext *catchCtx = new ExecutionContext();
catchCtx->initForCatch(current, exceptionVarName, exceptionValue);
current = catchCtx;
return catchCtx;
ensureContextStackSize();
assert(contextStack[contextStackPosition + 1] == 0);
ExecutionContext *ctx = new ExecutionContext();
ctx->initForCatch(current, exceptionVarName, exceptionValue);
current = ctx;
contextStack[++contextStackPosition] = current;
return current;
}
ExecutionContext *ExecutionEngine::newCallContext(FunctionObject *f, const Value &thisObject, Value *args, int argc)
{
ensureContextStackSize();
assert(contextStack[contextStackPosition + 1] == 0);
uint size = requiredMemoryForExecutionContect(f, argc);
ExecutionContext *ctx = static_cast<ExecutionContext *>(malloc(size));
ctx->function = f;
ctx->thisObject = thisObject;
ctx->arguments = args;
ctx->argumentCount = argc;
ctx->initCallContext(current);
current = ctx;
return ctx;
current = static_cast<ExecutionContext *>(malloc(size));
current->function = f;
current->thisObject = thisObject;
current->arguments = args;
current->argumentCount = argc;
current->initCallContext(this);
contextStack[++contextStackPosition] = current;
return current;
}
ExecutionContext *ExecutionEngine::pushGlobalContext()
{
ensureContextStackSize();
assert(contextStack[contextStackPosition + 1] == 0);
current = rootContext;
contextStack[++contextStackPosition] = current;
return current;
}
ExecutionContext *ExecutionEngine::popContext()
{
ExecutionContext *oldCtx = current;
current = current->parent;
oldCtx->parent = 0;
assert(current == contextStack[contextStackPosition]);
if (debugger)
debugger->justLeft(oldCtx);
debugger->justLeft(current);
contextStack[contextStackPosition] = 0;
current = contextStack[--contextStackPosition];
return current;
}

View File

@ -105,8 +105,14 @@ struct Q_V4_EXPORT ExecutionEngine
{
MemoryManager *memoryManager;
QScopedPointer<EvalISelFactory> iselFactory;
ExecutionContext **contextStack;
int contextStackPosition;
int contextStackSize;
ExecutionContext *current;
ExecutionContext *rootContext;
WTF::BumpPointerAllocator bumperPointerAllocator; // Used by Yarr Regex engine.
Identifiers *identifierCache;
@ -190,6 +196,7 @@ struct Q_V4_EXPORT ExecutionEngine
ExecutionContext *newWithContext(Object *with);
ExecutionContext *newCatchContext(String* exceptionVarName, const QQmlJS::VM::Value &exceptionValue);
ExecutionContext *newCallContext(FunctionObject *f, const QQmlJS::VM::Value &thisObject, QQmlJS::VM::Value *args, int argc);
ExecutionContext *pushGlobalContext();
ExecutionContext *popContext();
VM::Function *newFunction(const QString &name);
@ -230,6 +237,9 @@ struct Q_V4_EXPORT ExecutionEngine
void markObjects();
Value run(VM::Function *function, ExecutionContext *ctx = 0);
void initRootContext();
void ensureContextStackSize();
};
template <typename T>

View File

@ -370,7 +370,7 @@ Value ScriptFunction::construct(Managed *that, ExecutionContext *context, Value
try {
result = f->function->code(ctx, f->function->codeData);
} catch (Exception &ex) {
ex.partiallyUnwindContext(ctx->parent);
ex.partiallyUnwindContext(context);
throw;
}
ctx->engine->popContext();
@ -398,7 +398,7 @@ Value ScriptFunction::call(Managed *that, ExecutionContext *context, const Value
try {
result = f->function->code(ctx, f->function->codeData);
} catch (Exception &ex) {
ex.partiallyUnwindContext(ctx->parent);
ex.partiallyUnwindContext(context);
throw;
}
ctx->engine->popContext();
@ -442,7 +442,7 @@ Value BuiltinFunctionOld::call(Managed *that, ExecutionContext *context, const V
try {
result = f->code(ctx);
} catch (Exception &ex) {
ex.partiallyUnwindContext(ctx->parent);
ex.partiallyUnwindContext(context);
throw;
}

View File

@ -370,12 +370,7 @@ Value EvalFunction::evalCall(ExecutionContext *parentContext, Value /*thisObject
if (!directCall) {
// the context for eval should be the global scope, so we fake a root
// context
ctx = new ExecutionContext();
ctx->init(parentContext->engine);
ctx->activation = engine->globalObject.asObject();
ctx->strictMode = engine->rootContext->strictMode;
ctx->parent = parentContext;
ctx->engine->current = ctx;
ctx = engine->pushGlobalContext();
}
if (!args[0].isString())

View File

@ -196,8 +196,9 @@ void MemoryManager::mark()
{
m_d->engine->markObjects();
for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent)
ctxt->mark();
ExecutionEngine *e = engine();
for (int i = 0; i <= e->contextStackPosition; ++i)
e->contextStack[i]->mark();
for (QHash<Managed *, uint>::const_iterator it = m_d->protectedObject.begin(); it != m_d->protectedObject.constEnd(); ++it)
it.key()->mark();