From 69a526d37f275531fde3afc7647dd7dc338f5c92 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 17 Jan 2013 00:45:11 +0100 Subject: [PATCH] optimise property access Inline the canPut method and simplify our code path for [[put]]. Change-Id: I4198b0bdef16a4fdf6113a8e015915492c9c301d Reviewed-by: Simon Hausmann --- qmljs_objects.cpp | 200 ++++++++++++++++++++++++------------------------- qmljs_objects.h | 4 +- qmljs_runtime.cpp | 8 +- qv4array.h | 13 +++- tests/TestExpectations | 9 +-- 5 files changed, 116 insertions(+), 118 deletions(-) diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index fee5378..3a700e2 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -280,7 +280,7 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty) Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) { - if (PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index)) { + if (const PropertyDescriptor *p = __getPropertyDescriptor__(ctx, index)) { if (hasProperty) *hasProperty = true; return getValue(ctx, p); @@ -292,55 +292,6 @@ Value Object::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) } -// Section 8.12.4 -bool Object::__canPut__(ExecutionContext *ctx, String *name) -{ - uint idx = name->asArrayIndex(); - if (idx != String::InvalidArrayIndex) - return __canPut__(ctx, idx); - - if (PropertyDescriptor *p = __getOwnProperty__(ctx, name)) { - if (p->isAccessor()) - return p->set != 0; - return p->isWritable(); - } - - if (! prototype) - return extensible; - - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) { - if (p->isAccessor()) - return p->set != 0; - if (!extensible) - return false; - return p->isWritable(); - } - - return extensible; -} - -bool Object::__canPut__(ExecutionContext *ctx, uint index) -{ - if (PropertyDescriptor *p = __getOwnProperty__(ctx, index)) { - if (p->isAccessor()) - return p->set != 0; - return p->isWritable(); - } - - if (! prototype) - return extensible; - - if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) { - if (p->isAccessor()) - return p->set != 0; - if (!extensible) - return false; - return p->isWritable(); - } - - return extensible; -} - // Section 8.12.5 void Object::__put__(ExecutionContext *ctx, String *name, Value value) { @@ -348,42 +299,71 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) if (idx != String::InvalidArrayIndex) return __put__(ctx, idx, value); + PropertyDescriptor *pd = __getOwnProperty__(ctx, name); // clause 1 - if (!__canPut__(ctx, name)) - goto reject; + if (pd) { + if (pd->isAccessor()) { + if (pd->set) + goto cont; + goto reject; + } else if (!pd->isWritable()) + goto reject; + else if (isArray && name->isEqualTo(ctx->engine->id_length)) { + bool ok; + uint l = value.asArrayLength(ctx, &ok); + if (!ok) + ctx->throwRangeError(value); + ok = array.setLength(l); + if (!ok) + goto reject; + } else { + pd->value = value; + } + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, name)) { + if (p->isAccessor()) { + if (p->set) + goto cont; + goto reject; + } + if (!extensible) + goto reject; + if (!p->isWritable()) + goto reject; + } else { + if (!extensible) + goto reject; + } + } + + cont: if (!members) members.reset(new PropertyTable()); - { - // Clause 2 - PropertyDescriptor *pd = __getOwnProperty__(ctx, name); - // Clause 3 - if (pd && pd->isData()) { - // spec says to call [[DefineOwnProperty]] with { [[Value]]: value } - - // ### to simplify and speed up we should expand the relevant parts here (clauses 6,7,9,10,12,13) - PropertyDescriptor desc = PropertyDescriptor::fromValue(value); - __defineOwnProperty__(ctx, name, &desc); - return; - } - // clause 4 - if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, name); + // clause 4 + if (!pd && prototype) + pd = prototype->__getPropertyDescriptor__(ctx, name); - // Clause 5 - if (pd && pd->isAccessor()) { - assert(pd->set != 0); + // Clause 5 + if (pd && pd->isAccessor()) { + assert(pd->set != 0); - Value args[1]; - args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); - return; - } + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); + return; + } + { PropertyDescriptor *p = members->insert(name); - *p = PropertyDescriptor::fromValue(value); + p->type = PropertyDescriptor::Data; + p->value = value; p->configurable = PropertyDescriptor::Enabled; p->enumberable = PropertyDescriptor::Enabled; p->writable = PropertyDescriptor::Enabled; @@ -397,41 +377,57 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value) void Object::__put__(ExecutionContext *ctx, uint index, Value value) { + PropertyDescriptor *pd = __getOwnProperty__(ctx, index); // clause 1 - if (!__canPut__(ctx, index)) - goto reject; - - { - // Clause 2 - PropertyDescriptor *pd = __getOwnProperty__(ctx, index); - // Clause 3 - if (pd && pd->isData()) { - // spec says to call [[DefineOwnProperty]] with { [[Value]]: value } - - // ### to simplify and speed up we should expand the relevant parts here (clauses 6,7,9,10,12,13) - PropertyDescriptor desc = PropertyDescriptor::fromValue(value); - __defineOwnProperty__(ctx, index, &desc); - return; + if (pd) { + if (pd->isAccessor()) { + if (pd->set) + goto cont; + goto reject; + } else if (!pd->isWritable()) + goto reject; + else + pd->value = value; + return; + } else if (!prototype) { + if (!extensible) + goto reject; + } else { + if (PropertyDescriptor *p = prototype->__getPropertyDescriptor__(ctx, index)) { + if (p->isAccessor()) { + if (p->set) + goto cont; + goto reject; + } + if (!extensible) + goto reject; + if (!p->isWritable()) + goto reject; + } else { + if (!extensible) + goto reject; } + } - // clause 4 - if (!pd && prototype) - pd = prototype->__getPropertyDescriptor__(ctx, index); + cont: - // Clause 5 - if (pd && pd->isAccessor()) { - assert(pd->set != 0); + // clause 4 + if (!pd && prototype) + pd = prototype->__getPropertyDescriptor__(ctx, index); - Value args[1]; - args[0] = value; - pd->set->call(ctx, Value::fromObject(this), args, 1); - return; - } + // Clause 5 + if (pd && pd->isAccessor()) { + assert(pd->set != 0); - array.set(index, value); + Value args[1]; + args[0] = value; + pd->set->call(ctx, Value::fromObject(this), args, 1); return; } + array.set(index, value); + return; + reject: if (ctx->strictMode) __qmljs_throw_type_error(ctx); diff --git a/qmljs_objects.h b/qmljs_objects.h index d004868..362fe70 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -124,7 +124,7 @@ struct Object: Managed { PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name); PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, uint index); PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, String *name); - virtual PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); + PropertyDescriptor *__getPropertyDescriptor__(ExecutionContext *ctx, uint index); virtual Value __get__(ExecutionContext *ctx, String *name, bool *hasProperty = 0); virtual Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); @@ -132,8 +132,6 @@ struct Object: Managed { virtual void __put__(ExecutionContext *ctx, String *name, Value value); virtual void __put__(ExecutionContext *ctx, uint index, Value value); - virtual bool __canPut__(ExecutionContext *ctx, String *name); - virtual bool __canPut__(ExecutionContext *ctx, uint index); virtual bool __hasProperty__(const ExecutionContext *ctx, String *name) const; virtual bool __hasProperty__(const ExecutionContext *ctx, uint index) const; virtual bool __delete__(ExecutionContext *ctx, String *name); diff --git a/qmljs_runtime.cpp b/qmljs_runtime.cpp index 0bc37da..d7829e3 100644 --- a/qmljs_runtime.cpp +++ b/qmljs_runtime.cpp @@ -576,14 +576,16 @@ void __qmljs_set_property(ExecutionContext *ctx, Value object, String *name, Val Value __qmljs_get_element(ExecutionContext *ctx, Value object, Value index) { uint idx = index.asArrayIndex(); + if (object.isString() && idx < UINT_MAX) { - if (idx > INT_MAX || (int) idx >= object.stringValue()->toQString().length()) + String *str = object.stringValue(); + if (idx >= (uint)str->toQString().length()) return Value::undefinedValue(); - const QString s = object.stringValue()->toQString().mid(idx, 1); + const QString s = str->toQString().mid(idx, 1); return Value::fromString(ctx, s); } - if (! object.isObject()) + if (!object.isObject()) object = __qmljs_to_object(object, ctx); Object *o = object.objectValue(); diff --git a/qv4array.h b/qv4array.h index a7fc6b7..58529ef 100644 --- a/qv4array.h +++ b/qv4array.h @@ -512,16 +512,25 @@ public: } } + const PropertyDescriptor *nonSparseAt(uint index) const { + if (sparse) + return 0; + index += offset; + if (index >= (uint)values.size()) + return 0; + return values.constData() + index; + } + const PropertyDescriptor *at(uint index) const { if (!sparse) { if (index >= values.size() - offset) return 0; - return values.data() + index + offset; + return values.constData() + index + offset; } else { SparseArrayNode *n = sparse->findNode(index); if (!n) return 0; - return values.data() + n->value; + return values.constData() + n->value; } } diff --git a/tests/TestExpectations b/tests/TestExpectations index a3d65d6..7764282 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -1069,7 +1069,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.16-7-5 failing 15.4.4.16-7-8 failing 15.4.4.16-7-9 failing -15.4.4.16-7-b-1 failing 15.4.4.16-7-b-10 failing 15.4.4.16-7-b-12 failing 15.4.4.16-7-b-15 failing @@ -1194,7 +1193,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.17-7-5 failing 15.4.4.17-7-8 failing 15.4.4.17-7-9 failing -15.4.4.17-7-b-1 failing 15.4.4.17-7-b-10 failing 15.4.4.17-7-b-12 failing 15.4.4.17-7-b-15 failing @@ -1319,7 +1317,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.18-7-4 failing 15.4.4.18-7-8 failing 15.4.4.18-7-9 failing -15.4.4.18-7-b-1 failing 15.4.4.18-7-b-10 failing 15.4.4.18-7-b-12 failing 15.4.4.18-7-b-15 failing @@ -1439,7 +1436,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.19-8-1 failing 15.4.4.19-8-5 failing 15.4.4.19-8-8 failing -15.4.4.19-8-b-1 failing 15.4.4.19-8-b-10 failing 15.4.4.19-8-b-12 failing 15.4.4.19-8-b-15 failing @@ -1571,7 +1567,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.20-9-5 failing 15.4.4.20-9-8 failing 15.4.4.20-9-9 failing -15.4.4.20-9-b-1 failing 15.4.4.20-9-b-10 failing 15.4.4.20-9-b-12 failing 15.4.4.20-9-b-15 failing @@ -1936,7 +1931,6 @@ S15.4.4.13_A4_T2 failing 15.4.4.22-8-c-5 failing 15.4.4.22-8-c-6 failing 15.4.4.22-9-8 failing -15.4.4.22-9-9 failing 15.4.4.22-9-b-10 failing 15.4.4.22-9-b-12 failing 15.4.4.22-9-b-17 failing @@ -2596,10 +2590,9 @@ S15.4.4.13_A1_T2 failing 15.4.4.18-7-c-i-6 failing 15.4.4.19-8-c-i-6 failing 15.4.4.20-9-c-i-6 failing -15.4.4.21-8-b-iii-1-6 failing 15.4.4.22-8-b-iii-1-6 failing # Bugs in Array.prototype 15.4.4.14-9-b-i-5 failing 15.4.4.16-7-c-i-6 failing -15.4.4.17-7-c-i-6 failing +15.4.4.17-7-c-i-6 failing \ No newline at end of file -- 2.7.4