Ported the compile-time class setup for object literals to be run-time based
authorSimon Hausmann <simon.hausmann@digia.com>
Fri, 16 Aug 2013 11:21:31 +0000 (13:21 +0200)
committerLars Knoll <lars.knoll@digia.com>
Fri, 16 Aug 2013 14:52:18 +0000 (16:52 +0200)
Change-Id: I21225e4bf0f66914229cf64fa29ce870548c0e93
Reviewed-by: Lars Knoll <lars.knoll@digia.com>
src/qml/compiler/qv4compileddata.cpp
src/qml/compiler/qv4compileddata_p.h
src/qml/compiler/qv4compiler.cpp
src/qml/compiler/qv4compiler_p.h
src/qml/compiler/qv4instr_moth_p.h
src/qml/compiler/qv4isel_masm.cpp
src/qml/compiler/qv4isel_moth.cpp
src/qml/compiler/qv4isel_p.h
src/qml/jsruntime/qv4runtime.cpp
src/qml/jsruntime/qv4runtime_p.h
src/qml/jsruntime/qv4vme_moth.cpp

index 6ac7a48..4b7f132 100644 (file)
@@ -57,6 +57,7 @@ CompilationUnit::~CompilationUnit()
     free(runtimeStrings);
     delete [] runtimeLookups;
     delete [] runtimeRegularExpressions;
+    free(runtimeClasses);
 }
 
 QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
@@ -105,6 +106,20 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
         }
     }
 
+    if (data->jsClassTableSize) {
+        runtimeClasses = (QV4::InternalClass**)malloc(data->jsClassTableSize * sizeof(QV4::InternalClass*));
+
+        for (int i = 0; i < data->jsClassTableSize; ++i) {
+            int memberCount = 0;
+            const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount);
+            QV4::InternalClass *klass = engine->emptyClass;
+            for (int j = 0; j < memberCount; ++j, ++member)
+                klass = klass->addMember(runtimeStrings[member->nameOffset], member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
+
+            runtimeClasses[i] = klass;
+        }
+    }
+
     return linkBackendToEngine(engine);
 }
 
index 7b27d08..f050c4c 100644 (file)
@@ -94,6 +94,20 @@ struct Lookup
     static int calculateSize() { return sizeof(Lookup); }
 };
 
+struct JSClassMember
+{
+    uint nameOffset : 31;
+    uint isAccessor : 1;
+};
+
+struct JSClass
+{
+    uint nMembers;
+    // JSClassMember[nMembers]
+
+    static int calculateSize(int nMembers) { return (sizeof(JSClass) + nMembers * sizeof(JSClassMember) + 7) & ~7; }
+};
+
 static const char magic_str[] = "qv4cdata";
 
 struct Unit
@@ -115,6 +129,8 @@ struct Unit
     uint offsetToLookupTable;
     uint regexpTableSize;
     uint offsetToRegexpTable;
+    uint jsClassTableSize;
+    uint offsetToJSClassTable;
     uint indexOfRootFunction;
     quint32 sourceFileIndex;
 
@@ -135,10 +151,19 @@ struct Unit
         return reinterpret_cast<const RegExp*>(reinterpret_cast<const char *>(this) + offsetToRegexpTable + index * sizeof(RegExp));
     }
 
+    const JSClassMember *jsClassAt(int idx, int *nMembers) const {
+        const uint *offsetTable = reinterpret_cast<const uint *>(reinterpret_cast<const char *>(this) + offsetToJSClassTable);
+        const uint offset = offsetTable[idx];
+        const char *ptr = reinterpret_cast<const char *>(this) + offset;
+        const JSClass *klass = reinterpret_cast<const JSClass *>(ptr);
+        *nMembers = klass->nMembers;
+        return reinterpret_cast<const JSClassMember*>(ptr + sizeof(JSClass));
+    }
+
     static int calculateSize(uint nStrings, uint nFunctions, uint nRegExps,
-                             uint nLookups) {
+                             uint nLookups, uint nClasses) {
         return (sizeof(Unit)
-                + (nStrings + nFunctions) * sizeof(uint)
+                + (nStrings + nFunctions + nClasses) * sizeof(uint)
                 + nRegExps * RegExp::calculateSize()
                 + nLookups * Lookup::calculateSize()
                 + 7) & ~7; }
@@ -287,6 +312,7 @@ struct CompilationUnit
         , data(0)
         , runtimeStrings(0)
         , runtimeLookups(0)
+        , runtimeClasses(0)
     {}
     virtual ~CompilationUnit();
 
@@ -302,6 +328,7 @@ struct CompilationUnit
     QV4::String **runtimeStrings; // Array
     QV4::Lookup *runtimeLookups;
     QV4::Value *runtimeRegularExpressions;
+    QV4::InternalClass **runtimeClasses;
 
     QV4::Function *linkToEngine(QV4::ExecutionEngine *engine);
 
index 385f668..8e09e57 100644 (file)
@@ -47,6 +47,7 @@
 QV4::Compiler::JSUnitGenerator::JSUnitGenerator(QV4::ExecutionEngine *engine, QQmlJS::V4IR::Module *module)
     : irModule(module)
     , stringDataSize(0)
+    , jsClassDataSize(0)
 {
 }
 
@@ -116,6 +117,37 @@ void QV4::Compiler::JSUnitGenerator::registerLineNumberMapping(QQmlJS::V4IR::Fun
     lineNumberMappingsPerFunction.insert(function, mappings);
 }
 
+int QV4::Compiler::JSUnitGenerator::registerJSClass(QQmlJS::V4IR::ExprList *args)
+{
+    // ### re-use existing class definitions.
+
+    QList<CompiledData::JSClassMember> members;
+
+    QQmlJS::V4IR::ExprList *it = args;
+    while (it) {
+        CompiledData::JSClassMember member;
+
+        QQmlJS::V4IR::Name *name = it->expr->asName();
+        it = it->next;
+
+        const bool isData = it->expr->asConst()->value;
+        it = it->next;
+
+        member.nameOffset = registerString(*name->id);
+        member.isAccessor = !isData;
+        members << member;
+
+        if (!isData)
+            it = it->next;
+
+        it = it->next;
+    }
+
+    jsClasses << members;
+    jsClassDataSize += CompiledData::JSClass::calculateSize(members.count());
+    return jsClasses.size() - 1;
+}
+
 QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit()
 {
     registerString(irModule->fileName);
@@ -127,7 +159,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit()
             registerString(*f->locals.at(i));
     }
 
-    int unitSize = QV4::CompiledData::Unit::calculateSize(strings.size(), irModule->functions.size(), regexps.size(), lookups.size());
+    int unitSize = QV4::CompiledData::Unit::calculateSize(strings.size(), irModule->functions.size(), regexps.size(), lookups.size(), jsClasses.count());
 
     uint functionDataSize = 0;
     for (int i = 0; i < irModule->functions.size(); ++i) {
@@ -142,7 +174,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit()
         functionDataSize += QV4::CompiledData::Function::calculateSize(f->formals.size(), f->locals.size(), f->nestedFunctions.size(), lineNumberMappingCount);
     }
 
-    char *data = (char *)malloc(unitSize + functionDataSize + stringDataSize);
+    char *data = (char *)malloc(unitSize + functionDataSize + stringDataSize + jsClassDataSize);
     QV4::CompiledData::Unit *unit = (QV4::CompiledData::Unit*)data;
 
     memcpy(unit->magic, QV4::CompiledData::magic_str, sizeof(unit->magic));
@@ -157,6 +189,8 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit()
     unit->offsetToLookupTable = unit->offsetToFunctionTable + unit->functionTableSize * sizeof(uint);
     unit->regexpTableSize = regexps.size();
     unit->offsetToRegexpTable = unit->offsetToLookupTable + unit->lookupTableSize * CompiledData::Lookup::calculateSize();
+    unit->jsClassTableSize = jsClasses.count();
+    unit->offsetToJSClassTable = unit->offsetToRegexpTable + unit->regexpTableSize * CompiledData::RegExp::calculateSize();
     unit->sourceFileIndex = getStringId(irModule->fileName);
 
     // write strings and string table
@@ -200,6 +234,24 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit()
     CompiledData::RegExp *regexpTable = (CompiledData::RegExp *)(data + unit->offsetToRegexpTable);
     memcpy(regexpTable, regexps.constData(), regexps.size() * sizeof(*regexpTable));
 
+    // write js classes and js class lookup table
+    uint *jsClassTable = (uint*)(data + unit->offsetToJSClassTable);
+    char *jsClass = data + unitSize + stringDataSize + functionDataSize;
+    for (int i = 0; i < jsClasses.count(); ++i) {
+        jsClassTable[i] = jsClass - data;
+
+        const QList<CompiledData::JSClassMember> members = jsClasses.at(i);
+
+        CompiledData::JSClass *c = reinterpret_cast<CompiledData::JSClass*>(jsClass);
+        c->nMembers = members.count();
+
+        CompiledData::JSClassMember *memberToWrite = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + sizeof(CompiledData::JSClass));
+        foreach (const CompiledData::JSClassMember &member, members)
+            *memberToWrite++ = member;
+
+        jsClass += CompiledData::JSClass::calculateSize(members.count());
+    }
+
     return unit;
 }
 
index 1032369..c6dacfd 100644 (file)
@@ -52,6 +52,7 @@ namespace CompiledData {
 struct Unit;
 struct Lookup;
 struct RegExp;
+struct JSClassMember;
 }
 
 namespace Compiler {
@@ -72,6 +73,8 @@ struct JSUnitGenerator {
 
     void registerLineNumberMapping(QQmlJS::V4IR::Function *function, const QVector<uint> &mappings);
 
+    int registerJSClass(QQmlJS::V4IR::ExprList *args);
+
     QV4::CompiledData::Unit *generateUnit();
     // Returns bytes written
     int writeFunction(char *f, int index, QQmlJS::V4IR::Function *irFunction);
@@ -83,6 +86,8 @@ struct JSUnitGenerator {
     QList<CompiledData::Lookup> lookups;
     QVector<CompiledData::RegExp> regexps;
     QHash<QQmlJS::V4IR::Function *, QVector<uint> > lineNumberMappingsPerFunction;
+    QList<QList<CompiledData::JSClassMember> > jsClasses;
+    int jsClassDataSize;
 };
 
 }
index 69c7c36..2cc1b3e 100644 (file)
@@ -436,7 +436,7 @@ union Instr
     };
     struct instr_callBuiltinDefineObjectLiteral {
         MOTH_INSTR_HEADER
-        QV4::InternalClass *internalClass;
+        int internalClassId;
         quint32 args;
         Param result;
     };
index df5fa94..8e7d7a1 100644 (file)
@@ -1000,15 +1000,14 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4
 {
     int argc = 0;
 
-    InternalClass *klass = engine()->emptyClass;
+    const int classId = registerJSClass(args);
+
     V4IR::ExprList *it = args;
     while (it) {
-        V4IR::Name *name = it->expr->asName();
         it = it->next;
 
         bool isData = it->expr->asConst()->value;
         it = it->next;
-        klass = klass->addMember(engine()->newIdentifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor);
 
         _as->copyValue(argumentAddressForCall(argc++), it->expr);
 
@@ -1022,7 +1021,7 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4
 
     generateFunctionCall(Assembler::Void, __qmljs_builtin_define_object_literal, Assembler::ContextRegister,
                          Assembler::PointerToValue(result), baseAddressForCallArguments(),
-                         Assembler::TrustedImmPtr(klass));
+                         Assembler::TrustedImm32(classId));
 }
 
 void InstructionSelection::callValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result)
index f7ff490..f7dd4d2 100644 (file)
@@ -965,15 +965,13 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4
 {
     int argLocation = outgoingArgumentTempStart();
 
-    QV4::InternalClass *klass = engine()->emptyClass;
+    const int classId = registerJSClass(args);
     V4IR::ExprList *it = args;
     while (it) {
-        V4IR::Name *name = it->expr->asName();
         it = it->next;
 
         bool isData = it->expr->asConst()->value;
         it = it->next;
-        klass = klass->addMember(identifier(*name->id), isData ? QV4::Attr_Data : QV4::Attr_Accessor);
 
         Instruction::MoveTemp move;
         move.source = getParam(it->expr);
@@ -995,7 +993,7 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(V4IR::Temp *result, V4
     }
 
     Instruction::CallBuiltinDefineObjectLiteral call;
-    call.internalClass = klass;
+    call.internalClassId = classId;
     call.args = outgoingArgumentTempStart();
     call.result = getResultParam(result);
     addInstruction(call);
index 3553d9d..314af51 100644 (file)
@@ -74,6 +74,7 @@ public:
     uint registerSetterLookup(const QString &str) { return jsUnitGenerator.registerSetterLookup(str); }
     uint registerGlobalGetterLookup(const QString &str) { return jsUnitGenerator.registerGlobalGetterLookup(str); }
     void registerLineNumberMapping(V4IR::Function *function, const QVector<uint> &mappings) { jsUnitGenerator.registerLineNumberMapping(function, mappings); }
+    int registerJSClass(QQmlJS::V4IR::ExprList *args) { return jsUnitGenerator.registerJSClass(args); }
 
 protected:
     QV4::Function *createFunctionMapping(QV4::Function *outer, V4IR::Function *irFunction);
index 7d99a8e..3e0cf9a 100644 (file)
@@ -1179,8 +1179,11 @@ void __qmljs_builtin_define_getter_setter(ExecutionContext *ctx, const Value &ob
     pd->setSetter(setter ? setter->asFunctionObject() : 0);
 }
 
-void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass)
+void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, int classId)
 {
+    const QV4::Function *runtimeFunction = ctx->runtimeFunction();
+    Q_ASSERT(runtimeFunction);
+    QV4::InternalClass *klass = runtimeFunction->compilationUnit->runtimeClasses[classId];
     Object *o = ctx->engine->newObject(klass);
 
     for (int i = 0; i < klass->size; ++i) {
index 6f00d5b..ba6681e 100644 (file)
@@ -124,7 +124,7 @@ void __qmljs_builtin_declare_var(QV4::ExecutionContext *ctx, bool deletable, QV4
 void __qmljs_builtin_define_property(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, QV4::Value *val);
 void __qmljs_builtin_define_array(QV4::ExecutionContext *ctx, QV4::Value *array, QV4::Value *values, uint length);
 void __qmljs_builtin_define_getter_setter(QV4::ExecutionContext *ctx, const QV4::Value &object, QV4::String *name, const QV4::Value *getter, const QV4::Value *setter);
-void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, QV4::InternalClass *klass);
+void __qmljs_builtin_define_object_literal(QV4::ExecutionContext *ctx, QV4::Value *result, const QV4::Value *args, int classId);
 
 void __qmljs_value_from_string(QV4::Value *result, QV4::String *string);
 void __qmljs_lookup_runtime_regexp(QV4::ExecutionContext *ctx, QV4::Value *result, int id);
index a06ce41..b5e6fc1 100644 (file)
@@ -464,7 +464,7 @@ QV4::Value VME::run(QV4::ExecutionContext *context, const uchar *&code,
 
     MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral)
         QV4::Value *args = stack + instr.args;
-        __qmljs_builtin_define_object_literal(context, VALUEPTR(instr.result), args, instr.internalClass);
+        __qmljs_builtin_define_object_literal(context, VALUEPTR(instr.result), args, instr.internalClassId);
     MOTH_END_INSTR(CallBuiltinDefineObjectLiteral)
 
     MOTH_BEGIN_INSTR(CreateValue)