From 46953ed4d0a03d07c7d1c0e4aeeb5b0258909097 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Tue, 4 Dec 2012 13:46:48 -0800 Subject: [PATCH] Get rid of the ActivationObject Also implement __qmljs_xxx_activation_property in a more correct way. Change-Id: I60c330bccca21fad99930987ed78153114a80c7d Reviewed-by: Simon Hausmann --- main.cpp | 1 + qmljs_engine.cpp | 21 ++++++++--- qmljs_engine.h | 3 +- qmljs_environment.cpp | 100 ++++++++++++++++++++++++++++++++++++++++---------- qmljs_environment.h | 8 ++-- qmljs_objects.cpp | 44 +--------------------- qmljs_objects.h | 14 +------ qmljs_runtime.cpp | 27 +++----------- qmljs_runtime.h | 1 - qmljs_value.cpp | 5 --- qmljs_value.h | 2 - 11 files changed, 112 insertions(+), 114 deletions(-) diff --git a/main.cpp b/main.cpp index a516953..6f6d512 100644 --- a/main.cpp +++ b/main.cpp @@ -357,6 +357,7 @@ int main(int argc, char *argv[]) QQmlJS::IR::Function *f = QQmlJS::VM::EvalFunction::parseSource(ctx, fn, code, QQmlJS::Codegen::GlobalCode); if (!f) continue; + ctx->strictMode = f->isStrict; if (debugger) debugger->aboutToCall(0, ctx); diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index 027cc33..6617810 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -176,9 +176,20 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) glo->__put__(rootContext, identifier(QStringLiteral("TypeError")), typeErrorCtor); glo->__put__(rootContext, identifier(QStringLiteral("URIError")), uRIErrorCtor); glo->__put__(rootContext, identifier(QStringLiteral("Math")), Value::fromObject(newMathObject(rootContext))); - glo->__put__(rootContext, identifier(QStringLiteral("undefined")), Value::undefinedValue()); - glo->__put__(rootContext, identifier(QStringLiteral("NaN")), Value::fromDouble(nan(""))); - glo->__put__(rootContext, identifier(QStringLiteral("Infinity")), Value::fromDouble(INFINITY)); + + PropertyDescriptor pd; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Unset; + pd.enumberable = PropertyDescriptor::Unset; + pd.configurable = PropertyDescriptor::Unset; + + pd.value = Value::undefinedValue(); + glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("undefined")), &pd); + pd.value = Value::fromDouble(nan("")); + glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("NaN")), &pd); + pd.value = Value::fromDouble(INFINITY); + glo->__defineOwnProperty__(rootContext, identifier(QStringLiteral("Infinity")), &pd); + glo->__put__(rootContext, identifier(QStringLiteral("eval")), Value::fromObject(new EvalFunction(rootContext))); // TODO: parseInt [15.1.2.2] @@ -369,9 +380,9 @@ Object *ExecutionEngine::newMathObject(ExecutionContext *ctx) return object; } -Object *ExecutionEngine::newActivationObject(ExecutionContext *ctx) +Object *ExecutionEngine::newActivationObject() { - return new ActivationObject(ctx); + return new Object(); } Object *ExecutionEngine::newForEachIteratorObject(Object *o) diff --git a/qmljs_engine.h b/qmljs_engine.h index 2b053fd..2837adb 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -65,7 +65,6 @@ struct DateObject; struct FunctionObject; struct RegExpObject; struct ErrorObject; -struct ActivationObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; @@ -190,7 +189,7 @@ struct ExecutionEngine Object *newTypeErrorObject(ExecutionContext *ctx, const QString &message); Object *newMathObject(ExecutionContext *ctx); - Object *newActivationObject(ExecutionContext *ctx); + Object *newActivationObject(); Object *newForEachIteratorObject(Object *o); }; diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 12a2091..c542422 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -90,15 +90,17 @@ bool ExecutionContext::hasBinding(String *name) const if (__qmljs_string_equal(function->formalParameterList[i], name)) return true; } + if (activation) + return activation->__hasProperty__(this, name); return false; } -void ExecutionContext::createMutableBinding(ExecutionContext *ctx, String *name, bool deletable) +void ExecutionContext::createMutableBinding(String *name, bool deletable) { if (!activation) - activation = engine->newActivationObject(this); + activation = engine->newActivationObject(); - if (activation->__hasProperty__(ctx, name)) + if (activation->__hasProperty__(this, name)) return; PropertyDescriptor desc; desc.value = Value::undefinedValue(); @@ -106,31 +108,33 @@ void ExecutionContext::createMutableBinding(ExecutionContext *ctx, String *name, desc.configurable = deletable ? PropertyDescriptor::Set : PropertyDescriptor::Unset; desc.writable = PropertyDescriptor::Set; desc.enumberable = PropertyDescriptor::Set; - activation->__defineOwnProperty__(ctx, name, &desc); + activation->__defineOwnProperty__(this, name, &desc); } -void ExecutionContext::setMutableBinding(String *name, Value value, bool strict) +bool ExecutionContext::setMutableBinding(ExecutionContext *scope, String *name, Value value) { - Q_UNUSED(strict); - assert(function); - - // ### throw if strict is true, and it would change an immutable binding + // ### throw if scope->strict is true, and it would change an immutable binding for (unsigned int i = 0; i < variableCount(); ++i) { if (__qmljs_string_equal(variables()[i], name)) { locals[i] = value; - return; + return true; } } for (unsigned int i = 0; i < formalCount(); ++i) { if (__qmljs_string_equal(formals()[i], name)) { arguments[i] = value; - return; + return true; } } - assert(false); + if (activation && activation->__hasProperty__(scope, name)) { + activation->__put__(scope, name, value); + return true; + } + + return false; } -Value ExecutionContext::getBindingValue(String *name, bool strict) const +Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, bool strict) const { Q_UNUSED(strict); assert(function); @@ -143,16 +147,18 @@ Value ExecutionContext::getBindingValue(String *name, bool strict) const if (__qmljs_string_equal(formals()[i], name)) return arguments[i]; } + if (activation && activation->__hasProperty__(this, name)) + return activation->__get__(scope, name); assert(false); } -bool ExecutionContext::deleteBinding(ExecutionContext *ctx, String *name) +bool ExecutionContext::deleteBinding(ExecutionContext *scope, String *name) { if (activation) - activation->__delete__(ctx, name); + activation->__delete__(scope, name); - if (ctx->strictMode) - __qmljs_throw_type_error(ctx); + if (scope->strictMode) + __qmljs_throw_type_error(scope); return false; } @@ -251,10 +257,66 @@ bool ExecutionContext::deleteProperty(String *name) ctx->activation->__delete__(this, name); } } - // ### throw syntax error in strict mode + if (strictMode) + throwSyntaxError(0); return true; } +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; + } + } + if (ctx->setMutableBinding(this, name, value)) + return; + } + if (strictMode) + throwReferenceError(Value::fromString(name)); + engine->globalObject.objectValue()->__put__(this, name, value); +} + +Value ExecutionContext::getProperty(String *name) +{ + PropertyDescriptor tmp; + for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { + if (ctx->withObject) { + With *w = ctx->withObject; + while (w) { + if (PropertyDescriptor *pd = w->object->__getPropertyDescriptor__(this, name, &tmp)) + return pd->value; + w = w->next; + } + } + + for (unsigned int i = 0; i < ctx->variableCount(); ++i) + if (__qmljs_string_equal(ctx->variables()[i], name)) + return ctx->locals[i]; + for (unsigned int i = 0; i < ctx->formalCount(); ++i) + if (__qmljs_string_equal(ctx->formals()[i], name)) + return ctx->arguments[i]; + if (ctx->activation && ctx->activation->__hasProperty__(ctx, name)) + return ctx->activation->__get__(ctx, name); + if (name->isEqualTo(ctx->engine->id_arguments)) { + Value arguments = Value::fromObject(new ArgumentsObject(this)); + createMutableBinding(ctx->engine->id_arguments, false); + setMutableBinding(this, ctx->engine->id_arguments, arguments); + return arguments; + } + } + throwReferenceError(Value::fromString(name)); + return Value::undefinedValue(); +} + + + void ExecutionContext::inplaceBitOp(Value value, String *name, BinOp op) { for (ExecutionContext *ctx = this; ctx; ctx = ctx->outer()) { @@ -323,7 +385,7 @@ void ExecutionContext::initCallContext(ExecutionContext *parent, const Value tha std::fill(locals, locals + function->varCount, Value::undefinedValue()); if (function->needsActivation) - activation = engine->newActivationObject(this); + activation = engine->newActivationObject(); else activation = 0; diff --git a/qmljs_environment.h b/qmljs_environment.h index eb10b66..dca22dc 100644 --- a/qmljs_environment.h +++ b/qmljs_environment.h @@ -100,9 +100,9 @@ struct ExecutionContext void init(ExecutionEngine *e); bool hasBinding(String *name) const; - void createMutableBinding(ExecutionContext *ctx, String *name, bool deletable); - void setMutableBinding(String *name, Value value, bool strict); - Value getBindingValue(String *name, bool strict) const; + void createMutableBinding(String *name, bool deletable); + bool setMutableBinding(ExecutionContext *scope, String *name, Value value); + Value getBindingValue(ExecutionContext *scope, String *name, bool strict) const; bool deleteBinding(ExecutionContext *ctx, String *name); void pushWithObject(Object *with); @@ -121,6 +121,8 @@ struct ExecutionContext void throwUnimplemented(const QString &message); PropertyDescriptor *lookupPropertyDescriptor(String *name, PropertyDescriptor *tmp); + void setProperty(String *name, Value value); + Value getProperty(String *name); void inplaceBitOp(Value value, String *name, BinOp op); bool deleteProperty(String *name); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 12cc447..7fdb86a 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -216,7 +216,7 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) } // Section 8.12.6 -bool Object::__hasProperty__(ExecutionContext *ctx, String *name) const +bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const { if (members) return members->find(name) != 0; @@ -592,12 +592,6 @@ QQmlJS::IR::Function *EvalFunction::parseSource(QQmlJS::VM::ExecutionContext *ct __qmljs_throw_type_error(ctx); } - if (!ctx->activation) - ctx->activation = new QQmlJS::VM::Object(); - - foreach (const QString *local, globalCode->locals) { - ctx->activation->__put__(ctx, *local, QQmlJS::VM::Value::undefinedValue()); - } return globalCode; } @@ -681,42 +675,6 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx) return ctx->thisObject; } -PropertyDescriptor *ActivationObject::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) -{ - if (context) { - for (unsigned int i = 0; i < context->variableCount(); ++i) { - String *var = context->variables()[i]; - if (__qmljs_string_equal(var, name)) { - *to_fill = PropertyDescriptor::fromValue(context->locals[i]); - to_fill->writable = PropertyDescriptor::Set; - to_fill->enumberable = PropertyDescriptor::Set; - return to_fill; - } - } - for (unsigned int i = 0; i < context->formalCount(); ++i) { - String *formal = context->formals()[i]; - if (__qmljs_string_equal(formal, name)) { - *to_fill = PropertyDescriptor::fromValue(context->arguments[i]); - to_fill->writable = PropertyDescriptor::Set; - to_fill->enumberable = PropertyDescriptor::Set; - return to_fill; - } - } - if (name->isEqualTo(ctx->engine->id_arguments)) { - if (arguments.isUndefined()) { - arguments = Value::fromObject(new ArgumentsObject(ctx)); - arguments.objectValue()->prototype = ctx->engine->objectPrototype; - } - - *to_fill = PropertyDescriptor::fromValue(arguments); - to_fill->writable = PropertyDescriptor::Unset; - to_fill->enumberable = PropertyDescriptor::Unset; - return to_fill; - } - } - - return Object::__getPropertyDescriptor__(ctx, name, to_fill); -} Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name) { diff --git a/qmljs_objects.h b/qmljs_objects.h index 4a98ee1..fc8deed 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -72,7 +72,6 @@ struct DateObject; struct FunctionObject; struct RegExpObject; struct ErrorObject; -struct ActivationObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; @@ -419,7 +418,6 @@ struct Object { virtual FunctionObject *asFunctionObject() { return 0; } virtual RegExpObject *asRegExpObject() { return 0; } virtual ErrorObject *asErrorObject() { return 0; } - virtual ActivationObject *asActivationObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } virtual Value __get__(ExecutionContext *ctx, String *name); @@ -427,7 +425,7 @@ struct Object { virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); virtual void __put__(ExecutionContext *ctx, String *name, Value value); virtual bool __canPut__(ExecutionContext *ctx, String *name); - virtual bool __hasProperty__(ExecutionContext *ctx, String *name) const; + virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; virtual bool __delete__(ExecutionContext *ctx, String *name); virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc); @@ -641,16 +639,6 @@ struct URIErrorObject: ErrorObject { virtual QString className() { return QStringLiteral("URIError"); } }; -struct ActivationObject: Object { - ExecutionContext *context; - Value arguments; - ActivationObject(ExecutionContext *context) - : context(context), arguments(Value::undefinedValue()) {} - virtual QString className() { return QStringLiteral("Activation"); } - virtual ActivationObject *asActivationObject() { return this; } - virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); -}; - struct ArgumentsObject: Object { ExecutionContext *context; ArgumentsObject(ExecutionContext *context): context(context) {} diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 20002aa..d7bbf0e 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -600,15 +600,7 @@ Value __qmljs_foreach_next_property_name(Value foreach_iterator) void __qmljs_set_activation_property(ExecutionContext *ctx, String *name, Value value) { - PropertyDescriptor tmp; - PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp); - if (prop) { - prop->value = value; - return; - } - if (ctx->strictMode) - ctx->throwReferenceError(Value::fromString(name)); - ctx->engine->globalObject.objectValue()->__put__(ctx, name, value); + ctx->setProperty(name, value); } Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) @@ -631,11 +623,7 @@ Value __qmljs_get_property(ExecutionContext *ctx, Value object, String *name) Value __qmljs_get_activation_property(ExecutionContext *ctx, String *name) { - PropertyDescriptor tmp; - PropertyDescriptor *prop = ctx->lookupPropertyDescriptor(name, &tmp); - if (!prop) - ctx->throwReferenceError(Value::fromString(name)); - return prop->value; + return ctx->getProperty(name); } Value __qmljs_get_thisObject(ExecutionContext *ctx) @@ -700,7 +688,7 @@ uint __qmljs_equal(Value x, Value y, ExecutionContext *ctx) // TODO: remove this function. Backends should just generate a __qmljs_get_activation_property followed by a __qmljs_call_value Value __qmljs_call_activation_property(ExecutionContext *context, String *name, Value *args, int argc) { - Value func = __qmljs_get_activation_property(context, name); + Value func = context->getProperty(name); Object *o = func.asObject(); if (!o) context->throwReferenceError(Value::fromString(name)); @@ -734,11 +722,8 @@ Value __qmljs_call_value(ExecutionContext *context, Value thisObject, Value func Value __qmljs_construct_activation_property(ExecutionContext *context, String *name, Value *args, int argc) { - PropertyDescriptor tmp; - PropertyDescriptor *func = context->lookupPropertyDescriptor(name, &tmp); - if (! func) - context->throwReferenceError(Value::fromString(name)); - return __qmljs_construct_value(context, func->value, args, argc); + Value func = context->getProperty(name); + return __qmljs_construct_value(context, func, args, argc); } Value __qmljs_construct_value(ExecutionContext *context, Value func, Value *args, int argc) @@ -836,7 +821,7 @@ void __qmljs_builtin_pop_with(ExecutionContext *ctx) void __qmljs_builtin_declare_var(ExecutionContext *ctx, bool deletable, String *name) { - ctx->createMutableBinding(ctx, name, deletable); + ctx->createMutableBinding(name, deletable); } } // extern "C" diff --git a/qmljs_runtime.h b/qmljs_runtime.h index d6a7839..adcdae6 100644 --- a/qmljs_runtime.h +++ b/qmljs_runtime.h @@ -85,7 +85,6 @@ struct DateObject; struct RegExpObject; struct ArrayObject; struct ErrorObject; -struct ActivationObject; struct ExecutionEngine; extern "C" { diff --git a/qmljs_value.cpp b/qmljs_value.cpp index cbf4813..38e1f5d 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -210,11 +210,6 @@ ErrorObject *Value::asErrorObject() const return isObject() ? objectValue()->asErrorObject() : 0; } -ActivationObject *Value::asArgumentsObject() const -{ - return isObject() ? objectValue()->asActivationObject() : 0; -} - Value Value::property(ExecutionContext *ctx, String *name) const { return isObject() ? objectValue()->__get__(ctx, name) : undefinedValue(); diff --git a/qmljs_value.h b/qmljs_value.h index e15b970..3d29deb 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -61,7 +61,6 @@ struct DateObject; struct FunctionObject; struct RegExpObject; struct ErrorObject; -struct ActivationObject; struct ArgumentsObject; struct ExecutionContext; struct ExecutionEngine; @@ -238,7 +237,6 @@ struct Value RegExpObject *asRegExpObject() const; ArrayObject *asArrayObject() const; ErrorObject *asErrorObject() const; - ActivationObject *asArgumentsObject() const; Value property(ExecutionContext *ctx, String *name) const; -- 2.7.4