Use internal classes to store the layout of members
authorLars Knoll <lars.knoll@digia.com>
Sun, 10 Feb 2013 21:22:53 +0000 (22:22 +0100)
committerSimon Hausmann <simon.hausmann@digia.com>
Tue, 12 Feb 2013 13:54:16 +0000 (14:54 +0100)
Add an internal class structure to Object that will allow
us to do much more efficient property lookups in the future.

Change-Id: I9ee72f6d73113a489f00ad7a31a20e91fbba18ed
Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
26 files changed:
src/v4/qmljs_engine.cpp
src/v4/qmljs_engine.h
src/v4/qv4argumentsobject.cpp
src/v4/qv4arrayobject.cpp
src/v4/qv4booleanobject.cpp
src/v4/qv4booleanobject.h
src/v4/qv4dateobject.h
src/v4/qv4errorobject.cpp
src/v4/qv4functionobject.cpp
src/v4/qv4functionobject.h
src/v4/qv4identifier.h
src/v4/qv4internalclass.cpp [new file with mode: 0644]
src/v4/qv4internalclass.h [new file with mode: 0644]
src/v4/qv4jsonobject.cpp
src/v4/qv4mathobject.cpp
src/v4/qv4numberobject.h
src/v4/qv4object.cpp
src/v4/qv4object.h
src/v4/qv4objectiterator.cpp
src/v4/qv4objectiterator.h
src/v4/qv4objectproto.h
src/v4/qv4propertytable.h [deleted file]
src/v4/qv4regexpobject.cpp
src/v4/qv4regexpobject.h
src/v4/qv4stringobject.cpp
src/v4/v4.pro

index 05a03f6..b71111a 100644 (file)
@@ -75,6 +75,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
 
     identifierCache = new Identifiers(this);
 
+    emptyClass = new InternalClass(this);
     rootContext = newContext();
     rootContext->init(this);
     current = rootContext;
@@ -94,12 +95,12 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
     id_set = newIdentifier(QStringLiteral("set"));
     id_eval = newIdentifier(QStringLiteral("eval"));
 
-    objectPrototype = new (memoryManager) ObjectPrototype();
+    objectPrototype = new (memoryManager) ObjectPrototype(this);
     stringPrototype = new (memoryManager) StringPrototype(rootContext);
-    numberPrototype = new (memoryManager) NumberPrototype();
-    booleanPrototype = new (memoryManager) BooleanPrototype();
+    numberPrototype = new (memoryManager) NumberPrototype(this);
+    booleanPrototype = new (memoryManager) BooleanPrototype(this);
     arrayPrototype = new (memoryManager) ArrayPrototype(rootContext);
-    datePrototype = new (memoryManager) DatePrototype();
+    datePrototype = new (memoryManager) DatePrototype(this);
     functionPrototype = new (memoryManager) FunctionPrototype(rootContext);
     regExpPrototype = new (memoryManager) RegExpPrototype(this);
     errorPrototype = new (memoryManager) ErrorPrototype(this);
@@ -270,7 +271,7 @@ BoundFunction *ExecutionEngine::newBoundFunction(ExecutionContext *scope, Functi
 
 Object *ExecutionEngine::newObject()
 {
-    Object *object = new (memoryManager) Object();
+    Object *object = new (memoryManager) Object(this);
     object->prototype = objectPrototype;
     return object;
 }
@@ -294,14 +295,14 @@ Object *ExecutionEngine::newStringObject(ExecutionContext *ctx, const Value &val
 
 Object *ExecutionEngine::newNumberObject(const Value &value)
 {
-    NumberObject *object = new (memoryManager) NumberObject(value);
+    NumberObject *object = new (memoryManager) NumberObject(this, value);
     object->prototype = numberPrototype;
     return object;
 }
 
 Object *ExecutionEngine::newBooleanObject(const Value &value)
 {
-    Object *object = new (memoryManager) BooleanObject(value);
+    Object *object = new (memoryManager) BooleanObject(this, value);
     object->prototype = booleanPrototype;
     return object;
 }
@@ -322,7 +323,7 @@ ArrayObject *ExecutionEngine::newArrayObject(ExecutionContext *ctx)
 
 Object *ExecutionEngine::newDateObject(const Value &value)
 {
-    Object *object = new (memoryManager) DateObject(value);
+    Object *object = new (memoryManager) DateObject(this, value);
     object->prototype = datePrototype;
     return object;
 }
index e581beb..7aaefd4 100644 (file)
@@ -94,6 +94,7 @@ struct TypeErrorPrototype;
 struct URIErrorPrototype;
 struct EvalFunction;
 struct Identifiers;
+struct InternalClass;
 
 class RegExp;
 
@@ -176,6 +177,8 @@ struct Q_V4_EXPORT ExecutionEngine
 
     QVector<Function *> functions;
 
+    InternalClass *emptyClass;
+
     ExecutionEngine(EvalISelFactory *iselFactory);
     ~ExecutionEngine();
 
index 8211ef2..404edbb 100644 (file)
@@ -45,7 +45,7 @@ namespace VM {
 
 
 ArgumentsObject::ArgumentsObject(ExecutionContext *context, int formalParameterCount, int actualParameterCount)
-    : context(context)
+    : Object(context->engine), context(context)
 {
     type = Type_ArgumentsObject;
 
index 3079c52..3f70029 100644 (file)
@@ -78,8 +78,8 @@ Value ArrayCtor::call(ExecutionContext *ctx)
 
 void ArrayPrototype::init(ExecutionContext *ctx, const Value &ctor)
 {
-    ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
     ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1));
+    ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
     ctor.objectValue()->defineDefaultProperty(ctx, QStringLiteral("isArray"), method_isArray, 1);
     defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
     defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0);
index 640773d..748a695 100644 (file)
@@ -62,8 +62,8 @@ Value BooleanCtor::call(ExecutionContext *parentCtx, Value thisObject, Value *ar
 
 void BooleanPrototype::init(ExecutionContext *ctx, const Value &ctor)
 {
-    ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
     ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1));
+    ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
     defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
     defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString);
     defineDefaultProperty(ctx, QStringLiteral("valueOf"), method_valueOf);
index d0a7cda..c73ae9b 100644 (file)
@@ -60,7 +60,7 @@ struct BooleanCtor: FunctionObject
 
 struct BooleanPrototype: BooleanObject
 {
-    BooleanPrototype(): BooleanObject(Value::fromBoolean(false)) {}
+    BooleanPrototype(ExecutionEngine *engine): BooleanObject(engine, Value::fromBoolean(false)) {}
     void init(ExecutionContext *ctx, const Value &ctor);
 
     static Value method_toString(ExecutionContext *ctx);
index 2c09ad2..15bd99f 100644 (file)
@@ -52,7 +52,7 @@ namespace VM {
 
 struct DateObject: Object {
     Value value;
-    DateObject(const Value &value): value(value) { type = Type_DateObject; }
+    DateObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_DateObject; }
 };
 
 struct DateCtor: FunctionObject
@@ -65,7 +65,7 @@ struct DateCtor: FunctionObject
 
 struct DatePrototype: DateObject
 {
-    DatePrototype(): DateObject(Value::fromDouble(qSNaN())) {}
+    DatePrototype(ExecutionEngine *engine): DateObject(engine, Value::fromDouble(qSNaN())) {}
     void init(ExecutionContext *ctx, const Value &ctor);
 
     static double getThisDate(ExecutionContext *ctx);
index f381a78..c6b0a12 100644 (file)
@@ -74,6 +74,7 @@
 using namespace QQmlJS::VM;
 
 ErrorObject::ErrorObject(ExecutionEngine* engine, const Value &message)
+    : Object(engine)
 {
     type = Type_ErrorObject;
     subtype = Error;
index bba00b8..67ba487 100644 (file)
@@ -85,7 +85,8 @@ void Function::mark()
 }
 
 FunctionObject::FunctionObject(ExecutionContext *scope)
-    : scope(scope)
+    : Object(scope->engine)
+    , scope(scope)
     , name(0)
     , formalParameterList(0)
     , varList(0)
@@ -258,15 +259,16 @@ Value FunctionCtor::call(ExecutionContext *ctx)
 
 void FunctionPrototype::init(ExecutionContext *ctx, const Value &ctor)
 {
-    ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
     ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(1));
+    ctor.objectValue()->defineReadonlyProperty(ctx->engine->id_prototype, Value::fromObject(this));
+
+    defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0));
     defineDefaultProperty(ctx, QStringLiteral("constructor"), ctor);
     defineDefaultProperty(ctx, QStringLiteral("toString"), method_toString, 0);
     defineDefaultProperty(ctx, QStringLiteral("apply"), method_apply, 2);
     defineDefaultProperty(ctx, QStringLiteral("call"), method_call, 1);
     defineDefaultProperty(ctx, QStringLiteral("bind"), method_bind, 1);
 
-    defineReadonlyProperty(ctx->engine->id_length, Value::fromInt32(0));
 }
 
 Value FunctionPrototype::method_toString(ExecutionContext *ctx)
index 900cb8f..87fb696 100644 (file)
@@ -51,7 +51,6 @@
 #include "qv4isel_p.h"
 #include "qv4managed.h"
 #include "qv4propertydescriptor.h"
-#include "qv4propertytable.h"
 #include "qv4objectiterator.h"
 #include "qv4regexp.h"
 
index 3fa84d2..fd4ca7a 100644 (file)
@@ -73,8 +73,8 @@ public:
         str->stringIdentifier = currentIndex;
         if (currentIndex <= USHRT_MAX) {
             str->subtype = String::StringType_Identifier;
-            ++currentIndex;
             identifiers.insert(s, str);
+            ++currentIndex;
         }
         return str;
     }
@@ -95,8 +95,8 @@ public:
         s->stringIdentifier = currentIndex;
         if (currentIndex <= USHRT_MAX) {
             s->subtype = String::StringType_Identifier;
-            ++currentIndex;
             identifiers.insert(s->toQString(), s);
+            ++currentIndex;
         }
     }
 
diff --git a/src/v4/qv4internalclass.cpp b/src/v4/qv4internalclass.cpp
new file mode 100644 (file)
index 0000000..5577f09
--- /dev/null
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** 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 <qv4internalclass.h>
+#include <qv4string.h>
+#include <qmljs_engine.h>
+#include <qv4identifier.h>
+
+namespace QQmlJS {
+namespace VM {
+
+
+InternalClass::InternalClass(const QQmlJS::VM::InternalClass &other)
+    : engine(other.engine)
+    , propertyTable(other.propertyTable)
+    , nameMap(other.nameMap)
+    , transitions()
+    , size(other.size)
+{
+}
+
+uint InternalClass::getOrAddMember(Object *object, String *string)
+{
+    engine->identifierCache->toIdentifier(string);
+    uint id = string->stringIdentifier;
+
+    QHash<uint, uint>::const_iterator it = propertyTable.constFind(id);
+    if (it != propertyTable.constEnd())
+        return it.value();
+
+    // new member, need to transition to a new internal class
+
+    QHash<int, InternalClass *>::const_iterator tit = transitions.constFind(id);
+
+    if (tit != transitions.constEnd()) {
+        object->internalClass = tit.value();
+    } else {
+        // create a new class and add it to the tree
+        InternalClass *newClass = new InternalClass(*this);
+        newClass->propertyTable.insert(id, size);
+        newClass->nameMap.append(string);
+        ++newClass->size;
+        transitions.insert(id, newClass);
+        object->internalClass = newClass;
+    }
+    return size;
+}
+
+void InternalClass::removeMember(Object *object, uint id)
+{
+    assert (propertyTable.constFind(id) != propertyTable.constEnd());
+    int propIdx = propertyTable.constFind(id).value();
+    assert(propIdx < size);
+
+    int toRemove = - (int)id;
+    QHash<int, InternalClass *>::const_iterator tit = transitions.constFind(toRemove);
+
+    if (tit != transitions.constEnd()) {
+        object->internalClass = tit.value();
+        return;
+    }
+
+    // create a new class and add it to the tree
+    InternalClass *newClass = new InternalClass(*this);
+    newClass->propertyTable.remove(id);
+    newClass->nameMap.remove(propIdx);
+    --newClass->size;
+    for (QHash<uint, uint>::iterator it = newClass->propertyTable.begin(); it != newClass->propertyTable.end(); ++it) {
+        if ((*it) > propIdx)
+            --(*it);
+    }
+
+    transitions.insert(toRemove, newClass);
+    object->internalClass = newClass;
+}
+
+uint InternalClass::find(String *string)
+{
+    engine->identifierCache->toIdentifier(string);
+    uint id = string->stringIdentifier;
+
+    QHash<uint, uint>::const_iterator it = propertyTable.constFind(id);
+    if (it != propertyTable.constEnd())
+        return it.value();
+
+    return UINT_MAX;
+}
+
+
+}
+}
diff --git a/src/v4/qv4internalclass.h b/src/v4/qv4internalclass.h
new file mode 100644 (file)
index 0000000..d7da0ae
--- /dev/null
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** 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 QV4INTERNALCLASS_H
+#define QV4INTERNALCLASS_H
+
+#include <QHash>
+#include <QVector>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace VM {
+
+struct String;
+struct ExecutionEngine;
+struct Object;
+
+struct InternalClass {
+    ExecutionEngine *engine;
+    QHash<uint, uint> propertyTable; // id to valueIndex
+    QVector<String *> nameMap;
+    QHash<int, InternalClass *> transitions; // id to next class, positive means add, negative delete
+    uint size;
+
+    InternalClass(ExecutionEngine *engine) : engine(engine), size(0) {}
+
+    uint getOrAddMember(Object *object, String *string);
+    void removeMember(Object *object, uint id);
+    uint find(String *s);
+
+private:
+    InternalClass(const InternalClass &other);
+};
+
+
+} // namespace VM
+} // namespace QQmlJS
+
+QT_END_NAMESPACE
+
+#endif
index 241c3c8..74b25f3 100644 (file)
@@ -268,8 +268,6 @@ Value Parser::parseObject()
 bool Parser::parseMember(Object *o)
 {
     BEGIN << "parseMember";
-    if (!o->members)
-        o->members.reset(new PropertyTable());
 
     QString key;
     if (!parseString(&key))
@@ -864,7 +862,7 @@ QString Stringify::JA(ArrayObject *a)
 
 
 JsonObject::JsonObject(ExecutionContext *context)
-    : Object()
+    : Object(context->engine)
 {
     type = Type_JSONObject;
     prototype = context->engine->objectPrototype;
index f0523ba..e7c455a 100644 (file)
@@ -51,6 +51,7 @@ using namespace QQmlJS::VM;
 static const double qt_PI = 2.0 * ::asin(1.0);
 
 MathObject::MathObject(ExecutionContext *ctx)
+    : Object(ctx->engine)
 {
     type = Type_MathObject;
     prototype = ctx->engine->objectPrototype;
index aa2c9cf..b24e538 100644 (file)
@@ -60,7 +60,7 @@ struct NumberCtor: FunctionObject
 
 struct NumberPrototype: NumberObject
 {
-    NumberPrototype(): NumberObject(Value::fromDouble(0)) {}
+    NumberPrototype(ExecutionEngine *engine): NumberObject(engine, Value::fromDouble(0)) {}
     void init(ExecutionContext *ctx, const Value &ctor);
 
     static Value method_toString(ExecutionContext *ctx);
index f260762..31200bc 100644 (file)
 using namespace QQmlJS::VM;
 
 
-//
-// Object
-//
+Object::Object(ExecutionEngine *engine)
+    : prototype(0)
+    , internalClass(engine->emptyClass)
+    , memberDataAlloc(0), memberData(0)
+    , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), sparseArray(0)
+{
+    type = Type_Object;
+}
+
+
 Object::~Object()
 {
     delete [] memberData;
@@ -130,8 +137,6 @@ void Object::inplaceBinOp(Value rhs, Value index, BinOp op, ExecutionContext *ct
 
 void Object::defineDefaultProperty(String *name, Value value)
 {
-    if (!members)
-        members.reset(new PropertyTable());
     PropertyDescriptor *pd = insertMember(name);
     pd->type = PropertyDescriptor::Data;
     pd->writable = PropertyDescriptor::Enabled;
@@ -170,8 +175,6 @@ void Object::defineReadonlyProperty(ExecutionEngine *engine, const QString &name
 
 void Object::defineReadonlyProperty(String *name, Value value)
 {
-    if (!members)
-        members.reset(new PropertyTable());
     PropertyDescriptor *pd = insertMember(name);
     pd->type = PropertyDescriptor::Data;
     pd->writable = PropertyDescriptor::Disabled;
@@ -185,23 +188,16 @@ void Object::markObjects()
     if (prototype)
         prototype->mark();
 
-    if (members) {
-        for (PropertyTable::iterator it = members->begin(), eit = members->end(); it < eit; ++it) {
-            if (!(*it))
-                continue;
-            (*it)->name->mark();
-        }
-        for (int i = 0; i < memberDataSize; ++i) {
-            const PropertyDescriptor &pd = memberData[i];
-            if (pd.isData()) {
-                if (Managed *m = pd.value.asManaged())
-                    m->mark();
-             } else if (pd.isAccessor()) {
-                if (pd.get)
-                    pd.get->mark();
-                if (pd.set)
-                    pd.set->mark();
-            }
+    for (int i = 0; i < internalClass->size; ++i) {
+        const PropertyDescriptor &pd = memberData[i];
+        if (pd.isData()) {
+            if (Managed *m = pd.value.asManaged())
+                m->mark();
+         } else if (pd.isAccessor()) {
+            if (pd.get)
+                pd.get->mark();
+            if (pd.set)
+                pd.set->mark();
         }
     }
     markArrayObjects();
@@ -209,22 +205,16 @@ void Object::markObjects()
 
 PropertyDescriptor *Object::insertMember(String *s)
 {
-    if (!members)
-        members.reset(new PropertyTable);
-
-    PropertyTableEntry *e = members->insert(s);
-    if (e->valueIndex == UINT_MAX) {
-        if (memberDataSize == memberDataAlloc) {
-            memberDataAlloc = qMax((uint)8, 2*memberDataAlloc);
-            PropertyDescriptor *newMemberData = new PropertyDescriptor[memberDataAlloc];
-            memcpy(newMemberData, memberData, sizeof(PropertyDescriptor)*memberDataSize);
-            delete [] memberData;
-            memberData = newMemberData;
-        }
-        e->valueIndex = memberDataSize;
-        ++memberDataSize;
+    uint idx = internalClass->getOrAddMember(this, s);
+
+    if (idx >= memberDataAlloc) {
+        memberDataAlloc = qMax((uint)8, 2*memberDataAlloc);
+        PropertyDescriptor *newMemberData = new PropertyDescriptor[memberDataAlloc];
+        memcpy(newMemberData, memberData, sizeof(PropertyDescriptor)*idx);
+        delete [] memberData;
+        memberData = newMemberData;
     }
-    return memberData + e->valueIndex;
+    return memberData + idx;
 }
 
 // Section 8.12.1
@@ -234,11 +224,10 @@ PropertyDescriptor *Object::__getOwnProperty__(ExecutionContext *ctx, String *na
     if (idx != String::InvalidArrayIndex)
         return __getOwnProperty__(ctx, idx);
 
-    if (members) {
-        uint idx = members->find(name);
-        if (idx < UINT_MAX)
-            return memberData + idx;
-    }
+    uint member = internalClass->find(name);
+    if (member < UINT_MAX)
+        return memberData + member;
+
     return 0;
 }
 
@@ -263,11 +252,10 @@ PropertyDescriptor *Object::__getPropertyDescriptor__(ExecutionContext *ctx, Str
 
     Object *o = this;
     while (o) {
-        if (o->members) {
-            uint idx = o->members->find(name);
-            if (idx < UINT_MAX)
-                return o->memberData + idx;
-        }
+        uint idx = o->internalClass->find(name);
+        if (idx < UINT_MAX)
+            return o->memberData + idx;
+
         o = o->prototype;
     }
     return 0;
@@ -307,14 +295,13 @@ Value Object::__get__(ExecutionContext *ctx, String *name, bool *hasProperty)
 
     Object *o = this;
     while (o) {
-        if (o->members) {
-            uint idx = o->members->find(name);
-            if (idx < UINT_MAX) {
-                if (hasProperty)
-                    *hasProperty = true;
-                return getValue(ctx, o->memberData + idx);
-            }
+        uint idx = o->internalClass->find(name);
+        if (idx < UINT_MAX) {
+            if (hasProperty)
+                *hasProperty = true;
+            return getValue(ctx, o->memberData + idx);
         }
+
         o = o->prototype;
     }
 
@@ -407,9 +394,6 @@ void Object::__put__(ExecutionContext *ctx, String *name, Value value)
 
     cont:
 
-    if (!members)
-        members.reset(new PropertyTable());
-
 
     // clause 4
     if (!pd && prototype)
@@ -507,7 +491,7 @@ bool Object::__hasProperty__(const ExecutionContext *ctx, String *name) const
 
     name->makeIdentifier(ctx);
 
-    if (members && members->find(name) != UINT_MAX)
+    if (internalClass->find(name) != UINT_MAX)
         return true;
 
     return prototype ? prototype->__hasProperty__(ctx, name) : false;
@@ -531,21 +515,19 @@ bool Object::__delete__(ExecutionContext *ctx, String *name)
 
     name->makeIdentifier(ctx);
 
-    if (members) {
-        if (PropertyTableEntry *entry = members->findEntry(name)) {
-            PropertyDescriptor &pd = memberData[entry->valueIndex];
-            if (pd.isConfigurable()) {
-                members->remove(entry);
-                // ### leaves a hole in memberData
-                pd.type = PropertyDescriptor::Generic;
-                pd.writable = PropertyDescriptor::Undefined;
-                return true;
-            }
-            if (ctx->strictMode)
-                __qmljs_throw_type_error(ctx);
-            return false;
+    uint memberIdx = internalClass->find(name);
+    if (memberIdx != UINT_MAX) {
+        PropertyDescriptor &pd = memberData[memberIdx];
+        if (pd.isConfigurable()) {
+            internalClass->removeMember(this, name->stringIdentifier);
+            memmove(memberData + memberIdx, memberData + memberIdx + 1, (internalClass->size - memberIdx)*sizeof(PropertyDescriptor));
+            return true;
         }
+        if (ctx->strictMode)
+            __qmljs_throw_type_error(ctx);
+        return false;
     }
+
     return true;
 }
 
@@ -592,7 +574,7 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr
 
     if (isArrayObject() && name->isEqualTo(ctx->engine->id_length)) {
         PropertyDescriptor *lp = memberData + ArrayObject::LengthPropertyIndex;
-        assert(0 == members->find(ctx->engine->id_length));
+        assert(0 == internalClass->find(ctx->engine->id_length));
         if (desc->isEmpty() || desc->isSubset(lp))
             return true;
         if (!lp->isWritable() || desc->type == PropertyDescriptor::Accessor || desc->isConfigurable() || desc->isEnumerable())
@@ -612,9 +594,6 @@ bool Object::__defineOwnProperty__(ExecutionContext *ctx, String *name, const Pr
         return true;
     }
 
-    if (!members)
-        members.reset(new PropertyTable());
-
     // Clause 1
     current = __getOwnProperty__(ctx, name);
     if (!current) {
@@ -982,8 +961,6 @@ void ArrayObject::init(ExecutionContext *context)
 {
     type = Type_ArrayObject;
 
-    if (!members)
-        members.reset(new PropertyTable());
     PropertyDescriptor *pd = insertMember(context->engine->id_length);
     assert(pd == memberData + LengthPropertyIndex);
     pd->type = PropertyDescriptor::Data;
index 7f99668..964051f 100644 (file)
@@ -51,7 +51,7 @@
 #include "qv4isel_p.h"
 #include "qv4managed.h"
 #include "qv4propertydescriptor.h"
-#include "qv4propertytable.h"
+#include "qv4internalclass.h"
 #include "qv4objectiterator.h"
 #include "qv4regexp.h"
 
@@ -102,8 +102,7 @@ struct URIErrorPrototype;
 
 struct Q_V4_EXPORT Object: Managed {
     Object *prototype;
-    QScopedPointer<PropertyTable> members;
-    uint memberDataSize;
+    InternalClass *internalClass;
     uint memberDataAlloc;
     PropertyDescriptor *memberData;
 
@@ -116,11 +115,7 @@ struct Q_V4_EXPORT Object: Managed {
     PropertyDescriptor *arrayData;
     SparseArray *sparseArray;
 
-    Object()
-        : prototype(0)
-        , memberDataSize(0), memberDataAlloc(0), memberData(0)
-        , arrayOffset(0), arrayDataLen(0), arrayAlloc(0), sparseArray(0) { type = Type_Object; }
-
+    Object(ExecutionEngine *engine);
     virtual ~Object();
 
     PropertyDescriptor *__getOwnProperty__(ExecutionContext *ctx, String *name);
@@ -315,7 +310,7 @@ protected:
 struct ForEachIteratorObject: Object {
     ObjectIterator it;
     ForEachIteratorObject(ExecutionContext *ctx, Object *o)
-        : it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { type = Type_ForeachIteratorObject; }
+        : Object(ctx->engine), it(ctx, o, ObjectIterator::EnumberableOnly|ObjectIterator::WithProtoChain) { type = Type_ForeachIteratorObject; }
 
     Value nextPropertyName() { return it.nextPropertyNameAsString(); }
 
@@ -325,12 +320,12 @@ protected:
 
 struct BooleanObject: Object {
     Value value;
-    BooleanObject(const Value &value): value(value) { type = Type_BooleanObject; }
+    BooleanObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_BooleanObject; }
 };
 
 struct NumberObject: Object {
     Value value;
-    NumberObject(const Value &value): value(value) { type = Type_NumberObject; }
+    NumberObject(ExecutionEngine *engine, const Value &value): Object(engine), value(value) { type = Type_NumberObject; }
 };
 
 struct ArrayObject: Object {
@@ -338,7 +333,7 @@ struct ArrayObject: Object {
         LengthPropertyIndex = 0
     };
 
-    ArrayObject(ExecutionContext *ctx) { init(ctx); }
+    ArrayObject(ExecutionContext *ctx) : Object(ctx->engine) { init(ctx); }
     void init(ExecutionContext *context);
 };
 
index 7bcf872..d7c1a19 100644 (file)
@@ -41,6 +41,7 @@
 #include "qv4objectiterator.h"
 #include "qv4object.h"
 #include "qv4stringobject.h"
+#include "qv4identifier.h"
 
 namespace QQmlJS {
 namespace VM {
@@ -51,11 +52,15 @@ ObjectIterator::ObjectIterator(ExecutionContext *context, Object *o, uint flags)
     , current(o)
     , arrayNode(0)
     , arrayIndex(0)
-    , tableIndex(0)
+    , currentClass(0)
+    , memberIndex(0)
     , flags(flags)
 {
-    if (current && current->asStringObject())
-        this->flags |= CurrentIsString;
+    if (current) {
+        currentClass = current->internalClass;
+        if (current->asStringObject())
+            this->flags |= CurrentIsString;
+    }
 }
 
 PropertyDescriptor *ObjectIterator::next(String **name, uint *index)
@@ -110,7 +115,7 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index)
             }
         }
 
-        if (!current->members || tableIndex >= (uint)current->members->_propertyCount) {
+        if (memberIndex == currentClass->size) {
             if (flags & WithProtoChain)
                 current = current->prototype;
             else
@@ -122,19 +127,20 @@ PropertyDescriptor *ObjectIterator::next(String **name, uint *index)
 
 
             arrayIndex = 0;
-            tableIndex = 0;
+            memberIndex = 0;
+            if (current)
+                currentClass = current->internalClass;
             continue;
         }
-        PropertyTableEntry *pt = current->members->_properties[tableIndex];
-        ++tableIndex;
+        String *n = currentClass->nameMap.at(memberIndex);
+        assert(n);
         // ### check that it's not a repeated attribute
-        if (pt) {
-            PropertyDescriptor *pd = current->memberData + pt->valueIndex;
-            if (!(flags & EnumberableOnly) || pd->isEnumerable()) {
-                *name = pt->name;
-                p = pd;
-                return p;
-            }
+
+        p = current->memberData + memberIndex;
+        ++memberIndex;
+        if (!(flags & EnumberableOnly) || p->isEnumerable()) {
+            *name = n;
+            return p;
         }
     }
     return 0;
index a463eb8..c2e61dc 100644 (file)
@@ -42,6 +42,7 @@
 #define QV4OBJECTITERATOR_H
 
 #include "qmljs_value.h"
+#include <qv4internalclass.h>
 
 QT_BEGIN_NAMESPACE
 
@@ -66,7 +67,8 @@ struct ObjectIterator
     Object *current;
     SparseArrayNode *arrayNode;
     uint arrayIndex;
-    uint tableIndex;
+    InternalClass *currentClass;
+    uint memberIndex;
     uint flags;
 
     ObjectIterator(ExecutionContext *context, Object *o, uint flags);
index 585ccb4..679c271 100644 (file)
@@ -60,6 +60,8 @@ struct ObjectCtor: FunctionObject
 
 struct ObjectPrototype: Object
 {
+    ObjectPrototype(ExecutionEngine *engine) : Object(engine) {}
+
     void init(ExecutionContext *ctx, const Value &ctor);
 
     static Value method_getPrototypeOf(ExecutionContext *ctx);
diff --git a/src/v4/qv4propertytable.h b/src/v4/qv4propertytable.h
deleted file mode 100644 (file)
index 79e4c77..0000000
+++ /dev/null
@@ -1,234 +0,0 @@
-/****************************************************************************
-**
-** 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 QV4PROPERTYTABLE_H
-#define QV4PROPERTYTABLE_H
-
-#include "qmljs_value.h"
-#include "qv4propertydescriptor.h"
-
-QT_BEGIN_NAMESPACE
-
-namespace QQmlJS {
-namespace VM {
-
-struct ObjectIterator;
-
-struct PropertyTableEntry {
-    String *name;
-    uint valueIndex;
-    PropertyTableEntry *next;
-    int index;
-
-    inline PropertyTableEntry(String *name)
-        : name(name),
-          valueIndex(UINT_MAX),
-          next(0),
-          index(-1)
-    { }
-
-    inline unsigned hashValue() const { return name->hashValue(); }
-};
-
-class PropertyTable
-{
-    Q_DISABLE_COPY(PropertyTable)
-    friend struct ArrayObject;
-
-public:
-    PropertyTable()
-        : _properties(0)
-        , _buckets(0)
-        , _freeList(0)
-        , _propertyCount(0)
-        , _bucketCount(0)
-        , _primeIdx(-1)
-        , _allocated(0)
-    {}
-
-    ~PropertyTable()
-    {
-        qDeleteAll(_properties, _properties + _propertyCount);
-        delete[] _properties;
-        delete[] _buckets;
-    }
-
-    typedef PropertyTableEntry **iterator;
-    inline iterator begin() const { return _properties; }
-    inline iterator end() const { return _properties + _propertyCount; }
-
-    void remove(PropertyTableEntry *prop)
-    {
-        PropertyTableEntry **bucket = _buckets + (prop->hashValue() % _bucketCount);
-        if (*bucket == prop) {
-            *bucket = prop->next;
-        } else {
-            for (PropertyTableEntry *it = *bucket; it; it = it->next) {
-                if (it->next == prop) {
-                    it->next = it->next->next;
-                    break;
-                }
-            }
-        }
-
-        _properties[prop->index] = 0;
-        prop->next = _freeList;
-        _freeList = prop;
-    }
-
-    PropertyTableEntry *findEntry(String *name) const
-    {
-        if (_properties) {
-            for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) {
-                if (prop && prop->name->isEqualTo(name))
-                    return prop;
-            }
-        }
-
-        return 0;
-    }
-
-    uint find(String *name)
-    {
-        if (_properties) {
-            for (PropertyTableEntry *prop = _buckets[name->hashValue() % _bucketCount]; prop; prop = prop->next) {
-                if (prop && prop->name->isEqualTo(name))
-                    return prop->valueIndex;
-            }
-        }
-
-        return UINT_MAX;
-    }
-
-    PropertyTableEntry *insert(String *name)
-    {
-        if (PropertyTableEntry *prop = findEntry(name))
-            return prop;
-
-        if (_propertyCount == _allocated) {
-            if (! _allocated)
-                _allocated = 4;
-            else
-                _allocated *= 2;
-
-            PropertyTableEntry **properties = new PropertyTableEntry*[_allocated];
-            std::copy(_properties, _properties + _propertyCount, properties);
-            delete[] _properties;
-            _properties = properties;
-        }
-
-        PropertyTableEntry *prop;
-        if (_freeList) {
-            prop = _freeList;
-            prop->name = name;
-            _freeList = _freeList->next;
-        } else {
-            prop = new PropertyTableEntry(name);
-        }
-
-        prop->index = _propertyCount;
-        _properties[_propertyCount] = prop;
-        ++_propertyCount;
-
-        if (! _buckets || 3 * _propertyCount >= 2 * _bucketCount) {
-            rehash();
-        } else {
-            PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount];
-            prop->next = bucket;
-            bucket = prop;
-        }
-
-        return prop;
-//        prop->valueIndex = values.size();
-//        values.resize(values.size() + 1);
-//        return values.data() + prop->valueIndex;
-    }
-
-private:
-    void rehash()
-    {
-        _bucketCount = nextPrime();
-
-        delete[] _buckets;
-        _buckets = new PropertyTableEntry *[_bucketCount];
-        std::fill(_buckets, _buckets + _bucketCount, (PropertyTableEntry *) 0);
-
-        for (int i = 0; i < _propertyCount; ++i) {
-            PropertyTableEntry *prop = _properties[i];
-            if (prop) {
-                PropertyTableEntry *&bucket = _buckets[prop->hashValue() % _bucketCount];
-                prop->next = bucket;
-                bucket = prop;
-            }
-        }
-    }
-
-    inline int nextPrime()
-    {
-        // IMPORTANT: do not add more primes without checking if _primeIdx needs more bits!
-        static const int primes[] = {
-            11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877
-        };
-
-        if (_primeIdx < (int) (sizeof(primes)/sizeof(int)))
-            return primes[++_primeIdx];
-        else
-            return _bucketCount * 2 + 1; // Yes, we're degrading here. But who needs more than about 68000 properties?
-    }
-
-private:
-    friend struct ObjectIterator;
-    friend struct Object;
-
-    PropertyTableEntry **_properties;
-    PropertyTableEntry **_buckets;
-    PropertyTableEntry *_freeList;
-    int _propertyCount;
-    int _bucketCount;
-    int _primeIdx: 5;
-    int _allocated: 27;
-};
-
-} // namespace VM
-} // namespace QQmlJS
-
-QT_END_NAMESPACE
-
-#endif
index 8319a0c..2762c98 100644 (file)
@@ -65,13 +65,12 @@ using namespace QQmlJS::VM;
 
 
 RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr<RegExp> value, bool global)
-    : value(value)
+    : Object(engine)
+    , value(value)
     , global(global)
 {
     type = Type_RegExpObject;
 
-    if (!members)
-        members.reset(new PropertyTable());
     PropertyDescriptor *lastIndexProperty = insertMember(engine->newIdentifier(QStringLiteral("lastIndex")));
     lastIndexProperty->type = PropertyDescriptor::Data;
     lastIndexProperty->writable = PropertyDescriptor::Enabled;
@@ -88,7 +87,7 @@ RegExpObject::RegExpObject(ExecutionEngine *engine, PassRefPtr<RegExp> value, bo
 
 PropertyDescriptor *RegExpObject::lastIndexProperty(ExecutionContext *ctx)
 {
-    assert(0 == members->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex"))));
+    assert(0 == internalClass->find(ctx->engine->newIdentifier(QStringLiteral("lastIndex"))));
     return &memberData[0];
 }
 
index 6c7f583..dcf6120 100644 (file)
@@ -50,7 +50,6 @@
 #include "qv4isel_p.h"
 #include "qv4managed.h"
 #include "qv4propertydescriptor.h"
-#include "qv4propertytable.h"
 #include "qv4objectiterator.h"
 #include "qv4regexp.h"
 
index dee5dae..1e226b1 100644 (file)
@@ -76,7 +76,7 @@
 using namespace QQmlJS::VM;
 
 StringObject::StringObject(ExecutionContext *ctx, const Value &value)
-    : value(value)
+    : Object(ctx->engine), value(value)
 {
     type = Type_StringObject;
 
index 79b3316..2197ae3 100644 (file)
@@ -29,6 +29,7 @@ SOURCES += \
     debugging.cpp \
     qv4mm.cpp \
     qv4managed.cpp \
+    qv4internalclass.cpp \
     qv4sparsearray.cpp \
     qv4arrayobject.cpp \
     qv4argumentsobject.cpp \
@@ -65,6 +66,7 @@ HEADERS += \
     qv4identifier.h \
     qv4mm.h \
     qv4managed.h \
+    qv4internalclass.h \
     qv4sparsearray.h \
     qv4arrayobject.h \
     qv4argumentsobject.h \
@@ -82,7 +84,6 @@ HEADERS += \
     qv4stringobject.h \
     qv4string.h \
     qv4propertydescriptor.h \
-    qv4propertytable.h \
     qv4objectiterator.h \
     qv4regexp.h