diff options
author | Erik Verbruggen <[email protected]> | 2017-06-16 15:24:28 +0200 |
---|---|---|
committer | Lars Knoll <[email protected]> | 2017-06-20 10:03:37 +0000 |
commit | 24969e19b18ea68019ae66c99982f04b2d05a9d1 (patch) | |
tree | a4939d916d301b86659d82b30e63d8b1f184ff6e /src | |
parent | b829693623f886975a3e79c2f6804a7564449977 (diff) |
Support object literals
Change-Id: I7fc4565044caf23bec239561be4fbc020ccc2468
Reviewed-by: Lars Knoll <[email protected]>
Diffstat (limited to 'src')
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 172 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 15 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 23 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler_p.h | 7 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_moth.cpp | 7 | ||||
-rw-r--r-- | src/qml/compiler/qv4isel_p.h | 4 | ||||
-rw-r--r-- | src/qml/jit/qv4isel_masm.cpp | 7 |
7 files changed, 110 insertions, 125 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 6a0973f774..b443c5364f 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2104,21 +2104,6 @@ bool Codegen::visit(NumericLiteral *ast) return false; } -struct ObjectPropertyValue { - ObjectPropertyValue() - : value(0) - , getter(-1) - , setter(-1) - {} - - IR::Expr *value; - int getter; // index in _module->functions or -1 if not set - int setter; - - bool hasGetter() const { return getter >= 0; } - bool hasSetter() const { return setter >= 0; } -}; - bool Codegen::visit(ObjectLiteral *ast) { if (hasError) @@ -2126,33 +2111,28 @@ bool Codegen::visit(ObjectLiteral *ast) QMap<QString, ObjectPropertyValue> valueMap; - const unsigned t = _block->newTemp(); + auto result = Reference::fromTemp(this, _block->newTemp()); TempScope scope(_function); for (PropertyAssignmentList *it = ast->properties; it; it = it->next) { QString name = it->assignment->name->asString(); if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) { - Result value = expression(nv->value); + Reference value = expression(nv->value); if (hasError) return false; + ObjectPropertyValue &v = valueMap[name]; - if (v.hasGetter() || v.hasSetter() || (_function->isStrict && v.value)) { + if (v.hasGetter() || v.hasSetter() || (_function->isStrict && v.rvalue.isValid())) { throwSyntaxError(nv->lastSourceLocation(), QStringLiteral("Illegal duplicate key '%1' in object literal").arg(name)); return false; } - if (IR::Const *c = (*value)->asConst()) { - valueMap[name].value = c; - } else { - unsigned t = _block->newTemp(); - move(_block->TEMP(t), *value); - valueMap[name].value = _block->TEMP(t); - } + v.rvalue = value; } else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) { const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0); ObjectPropertyValue &v = valueMap[name]; - if (v.value || + if (v.rvalue.isValid() || (gs->type == PropertyGetterSetter::Getter && v.hasGetter()) || (gs->type == PropertyGetterSetter::Setter && v.hasSetter())) { throwSyntaxError(gs->lastSourceLocation(), @@ -2168,96 +2148,82 @@ bool Codegen::visit(ObjectLiteral *ast) } } - // The linked-list arguments to builtin_define_object_literal - // begin with a CONST counting the number of key/value pairs, followed by the - // key value pairs, followed by the array entries. - IR::ExprList *args = _function->New<IR::ExprList>(); - - IR::Const *entryCountParam = _function->New<IR::Const>(); - entryCountParam->init(IR::SInt32Type, 0); - args->expr = entryCountParam; - args->next = 0; - - IR::ExprList *keyValueEntries = 0; - IR::ExprList *currentKeyValueEntry = 0; - int keyValueEntryCount = 0; - IR::ExprList *arrayEntries = 0; + QVector<QString> nonArrayKey, arrayKeyWithValue, arrayKeyWithGetterSetter; + bool needSparseArray = false; // set to true if any array index is bigger than 16 - IR::ExprList *currentArrayEntry = 0; - - for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(); it != valueMap.end(); ) { - IR::ExprList **currentPtr = 0; - uint keyAsIndex = QV4::String::toArrayIndex(it.key()); - if (keyAsIndex != UINT_MAX) { - if (!arrayEntries) { - arrayEntries = _function->New<IR::ExprList>(); - currentArrayEntry = arrayEntries; - } else { - currentArrayEntry->next = _function->New<IR::ExprList>(); - currentArrayEntry = currentArrayEntry->next; - } - currentPtr = ¤tArrayEntry; - IR::Const *idx = _function->New<IR::Const>(); - idx->init(IR::UInt32Type, keyAsIndex); - (*currentPtr)->expr = idx; + for (QMap<QString, ObjectPropertyValue>::iterator it = valueMap.begin(), eit = valueMap.end(); + it != eit; ++it) { + QString name = it.key(); + uint keyAsIndex = QV4::String::toArrayIndex(name); + if (keyAsIndex != std::numeric_limits<uint>::max()) { + it->keyAsIndex = keyAsIndex; + if (keyAsIndex > 16) + needSparseArray = true; + if (it->hasSetter() || it->hasGetter()) + arrayKeyWithGetterSetter.append(name); + else + arrayKeyWithValue.append(name); } else { - if (!keyValueEntries) { - keyValueEntries = _function->New<IR::ExprList>(); - currentKeyValueEntry = keyValueEntries; - } else { - currentKeyValueEntry->next = _function->New<IR::ExprList>(); - currentKeyValueEntry = currentKeyValueEntry->next; - } - currentPtr = ¤tKeyValueEntry; - (*currentPtr)->expr = _block->NAME(it.key(), 0, 0); - keyValueEntryCount++; + nonArrayKey.append(name); } + } - IR::ExprList *¤t = *currentPtr; - if (it->value) { - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->CONST(IR::BoolType, true); + int argc = 0; + auto push = [this, &argc](const Reference &arg) { + Reference::fromTemp(this, argc).store(arg); + argc += 1; + }; - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = it->value; + auto undefined = [this](){ return Reference::fromConst(this, Encode::undefined()); }; + QVector<QV4::Compiler::JSUnitGenerator::MemberInfo> members; + + // generate the key/value pairs + for (const QString &key : qAsConst(nonArrayKey)) { + const ObjectPropertyValue &prop = valueMap[key]; + + if (prop.hasGetter() || prop.hasSetter()) { + Q_ASSERT(!prop.rvalue.isValid()); + push(prop.hasGetter() ? Reference::fromClosure(this, prop.getter) : undefined()); + push(prop.hasSetter() ? Reference::fromClosure(this, prop.setter) : undefined()); + members.append({ key, true }); } else { - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->CONST(IR::BoolType, false); - - unsigned getter = _block->newTemp(); - unsigned setter = _block->newTemp(); - move(_block->TEMP(getter), it->hasGetter() ? _block->CLOSURE(it->getter) : _block->CONST(IR::UndefinedType, 0)); - move(_block->TEMP(setter), it->hasSetter() ? _block->CLOSURE(it->setter) : _block->CONST(IR::UndefinedType, 0)); - - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->TEMP(getter); - current->next = _function->New<IR::ExprList>(); - current = current->next; - current->expr = _block->TEMP(setter); + Q_ASSERT(prop.rvalue.isValid()); + push(prop.rvalue); + members.append({ key, false }); } - - it = valueMap.erase(it); } - entryCountParam->value = keyValueEntryCount; + // generate array entries with values + for (const QString &key : qAsConst(arrayKeyWithValue)) { + const ObjectPropertyValue &prop = valueMap[key]; + Q_ASSERT(!prop.hasGetter() && !prop.hasSetter()); + push(Reference::fromConst(this, Encode(prop.keyAsIndex))); + push(prop.rvalue); + } - if (keyValueEntries) - args->next = keyValueEntries; - if (arrayEntries) { - if (currentKeyValueEntry) - currentKeyValueEntry->next = arrayEntries; - else - args->next = arrayEntries; + // generate array entries with both a value and a setter + for (const QString &key : qAsConst(arrayKeyWithGetterSetter)) { + const ObjectPropertyValue &prop = valueMap[key]; + Q_ASSERT(!prop.rvalue.isValid()); + push(Reference::fromConst(this, Encode(prop.keyAsIndex))); + push(prop.hasGetter() ? Reference::fromClosure(this, prop.getter) : undefined()); + push(prop.hasSetter() ? Reference::fromClosure(this, prop.setter) : undefined()); } - move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_object_literal, - ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args)); + int classId = jsUnitGenerator->registerJSClass(members); + + uint arrayGetterSetterCountAndFlags = arrayKeyWithGetterSetter.size(); + arrayGetterSetterCountAndFlags |= needSparseArray << 30; + + QV4::Moth::Instruction::CallBuiltinDefineObjectLiteral call; + call.internalClassId = classId; + call.arrayValueCount = arrayKeyWithValue.size(); + call.arrayGetterSetterCountAndFlags = arrayGetterSetterCountAndFlags; + call.args = 0; + call.result = result.asLValue(); + bytecodeGenerator->addInstruction(call); - _expr.code = _block->TEMP(t); + _expr.result = result; return false; } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index cad37c30d4..4762fff074 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -225,6 +225,21 @@ public: int tempCountForScope; }; + struct ObjectPropertyValue { + ObjectPropertyValue() + : getter(-1) + , setter(-1) + , keyAsIndex(UINT_MAX) + {} + + Reference rvalue; + int getter; // index in _module->functions or -1 if not set + int setter; + uint keyAsIndex; + + bool hasGetter() const { return getter >= 0; } + bool hasSetter() const { return setter >= 0; } + }; protected: enum Format { ex, cx, nx }; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index acfa2fb99a..373c7e9d87 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -201,33 +201,24 @@ QV4::ReturnedValue QV4::Compiler::JSUnitGenerator::constant(int idx) return constants.at(idx); } -int QV4::Compiler::JSUnitGenerator::registerJSClass(int count, IR::ExprList *args) +int QV4::Compiler::JSUnitGenerator::registerJSClass(const QVector<MemberInfo> &members) { // ### re-use existing class definitions. - const int size = CompiledData::JSClass::calculateSize(count); + const int size = CompiledData::JSClass::calculateSize(members.size()); jsClassOffsets.append(jsClassData.size()); const int oldSize = jsClassData.size(); jsClassData.resize(jsClassData.size() + size); memset(jsClassData.data() + oldSize, 0, size); CompiledData::JSClass *jsClass = reinterpret_cast<CompiledData::JSClass*>(jsClassData.data() + oldSize); - jsClass->nMembers = count; + jsClass->nMembers = members.size(); CompiledData::JSClassMember *member = reinterpret_cast<CompiledData::JSClassMember*>(jsClass + 1); - IR::ExprList *it = args; - for (int i = 0; i < count; ++i, it = it->next, ++member) { - QV4::IR::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; - - if (!isData) - it = it->next; + for (const MemberInfo &memberInfo : members) { + member->nameOffset = registerString(memberInfo.name); + member->isAccessor = memberInfo.isAccessor; + ++member; } return jsClassOffsets.size() - 1; diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 4571268cd8..bdf39c887e 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -90,6 +90,11 @@ private: }; struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { + struct MemberInfo { + QString name; + bool isAccessor; + }; + JSUnitGenerator(IR::Module *module); int registerString(const QString &str) { return stringTable.registerString(str); } @@ -108,7 +113,7 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { int registerConstant(ReturnedValue v); ReturnedValue constant(int idx); - int registerJSClass(int count, IR::ExprList *args); + int registerJSClass(const QVector<MemberInfo> &members); int registerJSClass(int count, CompiledData::JSClassMember *members); enum GeneratorOption { diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 7d62d71e98..24fbe3c680 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -1219,15 +1219,16 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int { int argLocation = outgoingArgumentTempStart(); - const int classId = registerJSClass(keyValuePairCount, keyValuePairs); + QVector<Compiler::JSUnitGenerator::MemberInfo> members; // Process key/value pairs first IR::ExprList *it = keyValuePairs; for (int i = 0; i < keyValuePairCount; ++i, it = it->next) { - // Skip name + QString key = *it->expr->asName()->id; it = it->next; bool isData = it->expr->asConst()->value; + members.append({ key, !isData }); it = it->next; if (IR::Const *c = it->expr->asConst()) { @@ -1254,6 +1255,8 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int } } + const int classId = registerJSClass(members); + // Process array values uint arrayValueCount = 0; it = arrayEntries; diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index 037c02e5ea..e27bf0e284 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -87,7 +87,9 @@ public: uint registerSetterLookup(const QString &name) { return jsGenerator->registerSetterLookup(name); } uint registerGlobalGetterLookup(const QString &name) { return jsGenerator->registerGlobalGetterLookup(name); } int registerRegExp(IR::RegExp *regexp) { return jsGenerator->registerRegExp(regexp); } - int registerJSClass(int count, IR::ExprList *args) { return jsGenerator->registerJSClass(count, args); } + int registerJSClass(const QVector<Compiler::JSUnitGenerator::MemberInfo> &members) { + return jsGenerator->registerJSClass(members); + } QV4::Compiler::JSUnitGenerator *jsUnitGenerator() const { return jsGenerator; } protected: diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 7784eb364e..e1974f5776 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -339,15 +339,16 @@ void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr { Q_ASSERT(result); + QVector<Compiler::JSUnitGenerator::MemberInfo> members; int argc = 0; - const int classId = registerJSClass(keyValuePairCount, keyValuePairs); - IR::ExprList *it = keyValuePairs; for (int i = 0; i < keyValuePairCount; ++i, it = it->next) { + QString key = *it->expr->asName()->id; it = it->next; bool isData = it->expr->asConst()->value; + members.append({ key, !isData }); it = it->next; _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); @@ -358,6 +359,8 @@ void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr } } + const int classId = registerJSClass(members); + it = arrayEntries; uint arrayValueCount = 0; while (it) { |