From 6b566d815f6fbe281233286fd3d4f8138aca088f Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 24 Jan 2013 11:58:46 +0100 Subject: [PATCH] Create a real execution context for with() statements This is required for full spec compliance. Change-Id: I0c1a3ed249a458032d29ba80650a5fdc2eac5c01 Reviewed-by: Simon Hausmann --- moth/qv4vme_moth.cpp | 4 +- qmljs_engine.h | 1 - qmljs_environment.cpp | 146 +++++++++++++++++++++++++++---------------------- qmljs_environment.h | 12 ++-- qmljs_runtime.cpp | 18 ++---- qmljs_runtime.h | 4 +- qv4isel_masm.cpp | 4 +- qv4mm.cpp | 5 +- tests/TestExpectations | 25 +-------- 9 files changed, 101 insertions(+), 118 deletions(-) diff --git a/moth/qv4vme_moth.cpp b/moth/qv4vme_moth.cpp index fab02c4..0a1b3a2 100644 --- a/moth/qv4vme_moth.cpp +++ b/moth/qv4vme_moth.cpp @@ -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!"); diff --git a/qmljs_engine.h b/qmljs_engine.h index 2b8b8b5..7bf3957 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -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; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 9d81bb7..627a988 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -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; diff --git a/qmljs_environment.h b/qmljs_environment.h index b5f6802..16b8832 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -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(); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 1accdea..a8610b2 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -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) diff --git a/qmljs_runtime.h b/qmljs_runtime.h index 1b638ef..2d280d7 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -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); diff --git a/qv4isel_masm.cpp b/qv4isel_masm.cpp index 844f786..d202475 100644 --- a/qv4isel_masm.cpp +++ b/qv4isel_masm.cpp @@ -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) diff --git a/qv4mm.cpp b/qv4mm.cpp index f624cec..895026d 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -345,9 +345,8 @@ void MemoryManager::collectRoots(QVector &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) { diff --git a/tests/TestExpectations b/tests/TestExpectations index 61ab662..faec629 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -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 -- 2.7.4