From ad1e9fb11927d470184b0f5bb2d702e563ff68d1 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 14 Dec 2012 09:57:02 +0100 Subject: [PATCH] Simplify/speed up retrieving of properties The hasProperty()/get() sequence used so far is as in the spec, but requires us to lookup the name twice. Instead add a bool hasProperty() to Object::__get__() and use that. Speeds up fact.2.js by ~20% Change-Id: Ic8c84718f1a702c3da9487010c0d6dd0fee44609 Reviewed-by: Simon Hausmann --- qmljs_environment.cpp | 36 +++++++++++++++++++-------- qmljs_objects.cpp | 67 +++++++++++++++++++++++++++++++++++---------------- qmljs_objects.h | 10 ++++---- qv4ecmaobjects.cpp | 9 ++++--- qv4ecmaobjects_p.h | 2 +- 5 files changed, 84 insertions(+), 40 deletions(-) diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index ef55252..a7cf2ce 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -148,8 +148,12 @@ Value ExecutionContext::getBindingValue(ExecutionContext *scope, String *name, b if (__qmljs_string_equal(formals()[i], name)) return arguments[i]; } - if (activation && activation->__hasProperty__(this, name)) - return activation->__get__(scope, name); + if (activation) { + bool hasProperty = false; + Value v = activation->__get__(scope, name, &hasProperty); + if (hasProperty) + return v; + } assert(false); } @@ -272,8 +276,10 @@ Value ExecutionContext::getProperty(String *name) if (ctx->withObject) { With *w = ctx->withObject; while (w) { - if (w->object->__hasProperty__(ctx, name)) - return w->object->__get__(ctx, name); + bool hasProperty = false; + Value v = w->object->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; w = w->next; } } @@ -284,8 +290,12 @@ Value ExecutionContext::getProperty(String *name) 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 (ctx->activation) { + bool hasProperty = false; + Value v = ctx->activation->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; + } if (name->isEqualTo(ctx->engine->id_arguments)) { Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this)); createMutableBinding(ctx->engine->id_arguments, false); @@ -306,8 +316,10 @@ Value ExecutionContext::getPropertyNoThrow(String *name) if (ctx->withObject) { With *w = ctx->withObject; while (w) { - if (w->object->__hasProperty__(ctx, name)) - return w->object->__get__(ctx, name); + bool hasProperty = false; + Value v = w->object->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; w = w->next; } } @@ -318,8 +330,12 @@ Value ExecutionContext::getPropertyNoThrow(String *name) 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 (ctx->activation) { + bool hasProperty = false; + Value v = ctx->activation->__get__(ctx, name, &hasProperty); + if (hasProperty) + return v; + } if (name->isEqualTo(ctx->engine->id_arguments)) { Value arguments = Value::fromObject(new (engine->memoryManager) ArgumentsObject(this)); createMutableBinding(ctx->engine->id_arguments, false); diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 616fe82..eb0f150 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -184,8 +184,9 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *, String *name) // Section 8.12.2 PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) { - if (PropertyDescriptor *p = __getOwnProperty__(ctx, name)) - return p; + if (members) + if (PropertyDescriptor *p = members->find(name)) + return p; if (prototype) return prototype->__getPropertyDescriptor__(ctx, name, to_fill); @@ -193,15 +194,23 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, Str } // Section 8.12.3 -Value Object::__get__(ExecutionContext *ctx, String *name) +Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { - if (name->isEqualTo(ctx->engine->id___proto__)) + if (name->isEqualTo(ctx->engine->id___proto__)) { + if (hasProperty) + *hasProperty = true; return Value::fromObject(prototype); + } PropertyDescriptor tmp; - if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name, &tmp)) + if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, name, &tmp)) { + if (hasProperty) + *hasProperty = true; return getValue(ctx, p); + } + if (hasProperty) + *hasProperty = false; return Value::undefinedValue(); } @@ -430,11 +439,14 @@ void ForEachIteratorObject::getCollectables(QVector &objects) objects.append(current); } -Value ArrayObject::__get__(ExecutionContext *ctx, String *name) +Value ArrayObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { - if (name->isEqualTo(ctx->engine->id_length)) + if (name->isEqualTo(ctx->engine->id_length)) { + if (hasProperty) + *hasProperty = true; return Value::fromDouble(value.size()); - return Object::__get__(ctx, name); + } + return Object::__get__(ctx, name, hasProperty); } bool ArrayObject::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx) @@ -718,28 +730,38 @@ Value IsFiniteFunction::call(ExecutionContext *context, Value /*thisObject*/, Va } -Value RegExpObject::__get__(ExecutionContext *ctx, String *name) +Value RegExpObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { QString n = name->toQString(); + Value v = Value::undefinedValue(); if (n == QLatin1String("source")) - return Value::fromString(ctx, value.pattern()); + v = Value::fromString(ctx, value.pattern()); else if (n == QLatin1String("global")) - return Value::fromBoolean(global); + v = Value::fromBoolean(global); else if (n == QLatin1String("ignoreCase")) - return Value::fromBoolean(value.patternOptions() & QRegularExpression::CaseInsensitiveOption); + v = Value::fromBoolean(value.patternOptions() & QRegularExpression::CaseInsensitiveOption); else if (n == QLatin1String("multiline")) - return Value::fromBoolean(value.patternOptions() & QRegularExpression::MultilineOption); + v = Value::fromBoolean(value.patternOptions() & QRegularExpression::MultilineOption); else if (n == QLatin1String("lastIndex")) - return lastIndex; - return Object::__get__(ctx, name); + v = lastIndex; + if (v.type() != Value::Undefined_Type) { + if (hasProperty) + *hasProperty = true; + return v; + } + + return Object::__get__(ctx, name, hasProperty); } -Value ErrorObject::__get__(ExecutionContext *ctx, String *name) +Value ErrorObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { QString n = name->toQString(); - if (n == QLatin1String("message")) + if (n == QLatin1String("message")) { + if (hasProperty) + *hasProperty = true; return value; - return Object::__get__(ctx, name); + } + return Object::__get__(ctx, name, hasProperty); } void ErrorObject::setNameProperty(ExecutionContext *ctx) @@ -775,11 +797,14 @@ Value ScriptFunction::construct(VM::ExecutionContext *ctx) return ctx->thisObject; } -Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name) +Value ArgumentsObject::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { - if (name->isEqualTo(ctx->engine->id_length)) + if (name->isEqualTo(ctx->engine->id_length)) { + if (hasProperty) + *hasProperty = true; return Value::fromInt32(context->argumentCount); - return Object::__get__(ctx, name); + } + return Object::__get__(ctx, name, hasProperty); } PropertyDescriptor *ArgumentsObject::__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill) diff --git a/qmljs_objects.h b/qmljs_objects.h index 419ada7..9e3edeb 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -440,7 +440,7 @@ struct Object: Managed { virtual ErrorObject *asErrorObject() { return 0; } virtual ArgumentsObject *asArgumentsObject() { return 0; } - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); virtual PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); virtual void __put__(ExecutionContext *ctx, String *name, Value value); @@ -519,7 +519,7 @@ struct ArrayObject: Object { ArrayObject(const Array &value): value(value) {} virtual QString className() { return QStringLiteral("Array"); } virtual ArrayObject *asArrayObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); virtual bool inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ctx); @@ -642,7 +642,7 @@ struct RegExpObject: Object { RegExpObject(const QRegularExpression &value, bool global): value(value), lastIndex(Value::fromInt32(0)), global(global) {} virtual QString className() { return QStringLiteral("RegExp"); } virtual RegExpObject *asRegExpObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); }; struct ErrorObject: Object { @@ -650,7 +650,7 @@ struct ErrorObject: Object { ErrorObject(const Value &message): value(message) {} virtual QString className() { return QStringLiteral("Error"); } virtual ErrorObject *asErrorObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); virtual struct SyntaxErrorObject *asSyntaxError() { return 0; } @@ -710,7 +710,7 @@ struct ArgumentsObject: Object { ArgumentsObject(ExecutionContext *context): context(context) {} virtual QString className() { return QStringLiteral("Arguments"); } virtual ArgumentsObject *asArgumentsObject() { return this; } - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name, PropertyDescriptor *to_fill); }; diff --git a/qv4ecmaobjects.cpp b/qv4ecmaobjects.cpp index bd8969d..c9e0ea6 100644 --- a/qv4ecmaobjects.cpp +++ b/qv4ecmaobjects.cpp @@ -542,11 +542,14 @@ Value ObjectCtor::call(ExecutionContext *ctx) return __qmljs_to_object(ctx->argument(0), ctx); } -Value ObjectCtor::__get__(ExecutionContext *ctx, String *name) +Value ObjectCtor::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) { - if (name == ctx->engine->id_length) + if (name == ctx->engine->id_length) { + if (hasProperty) + *hasProperty = true; return Value::fromDouble(1); - return Object::__get__(ctx, name); + } + return Object::__get__(ctx, name, hasProperty); } void ObjectPrototype::init(ExecutionContext *ctx, const Value &ctor) diff --git a/qv4ecmaobjects_p.h b/qv4ecmaobjects_p.h index e6ab1d7..be4b938 100644 --- a/qv4ecmaobjects_p.h +++ b/qv4ecmaobjects_p.h @@ -53,7 +53,7 @@ struct ObjectCtor: FunctionObject virtual Value construct(ExecutionContext *ctx); virtual Value call(ExecutionContext *ctx); - virtual Value __get__(ExecutionContext *ctx, String *name); + virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty); }; struct ObjectPrototype: Object -- 2.7.4