Remove the parent pointer from ExecutionContext
authorLars Knoll <lars.knoll@digia.com>
Thu, 14 Mar 2013 13:03:04 +0000 (14:03 +0100)
committerSimon Hausmann <simon.hausmann@digia.com>
Thu, 14 Mar 2013 20:21:02 +0000 (21:21 +0100)
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>
src/v4/qv4context.cpp
src/v4/qv4context.h
src/v4/qv4engine.cpp
src/v4/qv4engine.h
src/v4/qv4functionobject.cpp
src/v4/qv4globalobject.cpp
src/v4/qv4mm.cpp

index f50740e..5cb1d10 100644 (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();
index 96a3dbf..6112387 100644 (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();
 
index ed28b15..57d4a32 100644 (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;
 }
 
index ebbe33f..adaa159 100644 (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>
index 5336862..e43410e 100644 (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;
     }
 
index 5ba24c9..c0fd2aa 100644 (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())
index 281347b..b02df3b 100644 (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();