From 4a52bc9867b92843b2cd839269132aba0e104e7c Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Fri, 18 Jan 2013 12:47:43 +0100 Subject: [PATCH] Remove the need for virtual __get/set__ methods in ArgumentsObject In addition to not needing virtuals anymore, it also fixes a few corner cases in the test suite. Change-Id: I9f93d820aa7700c038c60a55daa57e1567d42b17 Reviewed-by: Simon Hausmann --- qmljs_engine.cpp | 20 +++++++++ qmljs_engine.h | 4 ++ qmljs_environment.cpp | 1 + qmljs_objects.cpp | 86 ++------------------------------------- qmljs_objects.h | 28 ++++--------- qmljs_value.cpp | 2 +- qmljs_value.h | 2 +- qv4argumentsobject.cpp | 106 ++++++++++++++++++++++++++++++++++++++++++++++++ qv4argumentsobject.h | 81 ++++++++++++++++++++++++++++++++++++ qv4mm.cpp | 6 +++ qv4propertydescriptor.h | 4 +- tests/TestExpectations | 6 +-- v4.pro | 2 + 13 files changed, 236 insertions(+), 112 deletions(-) create mode 100644 qv4argumentsobject.cpp create mode 100644 qv4argumentsobject.h diff --git a/qmljs_engine.cpp b/qmljs_engine.cpp index b58ff12..8bbff13 100644 --- a/qmljs_engine.cpp +++ b/qmljs_engine.cpp @@ -43,6 +43,7 @@ #include #include #include "qv4mm.h" +#include namespace QQmlJS { namespace VM { @@ -423,5 +424,24 @@ Object *ExecutionEngine::newForEachIteratorObject(ExecutionContext *ctx, Object return new (memoryManager) ForEachIteratorObject(ctx, o); } +void ExecutionEngine::requireArgumentsAccessors(int n) +{ + if (n <= argumentsAccessors.size()) + return; + + uint oldSize = argumentsAccessors.size(); + argumentsAccessors.resize(n); + for (int i = oldSize; i < n; ++i) { + FunctionObject *get = new (memoryManager) ArgumentsGetterFunction(rootContext, i); + get->prototype = functionPrototype; + FunctionObject *set = new (memoryManager) ArgumentsSetterFunction(rootContext, i); + set->prototype = functionPrototype; + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + argumentsAccessors[i] = pd; + } +} + } // namespace VM } // namespace QQmlJS diff --git a/qmljs_engine.h b/qmljs_engine.h index 83e8e62..72722e0 100644 --- a/qmljs_engine.h +++ b/qmljs_engine.h @@ -137,6 +137,8 @@ struct ExecutionEngine TypeErrorPrototype *typeErrorPrototype; URIErrorPrototype *uRIErrorPrototype; + QVector argumentsAccessors; + String *id_length; String *id_prototype; String *id_constructor; @@ -214,6 +216,8 @@ struct ExecutionEngine Object *newActivationObject(); Object *newForEachIteratorObject(ExecutionContext *ctx, Object *o); + + void requireArgumentsAccessors(int n); }; } // namespace VM diff --git a/qmljs_environment.cpp b/qmljs_environment.cpp index 3ab9ff5..9dc1761 100644 --- a/qmljs_environment.cpp +++ b/qmljs_environment.cpp @@ -45,6 +45,7 @@ #include #include #include "qv4mm.h" +#include namespace QQmlJS { namespace VM { diff --git a/qmljs_objects.cpp b/qmljs_objects.cpp index 5e3e0ef..d6a0640 100644 --- a/qmljs_objects.cpp +++ b/qmljs_objects.cpp @@ -486,7 +486,7 @@ bool Object::__delete__(ExecutionContext *ctx, uint index) } // Section 8.12.9 -bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc) { uint idx = name->asArrayIndex(); if (idx != String::InvalidArrayIndex) @@ -538,7 +538,7 @@ reject: return false; } -bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc) { PropertyDescriptor *current; @@ -566,7 +566,7 @@ reject: return false; } -bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc) { // clause 5 if (desc->isEmpty()) @@ -631,7 +631,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *cu } -bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, PropertyDescriptor *desc) +bool Object::__defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc) { return __defineOwnProperty__(ctx, ctx->engine->identifier(name), desc); } @@ -1173,84 +1173,6 @@ SyntaxErrorObject::SyntaxErrorObject(ExecutionContext *ctx, DiagnosticMessage *m } -ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) - : context(context) - , currentIndex(-1) -{ - defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); - if (context->strictMode) { - for (uint i = 0; i < context->argumentCount; ++i) - Object::__put__(context, QString::number(i), context->arguments[i]); - FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, __qmljs_throw_type_error); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); - pd.configurable = PropertyDescriptor::Disabled; - pd.enumberable = PropertyDescriptor::Disabled; - __defineOwnProperty__(context, QStringLiteral("callee"), &pd); - __defineOwnProperty__(context, QStringLiteral("caller"), &pd); - } else { - FunctionObject *get = context->engine->newBuiltinFunction(context, 0, method_getArg); - FunctionObject *set = context->engine->newBuiltinFunction(context, 0, method_setArg); - PropertyDescriptor pd = PropertyDescriptor::fromAccessor(get, set); - pd.configurable = PropertyDescriptor::Enabled; - pd.enumberable = PropertyDescriptor::Enabled; - uint enumerableParams = qMin(formalParameterCount, actualParameterCount); - for (uint i = 0; i < (uint)enumerableParams; ++i) - __defineOwnProperty__(context, i, &pd); - pd.type = PropertyDescriptor::Data; - pd.writable = PropertyDescriptor::Enabled; - for (uint i = enumerableParams; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { - pd.value = context->argument(i); - __defineOwnProperty__(context, i, &pd); - } - defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); - } -} - -Value ArgumentsObject::__get__(ExecutionContext *ctx, uint index, bool *hasProperty) -{ - if (!ctx->strictMode) - currentIndex = index; - Value result = Object::__get__(ctx, index, hasProperty); - currentIndex = -1; - return result; -} - -void ArgumentsObject::__put__(ExecutionContext *ctx, uint index, Value value) -{ - if (!ctx->strictMode) - currentIndex = index; - Object::__put__(ctx, index, value); - currentIndex = -1; -} - -Value ArgumentsObject::method_getArg(ExecutionContext *ctx) -{ - Object *that = ctx->thisObject.asObject(); - if (!that) - __qmljs_throw_type_error(ctx); - ArgumentsObject *args = that->asArgumentsObject(); - if (!args) - __qmljs_throw_type_error(ctx); - - assert(ctx != args->context); - assert(args->currentIndex >= 0 && args->currentIndex < (int)args->context->argumentCount); - return args->context->argument(args->currentIndex); -} - -Value ArgumentsObject::method_setArg(ExecutionContext *ctx) -{ - Object *that = ctx->thisObject.asObject(); - if (!that) - __qmljs_throw_type_error(ctx); - ArgumentsObject *args = that->asArgumentsObject(); - if (!args) - __qmljs_throw_type_error(ctx); - - assert(ctx != args->context); - assert(args->currentIndex >= 0 && args->currentIndex < (int)args->context->argumentCount); - args->context->arguments[args->currentIndex] = ctx->arguments[0]; - return Value::undefinedValue(); -} BuiltinFunction::BuiltinFunction(ExecutionContext *scope, String *name, Value (*code)(ExecutionContext *)) : FunctionObject(scope) diff --git a/qmljs_objects.h b/qmljs_objects.h index 01773ee..9c40ada 100644 --- a/qmljs_objects.h +++ b/qmljs_objects.h @@ -127,19 +127,19 @@ struct Object: Managed { 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); + Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); - virtual void __put__(ExecutionContext *ctx, String *name, Value value); - virtual void __put__(ExecutionContext *ctx, uint index, Value value); + void __put__(ExecutionContext *ctx, String *name, Value value); + void __put__(ExecutionContext *ctx, uint index, Value value); 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); virtual bool __delete__(ExecutionContext *ctx, uint index); - bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, PropertyDescriptor *desc); - virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, uint index, PropertyDescriptor *desc); - bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, PropertyDescriptor *current, const PropertyDescriptor *desc); + virtual bool __defineOwnProperty__(ExecutionContext *ctx, String *name, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const PropertyDescriptor *desc); + bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const PropertyDescriptor *desc); virtual Value call(ExecutionContext *context, Value, Value *, int); @@ -423,20 +423,6 @@ struct URIErrorObject: ErrorObject { virtual QString className() { return QStringLiteral("URIError"); } }; -struct ArgumentsObject: Object { - ExecutionContext *context; - int currentIndex; - ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); - virtual QString className() { return QStringLiteral("Arguments"); } - virtual ArgumentsObject *asArgumentsObject() { return this; } - - virtual Value __get__(ExecutionContext *ctx, uint index, bool *hasProperty = 0); - virtual void __put__(ExecutionContext *ctx, uint index, Value value); - - static Value method_getArg(ExecutionContext *ctx); - static Value method_setArg(ExecutionContext *ctx); -}; - } // namespace VM } // namespace QQmlJS diff --git a/qmljs_value.cpp b/qmljs_value.cpp index 78c549f..9121ea8 100644 --- a/qmljs_value.cpp +++ b/qmljs_value.cpp @@ -79,7 +79,7 @@ Value Value::toObject(ExecutionContext *ctx) const } -bool Value::sameValue(Value other) { +bool Value::sameValue(Value other) const { if (val == other.val) return true; if (isString() && other.isString()) diff --git a/qmljs_value.h b/qmljs_value.h index 1b94615..9e19788 100644 --- a/qmljs_value.h +++ b/qmljs_value.h @@ -257,7 +257,7 @@ struct Value Value property(ExecutionContext *ctx, String *name) const; // Section 9.12 - bool sameValue(Value other); + bool sameValue(Value other) const; }; inline Value Value::undefinedValue() diff --git a/qv4argumentsobject.cpp b/qv4argumentsobject.cpp new file mode 100644 index 0000000..7453b7a --- /dev/null +++ b/qv4argumentsobject.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include + +namespace QQmlJS { +namespace VM { + + +ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount) + : context(context) +{ + defineDefaultProperty(context->engine->id_length, Value::fromInt32(actualParameterCount)); + if (context->strictMode) { + for (uint i = 0; i < context->argumentCount; ++i) + Object::__put__(context, QString::number(i), context->arguments[i]); + FunctionObject *thrower = context->engine->newBuiltinFunction(context, 0, __qmljs_throw_type_error); + PropertyDescriptor pd = PropertyDescriptor::fromAccessor(thrower, thrower); + pd.configurable = PropertyDescriptor::Disabled; + pd.enumberable = PropertyDescriptor::Disabled; + __defineOwnProperty__(context, QStringLiteral("callee"), &pd); + __defineOwnProperty__(context, QStringLiteral("caller"), &pd); + } else { + uint enumerableParams = qMin(formalParameterCount, actualParameterCount); + context->engine->requireArgumentsAccessors(enumerableParams); + for (uint i = 0; i < (uint)enumerableParams; ++i) + __defineOwnProperty__(context, i, &context->engine->argumentsAccessors.at(i)); + PropertyDescriptor pd; + pd.type = PropertyDescriptor::Data; + pd.writable = PropertyDescriptor::Enabled; + pd.configurable = PropertyDescriptor::Enabled; + pd.enumberable = PropertyDescriptor::Enabled; + for (uint i = enumerableParams; i < qMin((uint)actualParameterCount, context->argumentCount); ++i) { + pd.value = context->argument(i); + __defineOwnProperty__(context, i, &pd); + } + defineDefaultProperty(context, QStringLiteral("callee"), Value::fromObject(context->function)); + } +} + +Value ArgumentsGetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *, int) +{ + Object *that = thisObject.asObject(); + if (!that) + __qmljs_throw_type_error(ctx); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + __qmljs_throw_type_error(ctx); + + assert(index < o->context->argumentCount); + return o->context->argument(index); +} + +Value ArgumentsSetterFunction::call(ExecutionContext *ctx, Value thisObject, Value *args, int argc) +{ + Object *that = thisObject.asObject(); + if (!that) + __qmljs_throw_type_error(ctx); + ArgumentsObject *o = that->asArgumentsObject(); + if (!o) + __qmljs_throw_type_error(ctx); + + assert(index < o->context->argumentCount); + o->context->arguments[index] = argc ? args[0] : Value::undefinedValue(); + return Value::undefinedValue(); +} + +} +} diff --git a/qv4argumentsobject.h b/qv4argumentsobject.h new file mode 100644 index 0000000..ce47cfa --- /dev/null +++ b/qv4argumentsobject.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the V4VM module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4ARGUMENTSOBJECTS_H +#define QV4ARGUMENTSOBJECTS_H + +#include + +namespace QQmlJS { +namespace VM { + +struct ArgumentsGetterFunction: FunctionObject +{ + uint index; + + ArgumentsGetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) {} + + virtual Value call(ExecutionContext *ctx, Value thisObject, Value *, int); +}; + +struct ArgumentsSetterFunction: FunctionObject +{ + uint index; + + ArgumentsSetterFunction(ExecutionContext *scope, uint index) + : FunctionObject(scope), index(index) {} + + virtual Value call(ExecutionContext *ctx, Value thisObject, Value *args, int argc); +}; + + +struct ArgumentsObject: Object { + ExecutionContext *context; + ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount); + virtual QString className() { return QStringLiteral("Arguments"); } + virtual ArgumentsObject *asArgumentsObject() { return this; } +}; + +} +} + +#endif + diff --git a/qv4mm.cpp b/qv4mm.cpp index 0af07d4..a05e593 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -333,6 +333,12 @@ void MemoryManager::collectRoots(QVector &roots) const add(roots, m_d->engine->globalObject); add(roots, m_d->engine->exception); + for (int i = 0; i < m_d->engine->argumentsAccessors.size(); ++i) { + const PropertyDescriptor &pd = m_d->engine->argumentsAccessors.at(i); + add(roots, Value::fromObject(pd.get)); + add(roots, Value::fromObject(pd.set)); + } + for (ExecutionContext *ctxt = engine()->current; ctxt; ctxt = ctxt->parent) { add(roots, ctxt->thisObject); if (ctxt->function) diff --git a/qv4propertydescriptor.h b/qv4propertydescriptor.h index 0f14a15..dc9e1c5 100644 --- a/qv4propertydescriptor.h +++ b/qv4propertydescriptor.h @@ -121,10 +121,10 @@ struct PropertyDescriptor { inline bool isEnumerable() const { return enumberable == Enabled; } inline bool isConfigurable() const { return configurable == Enabled; } - inline bool isEmpty() { + inline bool isEmpty() const { return type == Generic && writable == Undefined && enumberable == Undefined && configurable == Undefined; } - inline bool isSubset(PropertyDescriptor *other) { + inline bool isSubset(PropertyDescriptor *other) const { if (type != Generic && type != other->type) return false; if (enumberable != Undefined && enumberable != other->enumberable) diff --git a/tests/TestExpectations b/tests/TestExpectations index 4c1938b..48ae47a 100644 --- a/tests/TestExpectations +++ b/tests/TestExpectations @@ -666,14 +666,10 @@ S15.4.4.13_A3_T3 failing S15.4.4.13_A4_T1 failing S15.4.4.13_A4_T2 failing 15.4.4.14-1-13 failing -15.4.4.14-2-17 failing 15.4.4.14-9-a-10 failing 15.4.4.14-9-a-17 failing 15.4.4.14-9-a-7 failing 15.4.4.14-9-a-9 failing -15.4.4.14-9-b-i-25 failing -15.4.4.14-9-b-i-26 failing -15.4.4.14-9-b-i-27 failing 15.4.4.15-1-13 failing 15.4.4.15-3-2 failing 15.4.4.15-3-29 failing @@ -1304,4 +1300,4 @@ S15.4.4.13_A1_T2 failing 15.4.4.17-7-c-i-6 failing # Regressions introduced with https://codereview.qt-project.org/#change,45044 -15.4.4.21-8-b-iii-1-6 failing \ No newline at end of file +15.4.4.21-8-b-iii-1-6 failing diff --git a/v4.pro b/v4.pro index 7213e6c..9d6f5bd 100644 --- a/v4.pro +++ b/v4.pro @@ -27,6 +27,7 @@ SOURCES += main.cpp \ qv4mm.cpp \ qv4managed.cpp \ qv4array.cpp \ + qv4argumentsobject.cpp \ qv4string.cpp \ qv4objectiterator.cpp \ qv4regexp.cpp @@ -49,6 +50,7 @@ HEADERS += \ qv4mm.h \ qv4managed.h \ qv4array.h \ + qv4argumentsobject.h \ qv4string.h \ qv4propertydescriptor.h \ qv4propertytable.h \ -- 2.7.4