Create a real execution context for with() statements
authorLars Knoll <lars.knoll@digia.com>
Thu, 24 Jan 2013 10:58:46 +0000 (11:58 +0100)
committerSimon Hausmann <simon.hausmann@digia.com>
Thu, 24 Jan 2013 11:30:49 +0000 (12:30 +0100)
This is required for full spec compliance.

Change-Id: I0c1a3ed249a458032d29ba80650a5fdc2eac5c01
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
moth/qv4vme_moth.cpp
qmljs_engine.h
qmljs_environment.cpp
qmljs_environment.h
qmljs_runtime.cpp
qmljs_runtime.h
qv4isel_masm.cpp
qv4mm.cpp
tests/TestExpectations

index fab02c4..0a1b3a2 100644 (file)
@@ -276,10 +276,10 @@ VM::Value VME::operator()(QQmlJS::VM::ExecutionContext *context, const uchar *co
             TEMP(instr.targetTempIndex) = __qmljs_get_exception(context);
             break;
         case Instr::instr_callBuiltin::builtin_push_with:
-            __qmljs_builtin_push_with(TEMP(instr.argTemp), context);
+            context = __qmljs_builtin_push_with(TEMP(instr.argTemp), context);
             break;
         case Instr::instr_callBuiltin::builtin_pop_with:
-            __qmljs_builtin_pop_with(context);
+            context = __qmljs_builtin_pop_with(context);
             break;
         default:
             assert(!"TODO!");
index 2b8b8b5..7bf3957 100644 (file)
@@ -159,7 +159,6 @@ struct ExecutionEngine
 
     struct ExceptionHandler {
         ExecutionContext *context;
-        ExecutionContext::With *with;
         const uchar *code; // Interpreter state
         int targetTempIndex; // Interpreter state
         jmp_buf stackFrame;
index 9d81bb7..627a988 100644 (file)
@@ -170,26 +170,22 @@ bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name)
     return false;
 }
 
-void ExecutionContext::pushWithObject(Object *with)
+ExecutionContext *ExecutionContext::pushWithObject(Object *with)
 {
-    With *w = new With;
-    w->next = withObject;
-    w->object = with;
-    withObject = w;
+    ExecutionContext *withCtx = engine->newContext();
+    withCtx->init(this, with);
+    engine->current = withCtx;
+    return withCtx;
 }
 
-void ExecutionContext::popWithObject()
+ExecutionContext *ExecutionContext::popWithObject()
 {
-    assert(withObject);
+    assert(engine->current == this);
+    assert(withObject != 0);
 
-    With *w = withObject;
-    withObject = w->next;
-    delete w;
-}
-
-ExecutionContext *ExecutionContext::outer() const
-{
-    return function ? function->scope : 0;
+    engine->current = parent;
+    parent = 0;
+    return engine->current;
 }
 
 String **ExecutionContext::formals() const
@@ -217,6 +213,7 @@ void ExecutionContext::init(ExecutionEngine *eng)
 {
     engine = eng;
     parent = 0;
+    outer = 0;
     thisObject = eng->globalObject;
 
     function = 0;
@@ -230,6 +227,22 @@ void ExecutionContext::init(ExecutionEngine *eng)
     eng->exception = Value::undefinedValue();
 }
 
+void ExecutionContext::init(ExecutionContext *p, Object *with)
+{
+    engine = p->engine;
+    parent = p;
+    outer = p;
+    thisObject = p->thisObject;
+
+    function = 0;
+    arguments = 0;
+    argumentCount = 0;
+    locals = 0;
+    strictMode = false;
+    activation = 0;
+    withObject = with;
+}
+
 void ExecutionContext::destroy()
 {
     delete[] arguments;
@@ -238,17 +251,14 @@ void ExecutionContext::destroy()
 
 bool ExecutionContext::deleteProperty(String *name)
 {
-    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
+    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
         if (ctx->withObject) {
-            ExecutionContext::With *w = ctx->withObject;
-            while (w) {
-                if (w->object->__hasProperty__(this, name))
-                    return w->object->__delete__(this, name);
-                w = w->next;
-            }
+            if (ctx->withObject->__hasProperty__(this, name))
+                return ctx->withObject->__delete__(this, name);
+        } else {
+            if (ctx->activation && ctx->activation->__hasProperty__(this, name))
+                return ctx->activation->__delete__(this, name);
         }
-        if (ctx->activation && ctx->activation->__hasProperty__(this, name))
-            return ctx->activation->__delete__(this, name);
     }
     if (strictMode)
         throwSyntaxError(0);
@@ -257,19 +267,20 @@ bool ExecutionContext::deleteProperty(String *name)
 
 void ExecutionContext::setProperty(String *name, Value value)
 {
-    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
-        if (ctx->withObject) {
-            With *w = ctx->withObject;
-            while (w) {
-                if (w->object->__hasProperty__(ctx, name)) {
-                    w->object->__put__(ctx, name, value);
-                    return;
-                }
-                w = w->next;
+//    qDebug() << "=== SetProperty" << value.toString(this)->toQString();
+    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+        if (Object *w = ctx->withObject) {
+//            qDebug() << ctx << "hasWith";
+            if (w->__hasProperty__(ctx, name)) {
+//                qDebug() << "   withHasProp";
+                w->__put__(ctx, name, value);
+                return;
             }
+        } else {
+//            qDebug() << ctx << "setting mutable binding";
+            if (ctx->setMutableBinding(this, name, value))
+                return;
         }
-        if (ctx->setMutableBinding(this, name, value))
-            return;
     }
     if (strictMode || name == engine->id_this)
         throwReferenceError(Value::fromString(name));
@@ -281,19 +292,23 @@ Value ExecutionContext::getProperty(String *name)
     if (name == engine->id_this)
         return thisObject;
 
-    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
-        if (With *w = ctx->withObject) {
-            while (w) {
-                bool hasProperty = false;
-                Value v = w->object->__get__(ctx, name, &hasProperty);
-                if (hasProperty)
-                    return v;
-                w = w->next;
+    bool hasWith = false;
+//    qDebug() << "=== getProperty" << name->toQString();
+    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+        if (Object *w = ctx->withObject) {
+            hasWith = true;
+//            qDebug() << ctx << "hasWith";
+            bool hasProperty = false;
+            Value v = w->__get__(ctx, name, &hasProperty);
+            if (hasProperty) {
+//                qDebug() << "   withHasProp";
+                return v;
             }
+            continue;
         }
 
         if (FunctionObject *f = ctx->function) {
-            if (f->needsActivation || ctx->withObject) {
+            if (f->needsActivation || hasWith) {
                 for (unsigned int i = 0; i < f->varCount; ++i)
                     if (f->varList[i]->isEqualTo(name))
                         return ctx->locals[i];
@@ -318,19 +333,19 @@ Value ExecutionContext::getPropertyNoThrow(String *name)
     if (name == engine->id_this)
         return thisObject;
 
-    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
-        if (With *w = ctx->withObject) {
-            while (w) {
-                bool hasProperty = false;
-                Value v = w->object->__get__(ctx, name, &hasProperty);
-                if (hasProperty)
-                    return v;
-                w = w->next;
-            }
+    bool hasWith = false;
+    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+        if (Object *w = ctx->withObject) {
+            hasWith = true;
+            bool hasProperty = false;
+            Value v = w->__get__(ctx, name, &hasProperty);
+            if (hasProperty)
+                return v;
+            continue;
         }
 
         if (FunctionObject *f = ctx->function) {
-            if (f->needsActivation || ctx->withObject) {
+            if (f->needsActivation || hasWith) {
                 for (unsigned int i = 0; i < f->varCount; ++i)
                     if (f->varList[i]->isEqualTo(name))
                         return ctx->locals[i];
@@ -356,21 +371,21 @@ Value ExecutionContext::getPropertyAndBase(String *name, Object **base)
     if (name == engine->id_this)
         return thisObject;
 
-    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) {
-        if (With *w = ctx->withObject) {
-            while (w) {
-                bool hasProperty = false;
-                Value v = w->object->__get__(ctx, name, &hasProperty);
-                if (hasProperty) {
-                    *base = w->object;
-                    return v;
-                }
-                w = w->next;
+    bool hasWith = false;
+    for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer) {
+        if (Object *w = ctx->withObject) {
+            hasWith = true;
+            bool hasProperty = false;
+            Value v = w->__get__(ctx, name, &hasProperty);
+            if (hasProperty) {
+                *base = w;
+                return v;
             }
+            continue;
         }
 
         if (FunctionObject *f = ctx->function) {
-            if (f->needsActivation || ctx->withObject) {
+            if (f->needsActivation || hasWith) {
                 for (unsigned int i = 0; i < f->varCount; ++i)
                     if (f->varList[i]->isEqualTo(name))
                         return ctx->locals[i];
@@ -451,6 +466,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha
 
     engine = parent->engine;
     this->parent = parent;
+    outer = f->scope;
     engine->current = this;
 
     function = f;
index b5f6802..16b8832 100644 (file)
@@ -74,6 +74,7 @@ struct ExecutionContext
 {
     ExecutionEngine *engine;
     ExecutionContext *parent;
+    ExecutionContext *outer;
     Value thisObject;
 
     FunctionObject *function;
@@ -86,17 +87,14 @@ struct ExecutionContext
     unsigned int formalCount() const;
     String **variables() const;
     unsigned int variableCount() const;
-    ExecutionContext *outer() const;
 
     bool strictMode;
 
     Object *activation;
-    struct With {
-        Object *object;
-        With *next;
-    } *withObject;
+    Object *withObject;
 
     void init(ExecutionEngine *e);
+    void init(ExecutionContext *p, Object *with);
     void destroy();
 
     bool hasBinding(String *name) const;
@@ -105,8 +103,8 @@ struct ExecutionContext
     Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const;
     bool deleteBinding(ExecutionContext *ctx, String *name);
 
-    void pushWithObject(Object *with);
-    void popWithObject();
+    ExecutionContext *pushWithObject(Object *with);
+    ExecutionContext *popWithObject();
 
     void initCallContext(ExecutionContext *parent, const Value that, FunctionObject *f, Value *args, unsigned argc);
     void leaveCallContext();
index 1accdea..a8610b2 100644 (file)
@@ -813,16 +813,11 @@ void __qmljs_throw(Value value, ExecutionContext *context)
     // clean up call contexts
     while (context != handler.context) {
         ExecutionContext *parent = context->parent;
-        context->leaveCallContext();
+        if (!context->withObject)
+            context->leaveCallContext();
         context = parent;
     }
 
-    while (context->withObject != handler.with) {
-        ExecutionContext::With *w = context->withObject;
-        context->withObject = w->next;
-        delete w;
-    }
-
     context->engine->exception = value;
 
     longjmp(handler.stackFrame, 1);
@@ -834,7 +829,6 @@ void *__qmljs_create_exception_handler(ExecutionContext *context)
     context->engine->unwindStack.append(ExecutionEngine::ExceptionHandler());
     ExecutionEngine::ExceptionHandler &handler = context->engine->unwindStack.last();
     handler.context = context;
-    handler.with = context->withObject;
     return handler.stackFrame;
 }
 
@@ -900,15 +894,15 @@ void __qmljs_builtin_throw(Value val, ExecutionContext *context)
     __qmljs_throw(val, context);
 }
 
-void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx)
+ExecutionContext *__qmljs_builtin_push_with(Value o, ExecutionContext *ctx)
 {
     Object *obj = __qmljs_to_object(o, ctx).asObject();
-    ctx->pushWithObject(obj);
+    return ctx->pushWithObject(obj);
 }
 
-void __qmljs_builtin_pop_with(ExecutionContext *ctx)
+ExecutionContext *__qmljs_builtin_pop_with(ExecutionContext *ctx)
 {
-    ctx->popWithObject();
+    return ctx->popWithObject();
 }
 
 void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name)
index 1b638ef..2d280d7 100644 (file)
@@ -101,8 +101,8 @@ Value __qmljs_builtin_typeof_element(Value base, Value index, ExecutionContext *
 
 void __qmljs_builtin_throw(Value val, ExecutionContext *context);
 void __qmljs_builtin_rethrow(ExecutionContext *context);
-void __qmljs_builtin_push_with(Value o, ExecutionContext *ctx);
-void __qmljs_builtin_pop_with(ExecutionContext *ctx);
+ExecutionContext *__qmljs_builtin_push_with(Value o, ExecutionContext *ctx);
+ExecutionContext *__qmljs_builtin_pop_with(ExecutionContext *ctx);
 void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name);
 void __qmljs_builtin_define_property(Value object, String *name, Value val, ExecutionContext *ctx);
 void __qmljs_builtin_define_getter_setter(Value object, String *name, Value getter, Value setter, ExecutionContext *ctx);
index 844f786..d202475 100644 (file)
@@ -515,12 +515,12 @@ void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Temp *arg, IR:
 
 void InstructionSelection::callBuiltinPushWith(IR::Temp *arg)
 {
-    generateFunctionCall(Assembler::Void, __qmljs_builtin_push_with, arg, Assembler::ContextRegister);
+    generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_push_with, arg, Assembler::ContextRegister);
 }
 
 void InstructionSelection::callBuiltinPopWith()
 {
-    generateFunctionCall(Assembler::Void, __qmljs_builtin_pop_with, Assembler::ContextRegister);
+    generateFunctionCall(Assembler::ContextRegister, __qmljs_builtin_pop_with, Assembler::ContextRegister);
 }
 
 void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name)
index f624cec..895026d 100644 (file)
--- a/qv4mm.cpp
+++ b/qv4mm.cpp
@@ -345,9 +345,8 @@ void MemoryManager::collectRoots(QVector<VM::Object *> &roots) const
             add(roots, ctxt->locals[local]);
         if (ctxt->activation)
             roots.append(ctxt->activation);
-        for (ExecutionContext::With *it = ctxt->withObject; it; it = it->next)
-            if (it->object)
-                roots.append(it->object);
+        if (ctxt->withObject)
+            roots.append(ctxt->withObject);
     }
 
     for (int i = 0; i < m_d->engine->functions.size(); ++i) {
index 61ab662..faec629 100644 (file)
@@ -77,28 +77,11 @@ S11.5.3_A3_T2.9 failing
 S11.5.3_A4_T2 failing
 S11.8.6_A3 failing
 S11.8.6_A5_T2 failing
-12.10-0-3 failing
-S12.10_A1.11_T1 failing
-S12.10_A1.11_T2 failing
-S12.10_A1.11_T4 failing
-S12.10_A1.12_T1 failing
-S12.10_A1.12_T2 failing
-S12.10_A1.12_T4 failing
 S12.10_A1.4_T4 failing
 S12.10_A1.4_T5 failing
 S12.10_A1.5_T4 failing
 S12.10_A1.5_T5 failing
 12.14-13 failing
-S12.10_A3.11_T1 failing
-S12.10_A3.11_T2 failing
-S12.10_A3.11_T3 failing
-S12.10_A3.11_T4 failing
-S12.10_A3.11_T5 failing
-S12.10_A3.12_T1 failing
-S12.10_A3.12_T2 failing
-S12.10_A3.12_T3 failing
-S12.10_A3.12_T4 failing
-S12.10_A3.12_T5 failing
 S12.10_A3.4_T4 failing
 S12.10_A3.4_T5 failing
 S12.10_A3.5_T4 failing
@@ -117,12 +100,6 @@ S13_A3_T1 failing
 13.1-40-s failing
 13.1-41-s failing
 13.1-42-s failing
-S13.2.2_A19_T1 failing
-S13.2.2_A19_T3 failing
-S13.2.2_A19_T4 failing
-S13.2.2_A19_T5 failing
-S13.2.2_A19_T6 failing
-S13.2.2_A19_T8 failing
 S13.2.3_A1 failing
 S14_A2 failing
 14.1-5-s failing
@@ -187,4 +164,4 @@ S12.9_A1_T6 failing
 S12.9_A1_T7 failing
 S12.9_A1_T8 failing
 S12.9_A1_T9 failing
-S15.2.4.4_A14 failing
\ No newline at end of file
+S15.2.4.4_A14 failing