diff options
Diffstat (limited to 'src/qml')
129 files changed, 879 insertions, 27603 deletions
diff --git a/src/qml/compiler/qqmlpropertycachecreator_p.h b/src/qml/compiler/qqmlpropertycachecreator_p.h index 21d653af55..346cfb5803 100644 --- a/src/qml/compiler/qqmlpropertycachecreator_p.h +++ b/src/qml/compiler/qqmlpropertycachecreator_p.h @@ -704,8 +704,9 @@ inline QQmlCompileError QQmlPropertyCacheAliasCreator<ObjectContainer>::property QVarLengthArray<const QV4::CompiledData::Alias *, 4> seenAliases({lastAlias}); do { - const CompiledObject *targetObject = objectContainer->objectAt( - objectForId(component, lastAlias->targetObjectId)); + const int targetObjectIndex = objectForId(component, lastAlias->targetObjectId); + Q_ASSERT(targetObjectIndex >= 0); + const CompiledObject *targetObject = objectContainer->objectAt(targetObjectIndex); Q_ASSERT(targetObject); auto nextAlias = targetObject->aliasesBegin(); diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index 1d0a57c536..acd4aa62ea 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -66,8 +66,6 @@ namespace Moth { class BytecodeGenerator { public: - typedef CompiledData::Function::TraceInfoCount TraceInfoCount; - BytecodeGenerator(int line, bool debug) : startLine(line), debugMode(debug) {} @@ -164,15 +162,6 @@ public: addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr); } - // Same as addInstruction, but also add a trace slot. Move only, because the instruction cannot - // be reused afterwards. - template<int InstrT> - void addTracingInstruction(InstrData<InstrT> data) - { - data.traceSlot = nextTraceInfo(); - addInstruction(data); - } - Q_REQUIRED_RESULT Jump jump() { QT_WARNING_PUSH @@ -184,12 +173,12 @@ QT_WARNING_POP Q_REQUIRED_RESULT Jump jumpTrue() { - return addTracingJumpInstruction(Instruction::JumpTrue()); + return addJumpInstruction(Instruction::JumpTrue()); } Q_REQUIRED_RESULT Jump jumpFalse() { - return addTracingJumpInstruction(Instruction::JumpFalse()); + return addJumpInstruction(Instruction::JumpFalse()); } Q_REQUIRED_RESULT Jump jumpNotUndefined() @@ -209,7 +198,7 @@ QT_WARNING_POP Instruction::CmpStrictEqual cmp; cmp.lhs = lhs; addInstruction(std::move(cmp)); - addTracingJumpInstruction(Instruction::JumpTrue()).link(target); + addJumpInstruction(Instruction::JumpTrue()).link(target); } void jumpStrictNotEqual(const StackSlot &lhs, const Label &target) @@ -217,7 +206,13 @@ QT_WARNING_POP Instruction::CmpStrictNotEqual cmp; cmp.lhs = lhs; addInstruction(std::move(cmp)); - addTracingJumpInstruction(Instruction::JumpTrue()).link(target); + addJumpInstruction(Instruction::JumpTrue()).link(target); + } + + void checkException() + { + Instruction::CheckException chk; + addInstruction(chk); } void setUnwindHandler(ExceptionHandler *handler) @@ -258,13 +253,6 @@ QT_WARNING_POP void finalize(Compiler::Context *context); template<int InstrT> - Jump addTracingJumpInstruction(InstrData<InstrT> &&data) - { - data.traceSlot = nextTraceInfo(); - return addJumpInstruction(data); - } - - template<int InstrT> Jump addJumpInstruction(const InstrData<InstrT> &data) { Instr genericInstr; @@ -275,9 +263,9 @@ QT_WARNING_POP void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel) { if (jumpOnFalse) - addTracingJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); + addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); else - addTracingJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); + addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); } void clearLastInstruction() @@ -285,27 +273,6 @@ QT_WARNING_POP lastInstrType = -1; } - TraceInfoCount nextTraceInfo() - { - // If tracing is disabled, use slot 0 to unconditionally store all trace info - if (nTraceInfos == CompiledData::Function::NoTracing()) - return TraceInfoCount(0); - return nTraceInfos++; - } - - void setTracing(bool onoff, int argumentCount) - { - if (onoff) - nTraceInfos = argumentCount; - else - nTraceInfos = CompiledData::Function::NoTracing(); - } - - TraceInfoCount traceInfoCount() const - { - return nTraceInfos; - } - void addLoopStart(const Label &start) { _labelInfos.push_back({ start.index }); @@ -346,8 +313,6 @@ private: int lastInstrType = -1; Moth::Instr lastInstr; - TraceInfoCount nTraceInfos = TraceInfoCount(0); - struct LabelInfo { int labelIndex; }; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 88d3dbe9c5..1bf0e7147d 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -289,13 +289,13 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) case UMinus: { expr.loadInAccumulator(); Instruction::UMinus uminus = {}; - bytecodeGenerator->addTracingInstruction(uminus); + bytecodeGenerator->addInstruction(uminus); return Reference::fromAccumulator(this); } case UPlus: { expr.loadInAccumulator(); Instruction::UPlus uplus = {}; - bytecodeGenerator->addTracingInstruction(uplus); + bytecodeGenerator->addInstruction(uplus); return Reference::fromAccumulator(this); } case Not: { @@ -315,10 +315,10 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::UPlus uplus = {}; - bytecodeGenerator->addTracingInstruction(uplus); + bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); e.storeConsumeAccumulator(); return originalValue; } else { @@ -330,7 +330,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); if (exprAccept(nx)) return e.storeConsumeAccumulator(); else @@ -341,10 +341,10 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::UPlus uplus = {}; - bytecodeGenerator->addTracingInstruction(uplus); + bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); e.storeConsumeAccumulator(); return originalValue; } else { @@ -356,7 +356,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Reference e = expr.asLValue(); e.loadInAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); if (exprAccept(nx)) return e.storeConsumeAccumulator(); else @@ -1139,7 +1139,7 @@ bool Codegen::visit(ArrayPattern *ast) index.loadInAccumulator(); Instruction::Increment inc = {}; - bytecodeGenerator->addTracingInstruction(inc); + bytecodeGenerator->addInstruction(inc); index.storeConsumeAccumulator(); }; @@ -1196,11 +1196,12 @@ bool Codegen::visit(ArrayPattern *ast) next.value = lhsValue.stackSlot(); next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end); + bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end); lhsValue.loadInAccumulator(); pushAccumulator(); + bytecodeGenerator->checkException(); bytecodeGenerator->jump().link(in); end.link(); } @@ -1487,20 +1488,20 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Add add; add.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(add); + bytecodeGenerator->addInstruction(add); break; } case QSOperator::Sub: { if (right.isConstant() && right.constant == Encode(int(1))) { left.loadInAccumulator(); Instruction::Decrement dec = {}; - bytecodeGenerator->addTracingInstruction(dec); + bytecodeGenerator->addInstruction(dec); } else { left = left.storeOnStack(); right.loadInAccumulator(); Instruction::Sub sub; sub.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(sub); + bytecodeGenerator->addInstruction(sub); } break; } @@ -1517,7 +1518,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mul mul; mul.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(mul); + bytecodeGenerator->addInstruction(mul); break; } case QSOperator::Div: { @@ -1533,7 +1534,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mod mod; mod.lhs = left.stackSlot(); - bytecodeGenerator->addTracingInstruction(mod); + bytecodeGenerator->addInstruction(mod); break; } case QSOperator::BitAnd: @@ -1902,7 +1903,7 @@ bool Codegen::visit(CallExpression *ast) call.thisObject = baseObject.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::TailCall call; call.func = base.stackSlot(); @@ -1931,14 +1932,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.lookupIndex = registerGetterLookup(base.propertyNameIndex); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::CallProperty call; call.base = base.propertyBase.stackSlot(); call.name = base.propertyNameIndex; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else if (base.type == Reference::Subscript) { Instruction::CallElement call; @@ -1946,33 +1947,33 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.index = base.elementSubscript.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else if (base.type == Reference::Name) { if (base.name == QStringLiteral("eval")) { Instruction::CallPossiblyDirectEval call; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else if (!disable_lookups && useFastLookups && base.global) { if (base.qmlGlobal) { Instruction::CallQmlContextPropertyLookup call; call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Instruction::CallGlobalLookup call; call.index = registerGlobalGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else { Instruction::CallName call; call.name = base.nameAsIndex(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } } else if (base.type == Reference::SuperProperty) { Reference receiver = base.baseObject(); @@ -1989,14 +1990,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.thisObject = receiver.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } else { Q_ASSERT(base.isStackSlot()); Instruction::CallValue call; call.name = base.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addTracingInstruction(call); + bytecodeGenerator->addInstruction(call); } setExprResult(Reference::fromAccumulator(this)); @@ -2732,14 +2733,14 @@ bool Codegen::visit(TemplateLiteral *ast) Instruction::Add instr; instr.lhs = temp2; - bytecodeGenerator->addTracingInstruction(instr); + bytecodeGenerator->addInstruction(instr); } else { expr.loadInAccumulator(); } Instruction::Add instr; instr.lhs = temp; - bytecodeGenerator->addTracingInstruction(instr); + bytecodeGenerator->addInstruction(instr); } auto r = Reference::fromAccumulator(this); @@ -2997,7 +2998,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bool savedFunctionEndsWithReturn = functionEndsWithReturn; functionEndsWithReturn = endsWithReturn(_module, body); - bytecodeGenerator->setTracing(_functionContext->canUseTracingJit(), _context->arguments.size()); // reserve the js stack frame (Context & js Function & accumulator) bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size()); @@ -3084,7 +3084,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, Q_ASSERT(_context == _functionContext); bytecodeGenerator->finalize(_context); _context->registerCountInFunction = bytecodeGenerator->registerCount(); - _context->nTraceInfos = bytecodeGenerator->traceInfoCount(); static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); if (showCode) { qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict @@ -3203,11 +3202,13 @@ bool Codegen::visit(DoWhileStatement *ast) cond.link(); if (AST::cast<TrueLiteral *>(ast->expression)) { // do {} while (true) -> just jump back to the loop body, no need to generate a condition + bytecodeGenerator->checkException(); bytecodeGenerator->jump().link(body); } else if (AST::cast<FalseLiteral *>(ast->expression)) { // do {} while (false) -> fall through, no need to generate a condition } else { TailCallBlocker blockTailCalls(this); + bytecodeGenerator->checkException(); condition(ast->expression, &body, &end, false); } @@ -3288,7 +3289,7 @@ bool Codegen::visit(ForEachStatement *ast) next.value = lhsValue.stackSlot(); next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end); + bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end); // each iteration gets it's own context, as per spec { @@ -3324,6 +3325,7 @@ bool Codegen::visit(ForEachStatement *ast) setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); } + bytecodeGenerator->checkException(); bytecodeGenerator->jump().link(in); error: @@ -3372,6 +3374,7 @@ bool Codegen::visit(ForStatement *ast) bytecodeGenerator->addInstruction(clone); } statement(ast->expression); + bytecodeGenerator->checkException(); bytecodeGenerator->jump().link(cond); end.link(); @@ -3654,6 +3657,8 @@ bool Codegen::visit(WhileStatement *ast) ControlFlowLoop flow(this, &end, &cond); bytecodeGenerator->addLoopStart(cond); + bytecodeGenerator->checkException(); + if (!AST::cast<TrueLiteral *>(ast->expression)) { TailCallBlocker blockTailCalls(this); condition(ast->expression, &start, &end, true); @@ -4225,7 +4230,7 @@ void Codegen::Reference::storeAccumulator() const Instruction::StoreElement store; store.base = elementBase; store.index = elementSubscript.stackSlot(); - codegen->bytecodeGenerator->addTracingInstruction(store); + codegen->bytecodeGenerator->addInstruction(store); } return; case Invalid: case Accumulator: @@ -4315,12 +4320,12 @@ QT_WARNING_POP if (!scope) { Instruction::LoadLocal load; load.index = index; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadScopedLocal load; load.index = index; load.scope = scope; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } tdzCheck(requiresTDZCheck); return; @@ -4344,16 +4349,16 @@ QT_WARNING_POP if (qmlGlobal) { Instruction::LoadQmlContextPropertyLookup load; load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadGlobalLookup load; load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } } else { Instruction::LoadName load; load.name = nameAsIndex(); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Member: @@ -4362,11 +4367,11 @@ QT_WARNING_POP if (!disable_lookups && codegen->useFastLookups) { Instruction::GetLookup load; load.index = codegen->registerGetterLookup(propertyNameIndex); - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } else { Instruction::LoadProperty load; load.name = propertyNameIndex; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Import: { @@ -4381,7 +4386,7 @@ QT_WARNING_POP tdzCheck(subscriptRequiresTDZCheck); Instruction::LoadElement load; load.base = elementBase; - codegen->bytecodeGenerator->addTracingInstruction(load); + codegen->bytecodeGenerator->addInstruction(load); } return; case Invalid: break; diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 4cfd2d86e8..dd7ba471c3 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -79,7 +79,7 @@ QT_BEGIN_NAMESPACE // Also change the comment behind the number to describe the latest change. This has the added // benefit that if another patch changes the version too, it will result in a merge conflict, and // not get removed silently. -#define QV4_DATA_STRUCTURE_VERSION 0x22 // Add trace slot to UPlus +#define QV4_DATA_STRUCTURE_VERSION 0x23 // Remove trace slots class QIODevice; class QQmlPropertyData; @@ -296,10 +296,6 @@ struct Function quint32_le nLabelInfos; size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } - typedef quint16_le TraceInfoCount; - TraceInfoCount nTraceInfos; - static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); } - // Keep all unaligned data at the end quint8 flags; quint8 padding1; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 01c033cb2a..123d77f788 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -424,7 +424,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte Q_ASSERT(function->lineNumberOffset() == currentOffset); currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); - function->nTraceInfos = irFunction->nTraceInfos; function->nRegisters = irFunction->registerCountInFunction; if (!irFunction->labelInfo.empty()) { diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 52215c2ce6..d1a5fee92b 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -410,26 +410,4 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator) nRegisters = bytecodeGenerator->currentRegister() - registerOffset; } -bool Context::canUseTracingJit() const -{ -#if QT_CONFIG(qml_tracing) - static bool forceTracing = !qEnvironmentVariableIsEmpty("QV4_FORCE_TRACING"); - if (forceTracing) //### we can probably remove this when tracing is turned on by default - return true; // to be used by unittests - - static bool disableTracing = !qEnvironmentVariableIsEmpty("QV4_DISABLE_TRACING"); - if (disableTracing) - return false; - - static QStringList onlyTrace = - qEnvironmentVariable("QV4_ONLY_TRACE").split(QLatin1Char(','), QString::SkipEmptyParts); - if (!onlyTrace.isEmpty()) - return onlyTrace.contains(name); - - return true; -#else - return false; -#endif -} - QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 57ef4be36e..f56942fffa 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -162,7 +162,6 @@ struct Context { int line = 0; int column = 0; int registerCountInFunction = 0; - uint nTraceInfos = 0; int functionIndex = -1; int blockIndex = -1; @@ -363,8 +362,6 @@ struct Context { return parent->canHaveTailCalls(); return false; } - - bool canUseTracingJit() const; }; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index b019f191fa..5148154a6a 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -171,8 +171,6 @@ QString dumpArguments(int argc, int argv, int nFormals) return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")"); } -#define TRACE_SLOT QStringLiteral(" {%1}").arg(traceSlot) - void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping) { MOTH_JUMP_TABLE; @@ -241,9 +239,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadLocal) if (index < nLocals) - d << "l" << index << TRACE_SLOT; + d << "l" << index; else - d << "a" << (index - nLocals) << TRACE_SLOT; + d << "a" << (index - nLocals); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -255,9 +253,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadScopedLocal) if (index < nLocals) - d << "l" << index << "@" << scope << TRACE_SLOT; + d << "l" << index << "@" << scope; else - d << "a" << (index - nLocals) << "@" << scope << TRACE_SLOT; + d << "a" << (index - nLocals) << "@" << scope; MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -280,15 +278,15 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - d << name << TRACE_SLOT; + d << name; MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) - d << index << TRACE_SLOT; + d << index; MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) - d << index << TRACE_SLOT; + d << index; MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameSloppy) @@ -300,20 +298,19 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(StoreNameStrict) MOTH_BEGIN_INSTR(LoadElement) - d << dumpRegister(base, nFormals) << "[acc]" << TRACE_SLOT; + d << dumpRegister(base, nFormals) << "[acc]"; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" - << TRACE_SLOT; + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - d << "acc[" << name << "]" << TRACE_SLOT; + d << "acc[" << name << "]"; MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) - d << "acc(" << index << ")" << TRACE_SLOT; + d << "acc(" << index << ")"; MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) @@ -343,49 +340,48 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Resume) MOTH_BEGIN_INSTR(CallValue) - d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) - << TRACE_SLOT; + ; MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) d << dumpRegister(base, nFormals) << "." << lookupIndex - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" - << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) - d << name << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << name << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) - d << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) - d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) - d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; + d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) - << dumpArguments(argc, argv, nFormals) - << TRACE_SLOT; + << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(Construct) @@ -528,11 +524,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(JumpTrue) - d << ABSOLUTE_OFFSET() << TRACE_SLOT; + d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpTrue) MOTH_BEGIN_INSTR(JumpFalse) - d << ABSOLUTE_OFFSET() << TRACE_SLOT; + d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpFalse) MOTH_BEGIN_INSTR(JumpNotUndefined) @@ -543,6 +539,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << ABSOLUTE_OFFSET(); MOTH_END_INSTR(JumpNoException) + MOTH_BEGIN_INSTR(CheckException) + MOTH_END_INSTR(CheckException) + MOTH_BEGIN_INSTR(CmpEqNull) MOTH_END_INSTR(CmpEqNull) @@ -593,26 +592,22 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(UNot) MOTH_BEGIN_INSTR(UPlus) - d << TRACE_SLOT; MOTH_END_INSTR(UPlus) MOTH_BEGIN_INSTR(UMinus) - d << TRACE_SLOT; MOTH_END_INSTR(UMinus) MOTH_BEGIN_INSTR(UCompl) MOTH_END_INSTR(UCompl) MOTH_BEGIN_INSTR(Increment) - d << TRACE_SLOT; MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) - d << TRACE_SLOT; MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(BitAnd) @@ -668,7 +663,7 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Exp) MOTH_BEGIN_INSTR(Mul) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Div) @@ -676,11 +671,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Div) MOTH_BEGIN_INSTR(Mod) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(Sub) - d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; + d << dumpRegister(lhs, nFormals) << ", acc"; MOTH_END_INSTR(Sub) MOTH_BEGIN_INSTR(CmpIn) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 6a8c9a9549..35a5fdfba5 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -77,20 +77,20 @@ QT_BEGIN_NAMESPACE #define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg) #define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg) #define INSTR_LoadImport(op) INSTRUCTION(op, LoadImport, 1, index) -#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 2, index, traceSlot) +#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index) #define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index) -#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 3, scope, index, traceSlot) +#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index) #define INSTR_StoreScopedLocal(op) INSTRUCTION(op, StoreScopedLocal, 2, scope, index) #define INSTR_LoadRuntimeString(op) INSTRUCTION(op, LoadRuntimeString, 1, stringId) #define INSTR_MoveRegExp(op) INSTRUCTION(op, MoveRegExp, 2, regExpId, destReg) #define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value) -#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot) -#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot) -#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 2, index, traceSlot) +#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 1, name) +#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index) +#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 1, index) #define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) #define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) -#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, traceSlot) -#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot) +#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name) +#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index) #define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) #define INSTR_Yield(op) INSTRUCTION(op, Yield, 0) #define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0) @@ -100,18 +100,18 @@ QT_BEGIN_NAMESPACE #define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) #define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property) #define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property) -#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, traceSlot) -#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 3, base, index, traceSlot) -#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 4, name, argc, argv, traceSlot) -#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 5, name, thisObject, argc, argv, traceSlot) -#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 5, name, base, argc, argv, traceSlot) -#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 5, lookupIndex, base, argc, argv, traceSlot) -#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 5, base, index, argc, argv, traceSlot) -#define INSTR_CallName(op) INSTRUCTION(op, CallName, 4, name, argc, argv, traceSlot) -#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 3, argc, argv, traceSlot) -#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 4, index, argc, argv, traceSlot) -#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 4, index, argc, argv, traceSlot) -#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot) +#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base) +#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index) +#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv) +#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 4, name, thisObject, argc, argv) +#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv) +#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv) +#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv) +#define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv) +#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) +#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) +#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 3, index, argc, argv) +#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv) #define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) #define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv) #define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset) @@ -148,10 +148,11 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0) #define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0) #define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset) -#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 2, traceSlot, offset) -#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 2, traceSlot, offset) +#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset) +#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset) #define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset) #define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset) +#define INSTR_CheckException(op) INSTRUCTION(op, CheckException, 0) #define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0) #define INSTR_CmpNeNull(op) INSTRUCTION(op, CmpNeNull, 0) #define INSTR_CmpEqInt(op) INSTRUCTION(op, CmpEqInt, 1, lhs) @@ -167,12 +168,12 @@ QT_BEGIN_NAMESPACE #define INSTR_CmpIn(op) INSTRUCTION(op, CmpIn, 1, lhs) #define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs) #define INSTR_UNot(op) INSTRUCTION(op, UNot, 0) -#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 1, traceSlot) -#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 1, traceSlot) +#define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0) +#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0) #define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0) -#define INSTR_Increment(op) INSTRUCTION(op, Increment, 1, traceSlot) -#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 1, traceSlot) -#define INSTR_Add(op) INSTRUCTION(op, Add, 2, lhs, traceSlot) +#define INSTR_Increment(op) INSTRUCTION(op, Increment, 0) +#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 0) +#define INSTR_Add(op) INSTRUCTION(op, Add, 1, lhs) #define INSTR_BitAnd(op) INSTRUCTION(op, BitAnd, 1, lhs) #define INSTR_BitOr(op) INSTRUCTION(op, BitOr, 1, lhs) #define INSTR_BitXor(op) INSTRUCTION(op, BitXor, 1, lhs) @@ -186,10 +187,10 @@ QT_BEGIN_NAMESPACE #define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs) #define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs) #define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs) -#define INSTR_Mul(op) INSTRUCTION(op, Mul, 2, lhs, traceSlot) +#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs) #define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) -#define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot) -#define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot) +#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) +#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) #define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count) #define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0) @@ -241,6 +242,7 @@ QT_BEGIN_NAMESPACE F(JumpFalse) \ F(JumpNoException) \ F(JumpNotUndefined) \ + F(CheckException) \ F(CmpEqNull) \ F(CmpNeNull) \ F(CmpEqInt) \ diff --git a/src/qml/configure.json b/src/qml/configure.json index c35f5be06b..2f88aef1fb 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -8,7 +8,6 @@ "commandline": { "options": { "qml-network": "boolean", - "qml-tracing": "boolean", "qml-debug": "boolean" } }, @@ -24,6 +23,52 @@ ], "qmake": "CONFIG += c++11" } + }, + "pointer_32bit": { + "label": "32bit pointers", + "type": "compile", + "test": { + "main": "static_assert(sizeof(void *) == 4, \"fail\");" + } + }, + "pointer_64bit": { + "label": "64bit pointers", + "type": "compile", + "test": { + "main": "static_assert(sizeof(void *) == 8, \"fail\");" + } + }, + "arm_thumb": { + "label": "THUMB mode on ARM", + "type": "compile", + "test": { + "main": [ + "#if defined(thumb2) || defined(__thumb2__)", + "# define THUMB_OK", + "#elif (defined(__thumb) || defined(__thumb__)) && __TARGET_ARCH_THUMB-0 == 4", + "# define THUMB_OK", + "#elif defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB == 2", + "// clang 3.5 and later will set this if the core supports the Thumb-2 ISA.", + "# define THUMB_OK", + "#else", + "# error \"fail\"", + "#endif" + ] + } + }, + "arm_fp": { + "label": "Sufficiently recent FPU on ARM", + "type": "compile", + "test": { + "main": [ + "// if !defined(__ARM_FP) we might be on MSVC or we might have a device", + "// without an FPU.", + "// TODO: The latter case is not supported, but the test still succeeds.", + "#if defined(__ARM_FP) && (__ARM_FP <= 0x04)", + "# error \"fail\"", + "#endif" + ] + } } }, @@ -40,12 +85,26 @@ "condition": "features.network", "output": [ "publicFeature" ] }, - "qml-tracing": { - "label": "QML tracing JIT support", - "purpose": "Provides a JIT that uses trace information generated by the interpreter.", + "qml-jit": { + "label": "QML just-in-time compiler", + "purpose": "Provides a JIT for QML and JavaScript", "section": "QML", + "condition": [ + " (arch.i386 && tests.pointer_32bit) + || (arch.x86_64 && tests.pointer_64bit) + || (arch.arm && tests.pointer_32bit && tests.arm_fp && tests.arm_thumb + && (config.linux || config.ios || config.tvos || config.qnx)) + || (arch.arm64 && tests.pointer_64bit && tests.arm_fp + && (config.linux || config.ios || config.tvos || config.qnx || config.integrity))" + ], "output": [ "privateFeature" ], - "autoDetect": false + "autoDetect": "!config.ios && !config.tvos", + "comment": "On arm and arm64 we need a specialization of cacheFlush() for each OS to be + enabeled. Therefore the config white list. + Also Mind that e.g. x86_32 has arch.x86_64 but 32bit pointers. Therefore + the checks for architecture and pointer size. + Finally, ios and tvos can technically use the JIT but Apple does not allow + it. Therefore, it's disabled by default." }, "qml-debug": { "label": "QML debugging and profiling support", @@ -90,12 +149,6 @@ "section": "QML", "output": [ "privateFeature" ] }, - "qml-list-model": { - "label": "QML list model", - "purpose": "Provides the ListModel QML type.", - "section": "QML", - "output": [ "privateFeature" ] - }, "qml-xml-http-request": { "label": "QML XML http request", "purpose": "Provides support for sending XML http requests.", @@ -119,12 +172,6 @@ "condition": "features.animation", "output": [ "privateFeature" ] }, - "qml-delegate-model": { - "label": "QML delegate model", - "purpose": "Provides the DelegateModel QML type.", - "section": "QML", - "output": [ "privateFeature" ] - }, "qml-worker-script": { "label": "QML WorkerScript", "purpose": "Enables the use of threads in QML.", @@ -140,12 +187,10 @@ "entries": [ "qml-network", "qml-debug", - "qml-tracing", + "qml-jit", "qml-sequence-object", - "qml-list-model", "qml-xml-http-request", - "qml-locale", - "qml-delegate-model" + "qml-locale" ] } ] diff --git a/src/qml/jit/jit.pri b/src/qml/jit/jit.pri index bc0d20dba7..503ce0ebcd 100644 --- a/src/qml/jit/jit.pri +++ b/src/qml/jit/jit.pri @@ -10,34 +10,3 @@ HEADERS += \ $$PWD/qv4baselinejit_p.h \ $$PWD/qv4baselineassembler_p.h \ $$PWD/qv4assemblercommon_p.h - -qtConfig(qml-tracing) { -SOURCES += \ - $$PWD/qv4ir.cpp \ - $$PWD/qv4operation.cpp \ - $$PWD/qv4node.cpp \ - $$PWD/qv4graph.cpp \ - $$PWD/qv4graphbuilder.cpp \ - $$PWD/qv4lowering.cpp \ - $$PWD/qv4tracingjit.cpp \ - $$PWD/qv4mi.cpp \ - $$PWD/qv4domtree.cpp \ - $$PWD/qv4schedulers.cpp \ - $$PWD/qv4blockscheduler.cpp \ - $$PWD/qv4loopinfo.cpp - -HEADERS += \ - $$PWD/qv4ir_p.h \ - $$PWD/qv4operation_p.h \ - $$PWD/qv4runtimesupport_p.h \ - $$PWD/qv4node_p.h \ - $$PWD/qv4graph_p.h \ - $$PWD/qv4graphbuilder_p.h \ - $$PWD/qv4lowering_p.h \ - $$PWD/qv4mi_p.h \ - $$PWD/qv4miblockset_p.h \ - $$PWD/qv4domtree_p.h \ - $$PWD/qv4schedulers_p.h \ - $$PWD/qv4blockscheduler_p.h \ - $$PWD/qv4loopinfo_p.h -} diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp index dd810d9d70..800ee22cd7 100644 --- a/src/qml/jit/qv4assemblercommon.cpp +++ b/src/qml/jit/qv4assemblercommon.cpp @@ -53,8 +53,6 @@ #undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES -#ifdef V4_ENABLE_JIT - QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { @@ -366,5 +364,3 @@ void PlatformAssemblerCommon::storeInt32AsValue(int srcInt, Address destAddr) } // QV4 namepsace QT_END_NAMESPACE - -#endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4assemblercommon_p.h b/src/qml/jit/qv4assemblercommon_p.h index d3d7eedae2..f305213ce2 100644 --- a/src/qml/jit/qv4assemblercommon_p.h +++ b/src/qml/jit/qv4assemblercommon_p.h @@ -58,7 +58,7 @@ #include <wtf/Vector.h> #include <assembler/MacroAssembler.h> -#ifdef V4_ENABLE_JIT +QT_REQUIRE_CONFIG(qml_jit); QT_BEGIN_NAMESPACE @@ -619,6 +619,9 @@ public: for (Jump j : catchyJumps) j.link(this); + // We don't need to check for isInterrupted here because if that is set, + // then the first checkException() in any exception handler will find another "exception" + // and jump out of the exception handler. loadPtr(exceptionHandlerAddress(), ScratchRegister); Jump exitFunction = branchPtr(Equal, ScratchRegister, TrustedImmPtr(0)); jump(ScratchRegister); @@ -633,6 +636,8 @@ public: void checkException() { + // This actually reads 4 bytes, starting at hasException. + // Therefore, it also reads the isInterrupted flag, and triggers an exception on that. addCatchyJump( branch32(NotEqual, Address(EngineRegister, offsetof(EngineBase, hasException)), @@ -735,6 +740,4 @@ private: QT_END_NAMESPACE -#endif // V4_ENABLE_JIT - #endif // QV4PLATFORMASSEMBLER_P_H diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp index 238c11f478..5e34087ff5 100644 --- a/src/qml/jit/qv4baselineassembler.cpp +++ b/src/qml/jit/qv4baselineassembler.cpp @@ -55,8 +55,6 @@ #undef ENABLE_ALL_ASSEMBLERS_FOR_REFACTORING_PURPOSES -#ifdef V4_ENABLE_JIT - QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { @@ -1620,5 +1618,3 @@ void BaselineAssembler::ret() } // QV4 namepsace QT_END_NAMESPACE - -#endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4baselineassembler_p.h b/src/qml/jit/qv4baselineassembler_p.h index 3bbaefd000..5e5d9d0672 100644 --- a/src/qml/jit/qv4baselineassembler_p.h +++ b/src/qml/jit/qv4baselineassembler_p.h @@ -55,6 +55,8 @@ #include <private/qv4function_p.h> #include <QHash> +QT_REQUIRE_CONFIG(qml_jit); + QT_BEGIN_NAMESPACE namespace QV4 { diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 80155d7b20..f4807f1917 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -42,8 +42,6 @@ #include <private/qv4lookup_p.h> #include <private/qv4generatorobject_p.h> -#ifdef V4_ENABLE_JIT - QT_USE_NAMESPACE using namespace QV4; using namespace QV4::JIT; @@ -151,7 +149,7 @@ void BaselineJIT::generate_LoadImport(int index) as->loadImport(index); } -void BaselineJIT::generate_LoadLocal(int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadLocal(int index) { as->loadLocal(index); } @@ -162,7 +160,7 @@ void BaselineJIT::generate_StoreLocal(int index) as->storeLocal(index); } -void BaselineJIT::generate_LoadScopedLocal(int scope, int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadScopedLocal(int scope, int index) { as->loadLocal(index, scope); } @@ -195,7 +193,7 @@ void BaselineJIT::generate_LoadClosure(int value) BASELINEJIT_GENERATE_RUNTIME_CALL(Closure, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadName(int name, int /*traceSlot*/) +void BaselineJIT::generate_LoadName(int name) { STORE_IP(); as->prepareCallWithArgCount(2); @@ -204,7 +202,7 @@ void BaselineJIT::generate_LoadName(int name, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(LoadName, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadGlobalLookup(int index) { as->prepareCallWithArgCount(3); as->passInt32AsArg(index, 2); @@ -213,7 +211,7 @@ void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(LoadGlobalLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index, int /*traceSlot*/) +void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index) { as->prepareCallWithArgCount(2); as->passInt32AsArg(index, 1); @@ -243,7 +241,7 @@ void BaselineJIT::generate_StoreNameStrict(int name) BASELINEJIT_GENERATE_RUNTIME_CALL(StoreNameStrict, CallResultDestination::Ignore); } -void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/) +void BaselineJIT::generate_LoadElement(int base) { STORE_IP(); STORE_ACC(); @@ -254,7 +252,7 @@ void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(LoadElement, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/) +void BaselineJIT::generate_StoreElement(int base, int index) { STORE_IP(); STORE_ACC(); @@ -266,7 +264,7 @@ void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(StoreElement, CallResultDestination::Ignore); } -void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/) +void BaselineJIT::generate_LoadProperty(int name) { STORE_IP(); STORE_ACC(); @@ -277,7 +275,7 @@ void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/) BASELINEJIT_GENERATE_RUNTIME_CALL(LoadProperty, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_GetLookup(int index, int /*traceSlot*/) +void BaselineJIT::generate_GetLookup(int index) { STORE_IP(); STORE_ACC(); @@ -355,7 +353,7 @@ void BaselineJIT::generate_Resume(int) Q_UNREACHABLE(); } -void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallValue(int name, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -366,7 +364,7 @@ void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSl BASELINEJIT_GENERATE_RUNTIME_CALL(CallValue, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -378,7 +376,7 @@ void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, BASELINEJIT_GENERATE_RUNTIME_CALL(CallWithReceiver, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -390,7 +388,7 @@ void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv, BASELINEJIT_GENERATE_RUNTIME_CALL(CallProperty, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -402,7 +400,7 @@ void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int arg BASELINEJIT_GENERATE_RUNTIME_CALL(CallPropertyLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -414,7 +412,7 @@ void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv, BASELINEJIT_GENERATE_RUNTIME_CALL(CallElement, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallName(int name, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -425,7 +423,7 @@ void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlo BASELINEJIT_GENERATE_RUNTIME_CALL(CallName, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(3); @@ -435,7 +433,7 @@ void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv, int /*trac BASELINEJIT_GENERATE_RUNTIME_CALL(CallPossiblyDirectEval, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -446,8 +444,7 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int / BASELINEJIT_GENERATE_RUNTIME_CALL(CallGlobalLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv, - int /*traceSlot*/) +void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -458,7 +455,7 @@ void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int BASELINEJIT_GENERATE_RUNTIME_CALL(CallQmlContextPropertyLookup, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/) +void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -777,12 +774,12 @@ void BaselineJIT::generate_Jump(int offset) labels.insert(as->jump(absoluteOffset(offset))); } -void BaselineJIT::generate_JumpTrue(int /*traceSlot*/, int offset) +void BaselineJIT::generate_JumpTrue(int offset) { labels.insert(as->jumpTrue(absoluteOffset(offset))); } -void BaselineJIT::generate_JumpFalse(int /*traceSlot*/, int offset) +void BaselineJIT::generate_JumpFalse(int offset) { labels.insert(as->jumpFalse(absoluteOffset(offset))); } @@ -797,6 +794,11 @@ void BaselineJIT::generate_JumpNotUndefined(int offset) labels.insert(as->jumpNotUndefined(absoluteOffset(offset))); } +void BaselineJIT::generate_CheckException() +{ + as->checkException(); +} + void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); } void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); } void BaselineJIT::generate_CmpEqInt(int lhs) { as->cmpeqInt(lhs); } @@ -831,12 +833,12 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs) } void BaselineJIT::generate_UNot() { as->unot(); } -void BaselineJIT::generate_UPlus(int /*traceSlot*/) { as->toNumber(); } -void BaselineJIT::generate_UMinus(int /*traceSlot*/) { as->uminus(); } +void BaselineJIT::generate_UPlus() { as->toNumber(); } +void BaselineJIT::generate_UMinus() { as->uminus(); } void BaselineJIT::generate_UCompl() { as->ucompl(); } -void BaselineJIT::generate_Increment(int /*traceSlot*/) { as->inc(); } -void BaselineJIT::generate_Decrement(int /*traceSlot*/) { as->dec(); } -void BaselineJIT::generate_Add(int lhs, int /*traceSlot*/) { as->add(lhs); } +void BaselineJIT::generate_Increment() { as->inc(); } +void BaselineJIT::generate_Decrement() { as->dec(); } +void BaselineJIT::generate_Add(int lhs) { as->add(lhs); } void BaselineJIT::generate_BitAnd(int lhs) { as->bitAnd(lhs); } void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); } @@ -860,10 +862,10 @@ void BaselineJIT::generate_Exp(int lhs) { as->passJSSlotAsArg(lhs, 0); BASELINEJIT_GENERATE_RUNTIME_CALL(Exp, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_Mul(int lhs, int /*traceSlot*/) { as->mul(lhs); } +void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); } void BaselineJIT::generate_Div(int lhs) { as->div(lhs); } -void BaselineJIT::generate_Mod(int lhs, int /*traceSlot*/) { as->mod(lhs); } -void BaselineJIT::generate_Sub(int lhs, int /*traceSlot*/) { as->sub(lhs); } +void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); } +void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); } //void BaselineJIT::generate_BinopContext(int alu, int lhs) //{ @@ -913,5 +915,3 @@ void BaselineJIT::endInstruction(Instr::Type instr) { Q_UNUSED(instr); } - -#endif // V4_ENABLE_JIT diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index 37ab37eac2..284faf0ff0 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -56,7 +56,7 @@ #include <private/qv4instr_moth_p.h> #include <private/qv4bytecodehandler_p.h> -//QT_REQUIRE_CONFIG(qml_jit); +QT_REQUIRE_CONFIG(qml_jit); QT_BEGIN_NAMESPACE @@ -65,7 +65,6 @@ namespace JIT { class BaselineAssembler; -#ifdef V4_ENABLE_JIT class BaselineJIT final: public Moth::ByteCodeHandler { public: @@ -88,22 +87,22 @@ public: void generate_StoreReg(int reg) override; void generate_MoveReg(int srcReg, int destReg) override; void generate_LoadImport(int index) override; - void generate_LoadLocal(int index, int traceSlot) override; + void generate_LoadLocal(int index) override; void generate_StoreLocal(int index) override; - void generate_LoadScopedLocal(int scope, int index, int traceSlot) override; + void generate_LoadScopedLocal(int scope, int index) override; void generate_StoreScopedLocal(int scope, int index) override; void generate_LoadRuntimeString(int stringId) override; void generate_MoveRegExp(int regExpId, int destReg) override; void generate_LoadClosure(int value) override; - void generate_LoadName(int name, int traceSlot) override; - void generate_LoadGlobalLookup(int index, int traceSlot) override; - void generate_LoadQmlContextPropertyLookup(int index, int traceSlot) override; + void generate_LoadName(int name) override; + void generate_LoadGlobalLookup(int index) override; + void generate_LoadQmlContextPropertyLookup(int index) override; void generate_StoreNameSloppy(int name) override; void generate_StoreNameStrict(int name) override; - void generate_LoadElement(int base, int traceSlot) override; - void generate_StoreElement(int base, int index, int traceSlot) override; - void generate_LoadProperty(int name, int traceSlot) override; - void generate_GetLookup(int index, int traceSlot) override; + void generate_LoadElement(int base) override; + void generate_StoreElement(int base, int index) override; + void generate_LoadProperty(int name) override; + void generate_GetLookup(int index) override; void generate_StoreProperty(int name, int base) override; void generate_SetLookup(int index, int base) override; void generate_LoadSuperProperty(int property) override; @@ -112,16 +111,16 @@ public: void generate_YieldStar() override; void generate_Resume(int) override; - void generate_CallValue(int name, int argc, int argv, int traceSlot) override; - void generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int traceSlot) override; - void generate_CallProperty(int name, int base, int argc, int argv, int traceSlot) override; - void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int traceSlot) override; - void generate_CallElement(int base, int index, int argc, int argv, int traceSlot) override; - void generate_CallName(int name, int argc, int argv, int traceSlot) override; - void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override; - void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override; - void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override; - void generate_CallWithSpread(int func, int thisObject, int argc, int argv, int traceSlot) override; + void generate_CallValue(int name, int argc, int argv) override; + void generate_CallWithReceiver(int name, int thisObject, int argc, int argv) override; + void generate_CallProperty(int name, int base, int argc, int argv) override; + void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) override; + void generate_CallElement(int base, int index, int argc, int argv) override; + void generate_CallName(int name, int argc, int argv) override; + void generate_CallPossiblyDirectEval(int argc, int argv) override; + void generate_CallGlobalLookup(int index, int argc, int argv) override; + void generate_CallQmlContextPropertyLookup(int index, int argc, int argv) override; + void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override; void generate_TailCall(int func, int thisObject, int argc, int argv) override; void generate_Construct(int func, int argc, int argv) override; void generate_ConstructWithSpread(int func, int argc, int argv) override; @@ -160,10 +159,11 @@ public: void generate_LoadSuperConstructor() override; void generate_ToObject() override; void generate_Jump(int offset) override; - void generate_JumpTrue(int traceSlot, int offset) override; - void generate_JumpFalse(int traceSlot, int offset) override; + void generate_JumpTrue(int offset) override; + void generate_JumpFalse(int offset) override; void generate_JumpNoException(int offset) override; void generate_JumpNotUndefined(int offset) override; + void generate_CheckException() override; void generate_CmpEqNull() override; void generate_CmpNeNull() override; void generate_CmpEqInt(int lhs) override; @@ -179,12 +179,12 @@ public: void generate_CmpIn(int lhs) override; void generate_CmpInstanceOf(int lhs) override; void generate_UNot() override; - void generate_UPlus(int) override; - void generate_UMinus(int traceSlot) override; + void generate_UPlus() override; + void generate_UMinus() override; void generate_UCompl() override; - void generate_Increment(int traceSlot) override; - void generate_Decrement(int traceSlot) override; - void generate_Add(int lhs, int traceSlot) override; + void generate_Increment() override; + void generate_Decrement() override; + void generate_Add(int lhs) override; void generate_BitAnd(int lhs) override; void generate_BitOr(int lhs) override; void generate_BitXor(int lhs) override; @@ -198,10 +198,10 @@ public: void generate_ShrConst(int rhs) override; void generate_ShlConst(int rhs) override; void generate_Exp(int lhs) override; - void generate_Mul(int lhs, int traceSlot) override; + void generate_Mul(int lhs) override; void generate_Div(int lhs) override; - void generate_Mod(int lhs, int traceSlot) override; - void generate_Sub(int lhs, int traceSlot) override; + void generate_Mod(int lhs) override; + void generate_Sub(int lhs) override; void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; void generate_ThrowOnNullOrUndefined() override; void generate_GetTemplateObject(int index) override; @@ -214,7 +214,6 @@ private: QScopedPointer<BaselineAssembler> as; QSet<int> labels; }; -#endif // V4_ENABLE_JIT } // namespace JIT } // namespace QV4 diff --git a/src/qml/jit/qv4blockscheduler.cpp b/src/qml/jit/qv4blockscheduler.cpp deleted file mode 100644 index 3e2bfe15c5..0000000000 --- a/src/qml/jit/qv4blockscheduler.cpp +++ /dev/null @@ -1,208 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> - -#include "qv4blockscheduler_p.h" -#include "qv4domtree_p.h" -#include "qv4loopinfo_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcBlockScheduler, "qt.v4.ir.blockscheduler") - -bool BlockScheduler::checkCandidate(MIBlock *candidate) -{ - Q_ASSERT(loopInfo.loopHeaderFor(candidate) == currentGroup.group); - - for (MIBlock *pred : candidate->inEdges()) { - if (pred->isDeoptBlock()) - continue; - - if (emitted.alreadyProcessed(pred)) - continue; - - if (dominatorTree.dominates(candidate->index(), pred->index())) { - // this is a loop, where there in - // -> candidate edge is the jump back to the top of the loop. - continue; - } - - if (pred == candidate) - // this is a very tight loop, e.g.: - // L1: ... - // goto L1 - // This can happen when, for example, the basic-block merging gets rid of the empty - // body block. In this case, we can safely schedule this block (if all other - // incoming edges are either loop-back edges, or have been scheduled already). - continue; - - return false; // an incoming edge that is not yet emitted, and is not a back-edge - } - - if (loopInfo.isLoopHeader(candidate)) { - // postpone everything, and schedule the loop first. - postponedGroups.push(currentGroup); - currentGroup = WorkForGroup(candidate); - } - - return true; -} - -MIBlock *BlockScheduler::pickNext() -{ - while (true) { - while (currentGroup.postponed.isEmpty()) { - if (postponedGroups.isEmpty()) - return nullptr; - if (currentGroup.group) // record the first and the last node of a group - loopsStartEnd[currentGroup.group] = sequence.back(); - currentGroup = postponedGroups.pop(); - } - - MIBlock *next = currentGroup.postponed.pop(); - if (checkCandidate(next)) - return next; - } - - Q_UNREACHABLE(); - return nullptr; -} - -void BlockScheduler::emitBlock(MIBlock *bb) -{ - if (emitted.alreadyProcessed(bb)) - return; - - sequence.push_back(bb); - emitted.markAsProcessed(bb); -} - -void BlockScheduler::schedule(MIBlock *functionEntryPoint) -{ - MIBlock *next = functionEntryPoint; - - while (next) { - emitBlock(next); - // postpone all outgoing edges, if they were not already processed - QVarLengthArray<MIBlock *, 32> nonExceptionEdges; - // first postpone all exception edges, so they will be processed last - for (int i = next->outEdges().size(); i != 0; ) { - --i; - MIBlock *out = next->outEdges().at(i); - if (emitted.alreadyProcessed(out)) - continue; - if (out == nullptr) - continue; - if (out->instructions().front().opcode() == Meta::OnException) - postpone(out); - else - nonExceptionEdges.append(out); - } - for (MIBlock *edge : nonExceptionEdges) - postpone(edge); - next = pickNext(); - } - - // finally schedule all de-optimization blocks at the end - for (auto bb : dominatorTree.function()->blocks()) { - if (bb->isDeoptBlock()) - emitBlock(bb); - } -} - -void BlockScheduler::postpone(MIBlock *bb) -{ - if (currentGroup.group == loopInfo.loopHeaderFor(bb)) { - currentGroup.postponed.append(bb); - return; - } - - for (int i = postponedGroups.size(); i != 0; ) { - --i; - WorkForGroup &g = postponedGroups[i]; - if (g.group == loopInfo.loopHeaderFor(bb)) { - g.postponed.append(bb); - return; - } - } - - Q_UNREACHABLE(); -} - -void BlockScheduler::dump() const -{ - if (!lcBlockScheduler().isDebugEnabled()) - return; - - QString s = QStringLiteral("Scheduled blocks:\n"); - for (auto *bb : sequence) { - s += QLatin1String(" L") + QString::number(bb->index()); - MIBlock *loopEnd = loopsStartEnd[bb]; - if (loopEnd) - s += QLatin1String(", loop start, ends at L") + QString::number(loopEnd->index()); - s += QLatin1Char('\n'); - } - qCDebug(lcBlockScheduler).noquote().nospace() << s; -} - -BlockScheduler::BlockScheduler(const DominatorTree &dominatorTree, const LoopInfo &loopInfo) - : dominatorTree(dominatorTree) - , loopInfo(loopInfo) - , sequence(0) - , emitted(dominatorTree.function()) -{ - schedule(dominatorTree.function()->blocks().front()); - - dump(); - - if (dominatorTree.function()->blockCount() != sequence.size()) { - qFatal("The block scheduler did not schedule all blocks. This is most likely due to" - "a non-natural loop."); - // Usually caused by having an execution path that manages to skip over unwind handler - // reset: any exception happening after will jump back to the unwind handler, and thereby - // creating a loop that can be entered in 2 different ways. - } -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4blockscheduler_p.h b/src/qml/jit/qv4blockscheduler_p.h deleted file mode 100644 index 1289fda9f0..0000000000 --- a/src/qml/jit/qv4blockscheduler_p.h +++ /dev/null @@ -1,155 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4BLOCKSCHEDULER_P_H -#define QV4BLOCKSCHEDULER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qstack.h> - -#include "qv4mi_p.h" -#include "qv4util_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class DominatorTree; -class LoopInfo; - -// High-level algorithm: -// 0. start with the first node (the start node) of a function -// 1. emit the node -// 2. add all outgoing edges that are not yet emitted to the postponed stack -// 3. When the postponed stack is empty, pop a stack from the loop stack. If that is empty too, -// we're done. -// 4. pop a node from the postponed stack, and check if it can be scheduled: -// a. if all incoming edges are scheduled, go to 4. -// b. if an incoming edge is unscheduled, but it's a back-edge (an edge in a loop that jumps -// back to the start of the loop), ignore it -// c. if there is any unscheduled edge that is not a back-edge, ignore this node, and go to 4. -// 5. if this node is the start of a loop, push the postponed stack on the loop stack. -// 6. go back to 1. -// -// The postponing action in step 2 will put the node into its containing group. The case where this -// is important is when a (labeled) continue or a (labeled) break statement occur in a loop: the -// outgoing edge points to a node that is not part of the current loop (and possibly not of the -// parent loop). -// -// Linear scan register allocation benefits greatly from short life-time intervals with few holes -// (see for example section 4 (Lifetime Analysis) of [Wimmer1]). This algorithm makes sure that the -// blocks of a group are scheduled together, with no non-loop blocks in between. This applies -// recursively for nested loops. It also schedules groups of if-then-else-endif blocks together for -// the same reason. -class BlockScheduler -{ - const DominatorTree &dominatorTree; - const LoopInfo &loopInfo; - - struct WorkForGroup - { - MIBlock *group; - QStack<MIBlock *> postponed; - - WorkForGroup(MIBlock *group = nullptr) : group(group) {} - }; - WorkForGroup currentGroup; - QStack<WorkForGroup> postponedGroups; - std::vector<MIBlock *> sequence; - - class ProcessedBlocks - { - BitVector processed; - - public: - ProcessedBlocks(MIFunction *function) - : processed(int(function->blockCount()), false) - {} - - bool alreadyProcessed(MIBlock *bb) const - { - Q_ASSERT(bb); - - return processed.at(bb->index()); - } - - void markAsProcessed(MIBlock *bb) - { - processed.setBit(bb->index()); - } - } emitted; - QHash<MIBlock *, MIBlock *> loopsStartEnd; - - bool checkCandidate(MIBlock *candidate); - MIBlock *pickNext(); - void emitBlock(MIBlock *bb); - void schedule(MIBlock *functionEntryPoint); - void postpone(MIBlock *bb); - void dump() const; - -public: - BlockScheduler(const DominatorTree &dominatorTree, const LoopInfo &loopInfo); - - const std::vector<MIBlock *> &scheduledBlockSequence() const - { return sequence; } - - QHash<MIBlock *, MIBlock *> loopEndsByStartBlock() const - { return loopsStartEnd; } -}; - - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4BLOCKSCHEDULER_P_H diff --git a/src/qml/jit/qv4domtree.cpp b/src/qml/jit/qv4domtree.cpp deleted file mode 100644 index 9484f4e2dc..0000000000 --- a/src/qml/jit/qv4domtree.cpp +++ /dev/null @@ -1,436 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> -#include <QtCore/qbuffer.h> - -#include "qv4domtree_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcDomTree, "qt.v4.ir.domTree") -Q_LOGGING_CATEGORY(lcDomFrontier, "qt.v4.ir.domFrontier") - -DominatorTree::DominatorTree(MIFunction *f) - : m_function(f) - , m_data(new Data) -{ - calculateIDoms(); - m_data.reset(); -} - -void DominatorTree::dumpImmediateDominators() const -{ - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout << "Immediate dominators for " << m_function->irFunction()->name() << ":" << endl; - for (MIBlock *to : m_function->blocks()) { - MIBlock::Index from = m_idom.at(to->index()); - if (from != MIBlock::InvalidIndex) - qout << " " << from; - else - qout << " (none)"; - qout << " dominates " << to->index() << endl; - } - qCDebug(lcDomTree, "%s", buf.data().constData()); -} - -void DominatorTree::setImmediateDominator(MIBlock::Index dominated, MIBlock::Index dominator) -{ - if (m_idom.size() <= size_t(dominated)) - m_idom.resize(dominated + 1); - m_idom[dominated] = dominator; -} - -bool DominatorTree::dominates(MIBlock::Index dominator, MIBlock::Index dominated) const -{ - // dominator can be Invalid when the dominated block has no dominator (i.e. the start node) - Q_ASSERT(dominated != MIBlock::InvalidIndex); - - if (dominator == dominated) - return false; - - for (MIBlock::Index it = m_idom[dominated]; it != MIBlock::InvalidIndex; it = m_idom[it]) { - if (it == dominator) - return true; - } - - return false; -} - -// Calculate a depth-first iteration order on the nodes of the dominator tree. -// -// The order of the nodes in the vector is not the same as one where a recursive depth-first -// iteration is done on a tree. Rather, the nodes are (reverse) sorted on tree depth. -// So for the: -// 1 dominates 2 -// 2 dominates 3 -// 3 dominates 4 -// 2 dominates 5 -// the order will be: -// 4, 3, 5, 2, 1 -// or: -// 4, 5, 3, 2, 1 -// So the order of nodes on the same depth is undefined, but it will be after the nodes -// they dominate, and before the nodes that dominate them. -// -// The reason for this order is that a proper DFS pre-/post-order would require inverting -// the idom vector by either building a real tree datastructure or by searching the idoms -// for siblings and children. Both have a higher time complexity than sorting by depth. -std::vector<MIBlock *> DominatorTree::calculateDFNodeIterOrder() const -{ - std::vector<int> depths = calculateNodeDepths(); - std::vector<MIBlock *> order = m_function->blocks(); - std::sort(order.begin(), order.end(), [&depths](MIBlock *one, MIBlock *two) -> bool { - return depths.at(one->index()) > depths.at(two->index()); - }); - return order; -} - -// Algorithm: -// - for each node: -// - get the depth of a node. If it's unknown (-1): -// - get the depth of the immediate dominator. -// - if that's unknown too, calculate it by calling calculateNodeDepth -// - set the current node's depth to that of immediate dominator + 1 -std::vector<int> DominatorTree::calculateNodeDepths() const -{ - std::vector<int> nodeDepths(size_t(m_function->blockCount()), -1); - for (MIBlock *bb : m_function->blocks()) { - int &bbDepth = nodeDepths[bb->index()]; - if (bbDepth == -1) { - const int immDom = m_idom[bb->index()]; - if (immDom == -1) { - // no immediate dominator, so it's either the start block, or an unreachable block - bbDepth = 0; - } else { - int immDomDepth = nodeDepths[immDom]; - if (immDomDepth == -1) - immDomDepth = calculateNodeDepth(immDom, nodeDepths); - bbDepth = immDomDepth + 1; - } - } - } - return nodeDepths; -} - -// Algorithm: -// - search for the first dominator of a node that has a known depth. As all nodes are -// reachable from the start node, and that node's depth is 0, this is finite. -// - while doing that search, put all unknown nodes in the worklist -// - pop all nodes from the worklist, and set their depth to the previous' (== dominating) -// node's depth + 1 -// This way every node's depth is calculated once, and the complexity is O(n). -int DominatorTree::calculateNodeDepth(MIBlock::Index nodeIdx, std::vector<int> &nodeDepths) const -{ - std::vector<int> worklist; - worklist.reserve(8); - int depth = -1; - - do { - worklist.push_back(nodeIdx); - nodeIdx = m_idom[nodeIdx]; - depth = nodeDepths[nodeIdx]; - } while (depth == -1); - - for (auto it = worklist.rbegin(), eit = worklist.rend(); it != eit; ++it) - nodeDepths[*it] = ++depth; - - return depth; -} - -namespace { -struct DFSTodo { - MIBlock::Index node = MIBlock::InvalidIndex; - MIBlock::Index parent = MIBlock::InvalidIndex; - - DFSTodo() = default; - DFSTodo(MIBlock::Index node, MIBlock::Index parent) - : node(node) - , parent(parent) - {} -}; -} // anonymous namespace - -void DominatorTree::dfs(MIBlock::Index node) -{ - std::vector<DFSTodo> worklist; - worklist.reserve(m_data->vertex.capacity() / 2); - DFSTodo todo(node, MIBlock::InvalidIndex); - - while (true) { - MIBlock::Index n = todo.node; - - if (m_data->dfnum[n] == 0) { - m_data->dfnum[n] = m_data->size; - m_data->vertex[m_data->size] = n; - m_data->parent[n] = todo.parent; - ++m_data->size; - - MIBlock::OutEdges out = m_function->block(n)->outEdges(); - for (int i = out.size() - 1; i > 0; --i) - worklist.emplace_back(out[i]->index(), n); - - if (!out.isEmpty()) { - todo.node = out.first()->index(); - todo.parent = n; - continue; - } - } - - if (worklist.empty()) - break; - - todo = worklist.back(); - worklist.pop_back(); - } -} - -void DominatorTree::link(MIBlock::Index p, MIBlock::Index n) -{ - m_data->ancestor[n] = p; - m_data->best[n] = n; -} - -void DominatorTree::calculateIDoms() -{ - Q_ASSERT(m_function->block(0)->inEdges().count() == 0); - - const size_t bbCount = m_function->blockCount(); - m_data->vertex = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_data->parent = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_data->dfnum = std::vector<unsigned>(bbCount, 0); - m_data->semi = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_data->ancestor = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_idom = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_data->samedom = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - m_data->best = std::vector<MIBlock::Index>(bbCount, MIBlock::InvalidIndex); - - QHash<MIBlock::Index, std::vector<MIBlock::Index>> bucket; - bucket.reserve(int(bbCount)); - - dfs(m_function->block(0)->index()); - - std::vector<MIBlock::Index> worklist; - worklist.reserve(m_data->vertex.capacity() / 2); - - for (int i = m_data->size - 1; i > 0; --i) { - MIBlock::Index n = m_data->vertex[i]; - MIBlock::Index p = m_data->parent[n]; - MIBlock::Index s = p; - - for (auto inEdge : m_function->block(n)->inEdges()) { - if (inEdge->isDeoptBlock()) - continue; - MIBlock::Index v = inEdge->index(); - MIBlock::Index ss = MIBlock::InvalidIndex; - if (m_data->dfnum[v] <= m_data->dfnum[n]) - ss = v; - else - ss = m_data->semi[ancestorWithLowestSemi(v, worklist)]; - if (m_data->dfnum[ss] < m_data->dfnum[s]) - s = ss; - } - m_data->semi[n] = s; - bucket[s].push_back(n); - link(p, n); - if (bucket.contains(p)) { - for (MIBlock::Index v : bucket[p]) { - MIBlock::Index y = ancestorWithLowestSemi(v, worklist); - MIBlock::Index semi_v = m_data->semi[v]; - if (m_data->semi[y] == semi_v) - m_idom[v] = semi_v; - else - m_data->samedom[v] = y; - } - bucket.remove(p); - } - } - - for (unsigned i = 1; i < m_data->size; ++i) { - MIBlock::Index n = m_data->vertex[i]; - Q_ASSERT(n != MIBlock::InvalidIndex); - Q_ASSERT(!bucket.contains(n)); - Q_ASSERT(m_data->ancestor[n] != MIBlock::InvalidIndex); - Q_ASSERT((m_data->semi[n] != MIBlock::InvalidIndex - && m_data->dfnum[m_data->ancestor[n]] <= m_data->dfnum[m_data->semi[n]]) - || m_data->semi[n] == n); - MIBlock::Index sdn = m_data->samedom[n]; - if (sdn != MIBlock::InvalidIndex) - m_idom[n] = m_idom[sdn]; - } - - if (lcDomTree().isDebugEnabled()) - dumpImmediateDominators(); - - m_data.reset(nullptr); -} - -MIBlock::Index DominatorTree::ancestorWithLowestSemi(MIBlock::Index v, - std::vector<MIBlock::Index> &worklist) -{ - worklist.clear(); - for (MIBlock::Index it = v; it != MIBlock::InvalidIndex; it = m_data->ancestor[it]) - worklist.push_back(it); - - if (worklist.size() < 2) - return m_data->best[v]; - - MIBlock::Index b = MIBlock::InvalidIndex; - MIBlock::Index last = worklist.back(); - Q_ASSERT(worklist.size() <= INT_MAX); - for (int it = static_cast<int>(worklist.size()) - 2; it >= 0; --it) { - MIBlock::Index bbIt = worklist[it]; - m_data->ancestor[bbIt] = last; - MIBlock::Index &best_it = m_data->best[bbIt]; - if (b != MIBlock::InvalidIndex - && m_data->dfnum[m_data->semi[b]] < m_data->dfnum[m_data->semi[best_it]]) { - best_it = b; - } else { - b = best_it; - } - } - return b; -} - -void DominatorFrontier::compute(const DominatorTree &domTree) -{ - struct NodeProgress { - std::vector<MIBlock::Index> children; - std::vector<MIBlock::Index> todo; - }; - - MIFunction *function = domTree.function(); - m_df.resize(function->blockCount()); - - // compute children of each node in the dominator tree - std::vector<std::vector<MIBlock::Index> > children; // BasicBlock index -> children - children.resize(function->blockCount()); - for (MIBlock *n : function->blocks()) { - const MIBlock::Index nodeIndex = n->index(); - Q_ASSERT(function->block(nodeIndex) == n); - const MIBlock::Index nodeDominator = domTree.immediateDominator(nodeIndex); - if (nodeDominator == MIBlock::InvalidIndex) - continue; // there is no dominator to add this node to as a child (e.g. the start node) - children[nodeDominator].push_back(nodeIndex); - } - - // Fill the worklist and initialize the node status for each basic-block - std::vector<NodeProgress> nodeStatus; - nodeStatus.resize(function->blockCount()); - std::vector<MIBlock::Index> worklist; - worklist.reserve(function->blockCount()); - for (MIBlock *bb : function->blocks()) { - MIBlock::Index nodeIndex = bb->index(); - worklist.push_back(nodeIndex); - NodeProgress &np = nodeStatus[nodeIndex]; - np.children = children[nodeIndex]; - np.todo = children[nodeIndex]; - } - - BitVector DF_done(int(function->blockCount()), false); - - while (!worklist.empty()) { - MIBlock::Index node = worklist.back(); - - if (DF_done.at(node)) { - worklist.pop_back(); - continue; - } - - NodeProgress &np = nodeStatus[node]; - auto it = np.todo.begin(); - while (it != np.todo.end()) { - if (DF_done.at(*it)) { - it = np.todo.erase(it); - } else { - worklist.push_back(*it); - break; - } - } - - if (np.todo.empty()) { - MIBlockSet &miBlockSet = m_df[node]; - miBlockSet.init(function); - for (MIBlock *y : function->block(node)->outEdges()) { - if (domTree.immediateDominator(y->index()) != node) - miBlockSet.insert(y); - } - for (MIBlock::Index child : np.children) { - const MIBlockSet &ws = m_df[child]; - for (auto w : ws) { - const MIBlock::Index wIndex = w->index(); - if (node == wIndex || !domTree.dominates(node, w->index())) - miBlockSet.insert(w); - } - } - DF_done.setBit(node); - worklist.pop_back(); - } - } - - if (lcDomFrontier().isDebugEnabled()) - dump(domTree.function()); -} - -void DominatorFrontier::dump(MIFunction *function) -{ - QBuffer buf; - buf.open(QIODevice::WriteOnly); - QTextStream qout(&buf); - qout << "Dominator Frontiers:" << endl; - for (MIBlock *n : function->blocks()) { - qout << "\tDF[" << n->index() << "]: {"; - const MIBlockSet &SList = m_df[n->index()]; - for (MIBlockSet::const_iterator i = SList.begin(), ei = SList.end(); i != ei; ++i) { - if (i != SList.begin()) - qout << ", "; - qout << (*i)->index(); - } - qout << "}" << endl; - } - qCDebug(lcDomFrontier, "%s", buf.data().constData()); -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4domtree_p.h b/src/qml/jit/qv4domtree_p.h deleted file mode 100644 index 703e17ab61..0000000000 --- a/src/qml/jit/qv4domtree_p.h +++ /dev/null @@ -1,136 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4DOMTREE_P_H -#define QV4DOMTREE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4mi_p.h" -#include "qv4miblockset_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class DominatorTree -{ - Q_DISABLE_COPY_MOVE(DominatorTree) - -public: - DominatorTree(MIFunction *f); - ~DominatorTree() = default; - - void dumpImmediateDominators() const; - MIFunction *function() const - { return m_function; } - - void setImmediateDominator(MIBlock::Index dominated, MIBlock::Index dominator); - - MIBlock::Index immediateDominator(MIBlock::Index blockIndex) const - { return m_idom[blockIndex]; } - - bool dominates(MIBlock::Index dominator, MIBlock::Index dominated) const; - - bool insideSameDominatorChain(MIBlock::Index one, MIBlock::Index other) const - { return one == other || dominates(one, other) || dominates(other, one); } - - std::vector<MIBlock *> calculateDFNodeIterOrder() const; - - std::vector<int> calculateNodeDepths() const; - -private: // functions - int calculateNodeDepth(MIBlock::Index nodeIdx, std::vector<int> &nodeDepths) const; - void link(MIBlock::Index p, MIBlock::Index n); - void calculateIDoms(); - void dfs(MIBlock::Index node); - MIBlock::Index ancestorWithLowestSemi(MIBlock::Index v, std::vector<MIBlock::Index> &worklist); - -private: // data - struct Data { - std::vector<unsigned> dfnum; // MIBlock index -> dfnum - std::vector<MIBlock::Index> vertex; - std::vector<MIBlock::Index> parent; // MIBlock index -> parent MIBlock index - std::vector<MIBlock::Index> ancestor; // MIBlock index -> ancestor MIBlock index - std::vector<MIBlock::Index> best; // MIBlock index -> best MIBlock index - std::vector<MIBlock::Index> semi; // MIBlock index -> semi dominator MIBlock index - std::vector<MIBlock::Index> samedom; // MIBlock index -> same dominator MIBlock index - unsigned size = 0; - }; - - MIFunction *m_function; - QScopedPointer<Data> m_data; - std::vector<MIBlock::Index> m_idom; // MIBlock index -> immediate dominator MIBlock index -}; - -class DominatorFrontier -{ -public: - DominatorFrontier(const DominatorTree &domTree) - { compute(domTree); } - - const MIBlockSet &operator[](MIBlock *n) const - { return m_df[n->index()]; } - -private: // functions - void compute(const DominatorTree &domTree); - void dump(MIFunction *function); - -private: // data - std::vector<MIBlockSet> m_df; // MIBlock index -> dominator frontier -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4DOMTREE_P_H diff --git a/src/qml/jit/qv4graph.cpp b/src/qml/jit/qv4graph.cpp deleted file mode 100644 index 4025ceb993..0000000000 --- a/src/qml/jit/qv4graph.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4graph_p.h" -#include "qv4operation_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Graph *Graph::create(Function *function) -{ - auto storage = function->pool()->allocate(sizeof(Graph)); - auto g = new (storage) Graph(function); - g->m_undefinedNode = g->createNode(g->opBuilder()->get<Meta::Undefined>()); - g->m_emptyNode = g->createNode(g->opBuilder()->get<Meta::Empty>()); - g->m_nullNode = g->createNode(g->opBuilder()->getConstant(QV4::Value::nullValue())); - g->m_trueNode = g->createNode(g->opBuilder()->getConstant(QV4::Value::fromBoolean(true))); - g->m_falseNode = g->createNode(g->opBuilder()->getConstant(QV4::Value::fromBoolean(false))); - return g; -} - -Graph::MemoryPool *Graph::pool() const -{ - return m_function->pool(); -} - -Node *Graph::createNode(const Operation *op, Node *const operands[], size_t opCount, - bool incomplete) -{ - return Node::create(pool(), m_nextNodeId++, op, opCount, operands, incomplete); -} - -Node *Graph::createConstantBoolNode(bool value) -{ - return createNode(opBuilder()->getConstant(Primitive::fromBoolean(value))); -} - -Node *Graph::createConstantIntNode(int value) -{ - return createNode(opBuilder()->getConstant(Primitive::fromInt32(value))); -} - -Graph::Graph(Function *function) - : m_function(function) - , m_opBuilder(OperationBuilder::create(pool())) -{} - -Node *Graph::createConstantHeapNode(Heap::Base *heap) -{ - return createNode(opBuilder()->getConstant(Primitive::fromHeapObject(heap))); -} - -void Graph::addEndInput(Node *n) -{ - if (m_endNode) { - auto newEnd = m_opBuilder->getEnd(m_endNode->operation()->controlInputCount() + 1); - m_endNode->setOperation(newEnd); - m_endNode->addInput(m_function->pool(), n); - } -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4graph_p.h b/src/qml/jit/qv4graph_p.h deleted file mode 100644 index 4706399c94..0000000000 --- a/src/qml/jit/qv4graph_p.h +++ /dev/null @@ -1,146 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4GRAPH_P_H -#define QV4GRAPH_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmljsmemorypool_p.h> -#include <private/qv4global_p.h> -#include <private/qv4node_p.h> - -#include <array> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class Function; -class Operation; -class OperationBuilder; - -class Graph final -{ - Q_DISABLE_COPY_MOVE(Graph) - -public: - using MemoryPool = QQmlJS::MemoryPool; - -public: - static Graph *create(Function *function); - ~Graph() = delete; - - MemoryPool *pool() const; - OperationBuilder *opBuilder() const - { return m_opBuilder; } - - Node *createNode(const Operation *op, Node * const operands[] = nullptr, size_t opCount = 0, - bool incomplete = false); - template <typename... Nodes> - Node *createNode(Operation *op, Nodes*... nodes) { - std::array<Node *, sizeof...(nodes)> nodesArray {{ nodes... }}; - return createNode(op, nodesArray.data(), nodesArray.size()); - } - Node *createConstantBoolNode(bool value); - Node *createConstantIntNode(int value); - Node *createConstantHeapNode(Heap::Base *heap); - - Node *undefinedNode() const { return m_undefinedNode; } - Node *emptyNode() const { return m_emptyNode; } - Node *nullNode() const { return m_nullNode; } - Node *trueConstant() const { return m_trueNode; } - Node *falseConstant() const { return m_falseNode; } - - Node *startNode() const { return m_startNode; } - Node *engineNode() const { return m_engineNode; } - Node *functionNode() const { return m_functionNode; } - Node *cppFrameNode() const { return m_cppFrameNode; } - Node *endNode() const { return m_endNode; } - Node *initialFrameState() const { return m_initialFrameState; } - void setStartNode(Node *n) { m_startNode = n; } - void setEngineNode(Node *n) { m_engineNode = n; } - void setFunctionNode(Node *n) { m_functionNode = n; } - void setCppFrameNode(Node *n) { m_cppFrameNode = n; } - void setEndNode(Node *n) { m_endNode = n; } - void setInitialFrameState(Node *n) { m_initialFrameState = n; } - - unsigned nodeCount() const - { return unsigned(m_nextNodeId); } - - void addEndInput(Node *n); - -private: // types and methods - Graph(Function *function); - -private: // fields - Function *m_function; - OperationBuilder *m_opBuilder; - Node::Id m_nextNodeId = 0; - Node *m_undefinedNode = nullptr; - Node *m_emptyNode = nullptr; - Node *m_nullNode = nullptr; - Node *m_trueNode = nullptr; - Node *m_falseNode = nullptr; - Node *m_startNode = nullptr; - Node *m_engineNode = nullptr; - Node *m_functionNode = nullptr; - Node *m_cppFrameNode = nullptr; - Node *m_endNode = nullptr; - Node *m_initialFrameState = nullptr; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4GRAPH_P_H diff --git a/src/qml/jit/qv4graphbuilder.cpp b/src/qml/jit/qv4graphbuilder.cpp deleted file mode 100644 index 2c073701ee..0000000000 --- a/src/qml/jit/qv4graphbuilder.cpp +++ /dev/null @@ -1,1683 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> - -#include "qv4graphbuilder_p.h" -#include "qv4function_p.h" -#include "qv4lookup_p.h" -#include "qv4stackframe_p.h" -#include "qv4operation_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcIRGraphBuilder, "qt.v4.ir.graphbuilder") - -using MemoryPool = QQmlJS::MemoryPool; - -namespace { -template <typename T, size_t N> -char (&ArraySizeHelper(T (&array)[N]))[N]; -template <typename T, size_t N> -char (&ArraySizeHelper(const T (&array)[N]))[N]; - -template <typename Array> -inline size_t arraySize(const Array &array) -{ - Q_UNUSED(array); // for MSVC - return sizeof(ArraySizeHelper(array)); -} -} // anonymous namespace - -class GraphBuilder::InterpreterEnvironment -{ -public: - struct FrameState: public QQmlJS::FixedPoolArray<Node *> - { - FrameState(MemoryPool *pool, int totalSlotCount) - : FixedPoolArray(pool, totalSlotCount) - {} - - Node *&unwindHandlerOffset() - { return at(size() - 1); } - - static FrameState *create(MemoryPool *pool, int jsSlotCount) - { - auto totalSlotCount = jsSlotCount; - auto fs = pool->New<FrameState>(pool, totalSlotCount); - return fs; - } - - static FrameState *clone(MemoryPool *pool, FrameState *other) - { - FrameState *fs = create(pool, other->size()); - - for (int i = 0, ei = other->size(); i != ei; ++i) - fs->at(i) = other->at(i); - - return fs; - } - }; - -public: - InterpreterEnvironment(GraphBuilder *graphBuilder, Node *controlDependency) - : m_graphBuilder(graphBuilder) - , m_effectDependency(controlDependency) - , m_controlDependency(controlDependency) - , m_currentFrame(nullptr) - {} - - void createEnvironment() - { - Function *f = function(); - QV4::Function *v4Function = f->v4Function(); - const size_t nRegisters = v4Function->compiledFunction->nRegisters; - - // 1 extra slot for the unwindHandlerOffset - m_currentFrame = FrameState::create(graph()->pool(), int(nRegisters + 1)); - } - - void setupStartEnvironment() - { - Function *f = function(); - QV4::Function *v4Function = f->v4Function(); - const size_t nFormals = v4Function->compiledFunction->nFormals; - const size_t nRegisters = v4Function->compiledFunction->nRegisters; - - createEnvironment(); - - Node *startNode = graph()->startNode(); - auto opB = opBuilder(); - auto create = [&](int index, const char *name) { - m_currentFrame->at(index) = graph()->createNode( - opB->getParam(index, f->addString(QLatin1String(name))), startNode); - }; - create(0, "%function"); - create(1, "%context"); - create(2, "%acc"); - create(3, "%this"); - create(4, "%newTarget"); - create(5, "%argc"); - const quint32_le *formalNameIdx = v4Function->compiledFunction->formalsTable(); - for (size_t i = 0; i < nFormals; ++i, ++formalNameIdx) { - const int slot = int(CallData::HeaderSize() + i); - Q_ASSERT(*formalNameIdx <= quint32(std::numeric_limits<int>::max())); - auto op = opB->getParam( - slot, - f->addString(v4Function->compilationUnit->stringAt(int(*formalNameIdx)))); - Node *argNode = graph()->createNode(op, startNode); - m_currentFrame->at(slot) = argNode; - } - Node *undefinedNode = graph()->undefinedNode(); - Node *emptyNode = graph()->emptyNode(); - const auto firstDeadZoneRegister - = v4Function->compiledFunction->firstTemporalDeadZoneRegister; - const auto registerDeadZoneSize - = v4Function->compiledFunction->sizeOfRegisterTemporalDeadZone; - for (size_t i = CallData::HeaderSize() + nFormals; i < nRegisters; ++i) { - const bool isDead = i >= firstDeadZoneRegister - && i < size_t(firstDeadZoneRegister + registerDeadZoneSize); - m_currentFrame->at(int(i)) = isDead ? emptyNode : undefinedNode; - } - setUnwindHandlerOffset(0); - } - - Function *function() const { return m_graphBuilder->function(); } - Graph *graph() const { return function()->graph(); } - OperationBuilder *opBuilder() const { return graph()->opBuilder(); } - GraphBuilder *graphBuilder() const { return m_graphBuilder; } - - Node *bindAcc(Node *node) - { - bindNodeToSlot(node, CallData::Accumulator); - return node; - } - - Node *accumulator() const - { return slot(CallData::Accumulator); } - - Node *bindNodeToSlot(Node *node, int slot) - { - m_currentFrame->at(size_t(slot)) = node; - return node; - } - - Node *slot(int slot) const - { return m_currentFrame->at(slot); } - - int slotCount() const - { return m_currentFrame->size(); } - - Node *effectDependency() const - { return m_effectDependency; } - - void setEffectDependency(Node *newNode) - { m_effectDependency = newNode; } - - Node *controlDependency() const - { return m_controlDependency; } - - void setControlDependency(Node *newNode) - { m_controlDependency = newNode; } - - Node *createFrameState() - { - return graph()->createNode(graphBuilder()->opBuilder()->getFrameState(slotCount()), - m_currentFrame->begin(), slotCount()); - } - - Node *merge(InterpreterEnvironment *other); - - InterpreterEnvironment *copy() const - { - auto *newEnv = graph()->pool()->New<InterpreterEnvironment>(graphBuilder(), - controlDependency()); - newEnv->setEffectDependency(effectDependency()); - newEnv->m_currentFrame = FrameState::clone(graph()->pool(), m_currentFrame); - return newEnv; - } - - int unwindHandlerOffset() const - { - auto uhOp = m_currentFrame->unwindHandlerOffset()->operation(); - Q_ASSERT(uhOp->kind() == Meta::Constant); - return ConstantPayload::get(*uhOp)->value().int_32(); - } - - void setUnwindHandlerOffset(int newOffset) - { m_currentFrame->unwindHandlerOffset() = graphBuilder()->createConstant(newOffset); } - - FrameState *frameState() const - { return m_currentFrame; } - -private: - GraphBuilder *m_graphBuilder; - Node *m_effectDependency; - Node *m_controlDependency; - FrameState *m_currentFrame; -}; - -namespace { -class InterpreterSubEnvironment final -{ - Q_DISABLE_COPY_MOVE(InterpreterSubEnvironment) - -public: - explicit InterpreterSubEnvironment(GraphBuilder *builder) - : m_builder(builder) - , m_parent(builder->env()->copy()) - {} - - ~InterpreterSubEnvironment() - { m_builder->setEnv(m_parent); } - -private: - GraphBuilder *m_builder; - GraphBuilder::InterpreterEnvironment *m_parent; -}; -} // anonymous namespace - -Node *GraphBuilder::InterpreterEnvironment::merge(InterpreterEnvironment *other) -{ - Q_ASSERT(m_currentFrame->size() == other->m_currentFrame->size()); - - auto gb = graphBuilder(); - Node *mergedControl = gb->mergeControl(controlDependency(), other->controlDependency()); - setControlDependency(mergedControl); - setEffectDependency(gb->mergeEffect(effectDependency(), other->effectDependency(), mergedControl)); - - // insert/update phi nodes, but not for the unwind handler: - for (int i = 0, ei = m_currentFrame->size() - 1; i != ei; ++i) { - //### use lifeness info to trim this! - m_currentFrame->at(i) = gb->mergeValue(m_currentFrame->at(i), - other->m_currentFrame->at(i), - mergedControl); - } - Q_ASSERT(unwindHandlerOffset() >= 0); // specifically: don't crash - return mergedControl; -} - -void GraphBuilder::buildGraph(IR::Function *function) -{ - const char *code = function->v4Function()->codeData; - uint len = function->v4Function()->compiledFunction->codeSize; - - GraphBuilder builder(function); - builder.startGraph(); - - InterpreterEnvironment initial(&builder, function->graph()->startNode()); - initial.setupStartEnvironment(); - builder.setEnv(&initial); - builder.graph()->setInitialFrameState(initial.createFrameState()); - builder.decode(code, len); - builder.endGraph(); -}; - -GraphBuilder::GraphBuilder(IR::Function *function) - : m_func(function) - , m_graph(function->graph()) - , m_currentEnv(nullptr) -{ - for (unsigned i = 0, ei = m_func->v4Function()->compiledFunction->nLabelInfos; i != ei; ++i) { - unsigned label = m_func->v4Function()->compiledFunction->labelInfoTable()[i]; - m_labelInfos.emplace_back(label); - if (lcIRGraphBuilder().isDebugEnabled()) { - const LabelInfo &li = m_labelInfos.back(); - qCDebug(lcIRGraphBuilder) << "Loop start at" << li.labelOffset; - } - } -} - -void GraphBuilder::startGraph() -{ - size_t nValuesOut = 1 + CallData::HeaderSize() - + m_func->v4Function()->compiledFunction->nFormals; - Node *start = m_graph->createNode(opBuilder()->getStart(uint16_t(nValuesOut)), nullptr, 0); - m_func->nodeInfo(start)->setBytecodeOffsets(0, 0); - m_graph->setStartNode(start); - m_graph->setEngineNode(m_graph->createNode(opBuilder()->get<Meta::Engine>(), &start, 1)); - auto frame = m_graph->createNode(opBuilder()->get<Meta::CppFrame>(), &start, 1); - m_graph->setCppFrameNode(frame); - m_graph->setFunctionNode(m_graph->createNode(opBuilder()->get<Meta::Function>(), - &frame, 1)); -} - -void GraphBuilder::endGraph() -{ - const auto inputCount = uint16_t(m_exitControls.size()); - Node **inputs = &m_exitControls.front(); - Q_ASSERT(m_graph->endNode() == nullptr); - m_graph->setEndNode(m_graph->createNode(opBuilder()->getEnd(inputCount), inputs, inputCount)); -} - -Node *GraphBuilder::bindAcc(Node *n) -{ - return env()->bindAcc(n); -} - -/* IMPORTANT!!! - * - * This might change the success environment, so don't call: - * env()->bindAcc(createNode(...)) - * because the binding should only happen on success, but the call to env() will get the - * environment from *before* the new success environment was created. Instead, do: - * bindAcc(createNode(....)) - */ -Node *GraphBuilder::createAndLinkNode(Operation *op, Node *operands[], size_t opCount, - bool incomplete) -{ - Q_ASSERT(op->effectInputCount() < 2); - Q_ASSERT(op->controlInputCount() < 2); - - QVarLengthArray<Node *, 32> inputs(static_cast<int>(opCount)); - std::copy_n(operands, opCount, inputs.data()); - - if (op->effectInputCount() == 1) - inputs.append(env()->effectDependency()); - if (op->controlInputCount() == 1) - inputs.append(env()->controlDependency()); - if (op->hasFrameStateInput()) - inputs.append(env()->createFrameState()); - - Node *node = m_graph->createNode(op, inputs.data(), inputs.size(), incomplete); - - if (op->needsBytecodeOffsets()) { - m_func->nodeInfo(node)->setBytecodeOffsets(currentInstructionOffset(), - nextInstructionOffset()); - } - - if (op->effectOutputCount() > 0) - env()->setEffectDependency(node); - if (op->controlOutputCount() > 0) - env()->setControlDependency(node); - - if (op->canThrow() && env()->unwindHandlerOffset()) { - InterpreterSubEnvironment successEnv(this); - Node *control = env()->controlDependency(); - control = m_graph->createNode(opBuilder()->get<Meta::OnException>(), &control, 1); - env()->setControlDependency(control); - auto unwindHandlerOffset = env()->unwindHandlerOffset(); - mergeIntoSuccessor(unwindHandlerOffset); - } - - return node; -} - -Node *GraphBuilder::createNode(Operation *op, bool incomplete) -{ - return createAndLinkNode(op, nullptr, 0, incomplete); -} - -Node *GraphBuilder::createNode(Operation *op, Node *n1) -{ - Node *buf[] = { n1 }; - return createAndLinkNode(op, buf, arraySize(buf)); -} - -Node *GraphBuilder::createNode(Operation *op, Node *n1, Node *n2) -{ - Node *buf[] = { n1, n2 }; - return createAndLinkNode(op, buf, arraySize(buf)); -} - -Node *GraphBuilder::createNode(Operation *op, Node *n1, Node *n2, Node *n3) -{ - Node *buf[] = { n1, n2, n3 }; - return createAndLinkNode(op, buf, arraySize(buf)); -} - -Node *GraphBuilder::createNode(Operation *op, Node *n1, Node *n2, Node *n3, Node *n4) -{ - Node *buf[] = { n1, n2, n3, n4 }; - return createAndLinkNode(op, buf, arraySize(buf)); -} - -Node *GraphBuilder::createRegion(unsigned nControlInputs) -{ - return createNode(opBuilder()->getRegion(nControlInputs), true); -} - -Node *GraphBuilder::createIfTrue() -{ - return createNode(opBuilder()->get<Meta::IfTrue>()); -} - -Node *GraphBuilder::createIfFalse() -{ - return createNode(opBuilder()->get<Meta::IfFalse>()); -} - -Node *GraphBuilder::createConstant(int v) -{ - return m_graph->createNode(opBuilder()->getConstant(Primitive::fromInt32(v))); -} - -Node *GraphBuilder::createPhi(unsigned nInputs, Node *input, Node *control) -{ - auto phiOp = opBuilder()->getPhi(nInputs); - QVarLengthArray<Node *, 32> buffer(int(nInputs + 1)); - std::fill_n(buffer.data(), nInputs, input); - buffer[int(nInputs)] = control; - return m_graph->createNode(phiOp, buffer.data(), nInputs + 1, true); -} - -Node *GraphBuilder::createEffectPhi(unsigned nInputs, Node *input, Node *control) -{ - auto phiOp = opBuilder()->getEffectPhi(nInputs); - QVarLengthArray<Node *, 32> buffer(int(nInputs + 1)); - std::fill_n(buffer.data(), nInputs, input); - buffer[int(nInputs)] = control; - return m_graph->createNode(phiOp, buffer.data(), nInputs + 1, true); -} - -Node *GraphBuilder::createHandleUnwind(int offset) -{ - return createNode(opBuilder()->getHandleUnwind(offset)); -} - -Node *GraphBuilder::mergeControl(Node *c1, Node *c2) -{ - if (c1->operation()->kind() == Meta::Region) { - const unsigned nInputs = c1->operation()->controlInputCount() + 1; - c1->addInput(m_graph->pool(), c2); - c1->setOperation(opBuilder()->getRegion(nInputs)); - return c1; - } - auto op = opBuilder()->getRegion(2); - Node *inputs[] = { c1, c2 }; - return m_graph->createNode(op, inputs, 2); -} - -Node *GraphBuilder::mergeEffect(Node *e1, Node *e2, Node *control) -{ - const unsigned nInputs = control->operation()->controlInputCount(); - if (e1->operation()->kind() == Meta::EffectPhi && e1->controlInput() == control) { - e1->insertInput(m_graph->pool(), nInputs - 1, e2); - e1->setOperation(opBuilder()->getEffectPhi(nInputs)); - return e1; - } - - if (e1 != e2) { - Node *phi = createEffectPhi(nInputs, e1, control); - phi->replaceInput(nInputs - 1, e2); - return phi; - } - - return e1; -} - -Node *GraphBuilder::mergeValue(Node *v1, Node *v2, Node *control) -{ - const unsigned nInputs = control->operation()->controlInputCount(); - if (v1->operation()->kind() == Meta::Phi && v1->controlInput() == control) { - v1->insertInput(m_graph->pool(), nInputs - 1, v2); - v1->setOperation(opBuilder()->getPhi(nInputs)); - return v1; - } - - if (v1 != v2) { - Node *phi = createPhi(nInputs, v1, control); - phi->replaceInput(nInputs - 1, v2); - return phi; - } - - return v1; -} - -Node *GraphBuilder::createToBoolean(Node *input) -{ - return createNode(opBuilder()->get<Meta::ToBoolean>(), input); -} - -void GraphBuilder::populate(VarArgNodes &args, int argc, int argv) -{ - for (int i = 0; i < argc; ++i) - args.append(env()->slot(argv + i)); - Q_ASSERT(argc >= 0 && argc <= std::numeric_limits<uint16_t>::max()); -} - -void GraphBuilder::queueFunctionExit(Node *exitNode) -{ - m_exitControls.push_back(exitNode); - setEnv(nullptr); -} - -Node *GraphBuilder::mergeIntoSuccessor(int offset) -{ - InterpreterEnvironment *&successorEnvironment = m_envForOffset[offset]; - - Node *region = nullptr; - if (successorEnvironment == nullptr) { - region = createRegion(1); - successorEnvironment = env(); - } else { - // Merge any values which are live coming into the successor. - region = successorEnvironment->merge(env()); - } - setEnv(nullptr); - return region; -} - -const GraphBuilder::LabelInfo *GraphBuilder::labelInfoAt(unsigned offset) const -{ - for (const LabelInfo &li : m_labelInfos) { - if (li.labelOffset == offset) - return &li; - } - return nullptr; -} - -const GraphBuilder::LabelInfo *GraphBuilder::isLoopStart(unsigned offset) const -{ - if (auto li = labelInfoAt(offset)) { - //### in the future, check if this is a loop start, or some other label - return li; - } - - return nullptr; -} - -void GraphBuilder::handleLoopStart(const LabelInfo &labelInfo) -{ - Q_ASSERT(env() != nullptr); - - // We unconditionally insert a region node with phi nodes here. Now there might already be - // such a node, (e.g. the region after an if-then-else), but for simplicity we ignore that. - // A subsequent pass will fold/remove chains of Region nodes. - //### FIXME: add a DCE pass - - const auto offset = int(labelInfo.labelOffset); - Node *control = createRegion(1); - env()->setControlDependency(control); - Node *effect = createEffectPhi(1, env()->effectDependency(), control); - env()->setEffectDependency(effect); - - // insert/update phi nodes, but not for the unwind handler: - for (int i = 0, ei = env()->slotCount() - 1; i != ei; ++i) { - //### use lifeness info to trim this further! - if (i == CallData::Accumulator) - continue; // should never be alive on loop entry - env()->bindNodeToSlot(createPhi(1, env()->slot(i), control), i); - } - - m_envForOffset.insert(offset, env()->copy()); -} - -void GraphBuilder::startUnwinding() -{ - if (int target = env()->unwindHandlerOffset()) { - mergeIntoSuccessor(target); - } else { - bindAcc(graph()->undefinedNode()); - generate_Ret(); - } -} - -void GraphBuilder::generate_Ret() -{ - Node* control = createNode(opBuilder()->get<Meta::Return>(), env()->accumulator()); - queueFunctionExit(control); -} - -void GraphBuilder::generate_Debug() { Q_UNREACHABLE(); } - -void GraphBuilder::generate_LoadConst(int index) -{ - auto func = function()->v4Function(); - Value v = func->compilationUnit->constants[index]; - bindAcc(createNode(opBuilder()->getConstant(v))); -} - -void GraphBuilder::generate_LoadZero() -{ - bindAcc(createConstant(0)); -} - -void GraphBuilder::generate_LoadTrue() -{ - bindAcc(m_graph->trueConstant()); -} - -void GraphBuilder::generate_LoadFalse() -{ - bindAcc(m_graph->falseConstant()); -} - -void GraphBuilder::generate_LoadNull() -{ - bindAcc(m_graph->nullNode()); -} - -void GraphBuilder::generate_LoadUndefined() -{ - bindAcc(m_graph->undefinedNode()); -} - -void GraphBuilder::generate_LoadInt(int value) -{ - bindAcc(m_graph->createNode(opBuilder()->getConstant(Primitive::fromInt32(value)))); -} - -void GraphBuilder::generate_MoveConst(int constIndex, int destTemp) -{ - auto func = function()->v4Function(); - Value v = func->compilationUnit->constants[constIndex]; - env()->bindNodeToSlot(createNode(opBuilder()->getConstant(v)), destTemp); -} - -void GraphBuilder::generate_LoadReg(int reg) -{ - bindAcc(env()->slot(reg)); -} - -void GraphBuilder::generate_StoreReg(int reg) -{ - Node *n = env()->accumulator(); - if (reg == CallData::This) - n = createNode(opBuilder()->get<Meta::StoreThis>(), n); - env()->bindNodeToSlot(n, reg); -} - -void GraphBuilder::generate_MoveReg(int srcReg, int destReg) -{ - env()->bindNodeToSlot(env()->slot(srcReg), destReg); -} - -void GraphBuilder::generate_LoadImport(int index) -{ - auto func = function()->v4Function(); - Value v = *func->compilationUnit->imports[index]; - bindAcc(createNode(opBuilder()->getConstant(v))); -} - -void GraphBuilder::generate_LoadLocal(int index, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::ScopedLoad>(), - createConstant(0), - createConstant(index))); -} - -void GraphBuilder::generate_StoreLocal(int index) -{ - createNode(opBuilder()->get<Meta::ScopedStore>(), - createConstant(0), - createConstant(index), - env()->accumulator()); -} - -void GraphBuilder::generate_LoadScopedLocal(int scope, int index, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::ScopedLoad>(), - createConstant(scope), - createConstant(index))); -} - -void GraphBuilder::generate_StoreScopedLocal(int scope, int index) -{ - createNode(opBuilder()->get<Meta::ScopedStore>(), - createConstant(scope), - createConstant(index), - env()->accumulator()); -} - -void GraphBuilder::generate_LoadRuntimeString(int stringId) -{ - auto func = function()->v4Function(); - Value v = Value::fromHeapObject(func->compilationUnit->runtimeStrings[stringId]); - bindAcc(createNode(opBuilder()->getConstant(v))); -} - -void GraphBuilder::generate_MoveRegExp(int regExpId, int destReg) -{ - env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::LoadRegExp>(), - createConstant(regExpId)), destReg); -} - -void GraphBuilder::generate_LoadClosure(int value) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadClosure>(), - createConstant(value))); -} - -void GraphBuilder::generate_LoadName(int name, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadName>(), - createConstant(name))); -} - -void GraphBuilder::generate_LoadGlobalLookup(int index, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadGlobalLookup>(), createConstant(index))); -} - -void GraphBuilder::generate_StoreNameSloppy(int name) -{ - createNode(opBuilder()->get<Meta::JSStoreNameSloppy>(), createConstant(name), env()->accumulator()); -} - -void GraphBuilder::generate_StoreNameStrict(int name) -{ - createNode(opBuilder()->get<Meta::JSStoreNameStrict>(), createConstant(name), env()->accumulator()); -} - -void GraphBuilder::generate_LoadElement(int base, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadElement>(), - env()->slot(base), - env()->accumulator())); -} - -void GraphBuilder::generate_StoreElement(int base, int index, int /*traceSlot*/) -{ - createNode(opBuilder()->get<Meta::JSStoreElement>(), - env()->slot(base), - env()->slot(index), - env()->accumulator()); -} - -void GraphBuilder::generate_LoadProperty(int name, int /*traceSlot*/) -{ - Node *n = createNode(opBuilder()->get<Meta::JSLoadProperty>(), - env()->accumulator(), - createConstant(name)); - bindAcc(n); -} - -void GraphBuilder::generate_GetLookup(int index, int /*traceSlot*/) -{ - Node *n = createNode(opBuilder()->get<Meta::JSGetLookup>(), - env()->accumulator(), - createConstant(index)); - bindAcc(n); -} - -void GraphBuilder::generate_StoreProperty(int name, int base) -{ - createNode(opBuilder()->get<Meta::JSStoreProperty>(), - env()->slot(base), - createConstant(name), - env()->accumulator()); -} - -void GraphBuilder::generate_SetLookup(int index, int base) -{ - - function()->v4Function()->isStrict() - ? createNode(opBuilder()->get<Meta::JSSetLookupStrict>(), env()->slot(base), - createConstant(index), env()->accumulator()) - : createNode(opBuilder()->get<Meta::JSSetLookupSloppy>(), env()->slot(base), - createConstant(index), env()->accumulator()); -} - -void GraphBuilder::generate_LoadSuperProperty(int property) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadSuperProperty>(), - env()->slot(property))); -} - -void GraphBuilder::generate_StoreSuperProperty(int property) -{ - createNode(opBuilder()->get<Meta::JSStoreSuperProperty>(), - createConstant(property), - env()->accumulator()); -} - -void GraphBuilder::generate_LoadQmlContextPropertyLookup(int propertyIndex, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::QMLLoadQmlContextPropertyLookup>(), - createConstant(propertyIndex))); -} - -void GraphBuilder::generate_Yield() { Q_UNREACHABLE(); } -void GraphBuilder::generate_YieldStar() { Q_UNREACHABLE(); } -void GraphBuilder::generate_Resume(int /*offset*/) { Q_UNREACHABLE(); } - -void GraphBuilder::finalizeCall(Operation::Kind kind, VarArgNodes &args, int argc, int argv) -{ - populate(args, argc, argv); - bindAcc(createAndLinkNode(opBuilder()->getJSVarArgsCall(kind, uint16_t(args.size())), - args.data(), size_t(args.size()))); -} - -void GraphBuilder::generate_CallValue(int name, int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(name)); - finalizeCall(Meta::JSCallValue, args, argc, argv); -} - -void GraphBuilder::generate_CallWithReceiver(int name, int thisObject, int argc, int argv, - int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(name)); - args.append(env()->slot(thisObject)); - finalizeCall(Meta::JSCallWithReceiver, args, argc, argv); -} - -void GraphBuilder::generate_CallProperty(int name, int base, int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(base)); - args.append(createConstant(name)); - finalizeCall(Meta::JSCallProperty, args, argc, argv); -} - -void GraphBuilder::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, - int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(base)); - args.append(createConstant(lookupIndex)); - finalizeCall(Meta::JSCallLookup, args, argc, argv); -} - -void GraphBuilder::generate_CallElement(int base, int index, int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(base)); - args.append(env()->slot(index)); - finalizeCall(Meta::JSCallElement, args, argc, argv); -} - -void GraphBuilder::generate_CallName(int name, int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - args.append(createConstant(name)); - finalizeCall(Meta::JSCallName, args, argc, argv); -} - -void GraphBuilder::generate_CallPossiblyDirectEval(int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - finalizeCall(Meta::JSCallPossiblyDirectEval, args, argc, argv); -} - -void GraphBuilder::generate_CallGlobalLookup(int index, int argc, int argv, int /*traceSlot*/) -{ - VarArgNodes args; - args.append(createConstant(index)); - finalizeCall(Meta::JSCallGlobalLookup, args, argc, argv); -} - -void GraphBuilder::generate_CallQmlContextPropertyLookup(int index, int argc, int argv, - int /*traceSlot*/) -{ - VarArgNodes args; - args.append(createConstant(index)); - finalizeCall(Meta::QMLCallQmlContextPropertyLookup, args, argc, argv); -} - -void GraphBuilder::generate_SetUnwindHandler(int offset) -{ - m_currentUnwindHandlerOffset = offset ? absoluteOffset(offset) : 0; - env()->setUnwindHandlerOffset(m_currentUnwindHandlerOffset); -} - -void GraphBuilder::generate_UnwindDispatch() -{ - auto e = createNode(opBuilder()->get<Meta::HasException>(), graph()->engineNode()); - createNode(opBuilder()->get<Meta::Branch>(), e); - { - InterpreterSubEnvironment subEnvironment(this); - createIfTrue(); - startUnwinding(); - } - - createIfFalse(); - - const auto unwindHandlerOffset = env()->unwindHandlerOffset(); - const auto fallthroughSuccessor = nextInstructionOffset(); - auto nContinuations = m_func->unwindLabelOffsets().size() + 1; - if (unwindHandlerOffset) - ++nContinuations; - Q_ASSERT(nContinuations <= std::numeric_limits<unsigned>::max()); - createNode(opBuilder()->getUnwindDispatch(unsigned(nContinuations), unwindHandlerOffset, - fallthroughSuccessor)); - - { - InterpreterSubEnvironment fallthroughEnv(this); - mergeIntoSuccessor(fallthroughSuccessor); - } - - if (unwindHandlerOffset) { - InterpreterSubEnvironment unwindHandlerEnv(this); - createHandleUnwind(unwindHandlerOffset); - mergeIntoSuccessor(unwindHandlerOffset); - } - - for (int unwindLabelOffset : m_func->unwindLabelOffsets()) { - if (unwindLabelOffset <= currentInstructionOffset()) - continue; - InterpreterSubEnvironment unwindLabelEnv(this); - createHandleUnwind(unwindLabelOffset); - mergeIntoSuccessor(unwindLabelOffset); - } - - setEnv(nullptr); -} - -void GraphBuilder::generate_UnwindToLabel(int level, int offset) -{ - //### For de-optimization, the relative offset probably also needs to be stored - int unwinder = absoluteOffset(offset); - createNode(opBuilder()->get<Meta::UnwindToLabel>(), - createConstant(level), - createConstant(unwinder)); - m_func->addUnwindLabelOffset(unwinder); - startUnwinding(); -} - -void GraphBuilder::generate_DeadTemporalZoneCheck(int name) -{ - Node *check = createNode(opBuilder()->get<Meta::IsEmpty>(), env()->accumulator()); - createNode(opBuilder()->get<Meta::Branch>(), check); - - { //### it's probably better to handle this by de-optimizing - InterpreterSubEnvironment subEnvironment(this); - createIfTrue(); - createNode(opBuilder()->get<Meta::ThrowReferenceError>(), - createConstant(name)); - startUnwinding(); - } - - createIfFalse(); -} - -void GraphBuilder::generate_ThrowException() -{ - createNode(opBuilder()->get<Meta::Throw>(), env()->accumulator()); - startUnwinding(); -} - -void GraphBuilder::generate_GetException() -{ - bindAcc(createNode(opBuilder()->get<Meta::GetException>())); -} - -void GraphBuilder::generate_SetException() -{ - createNode(opBuilder()->get<Meta::SetException>(), - env()->accumulator()); -} - -void GraphBuilder::generate_CreateCallContext() -{ - createNode(opBuilder()->get<Meta::JSCreateCallContext>()); -} - -void GraphBuilder::generate_PushCatchContext(int index, int name) -{ - createNode(opBuilder()->get<Meta::JSCreateCatchContext>(), - createConstant(index), - createConstant(name)); -} - -void GraphBuilder::generate_PushWithContext() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSCreateWithContext>(), - env()->accumulator())); -} - -void GraphBuilder::generate_PushBlockContext(int index) -{ - createNode(opBuilder()->get<Meta::JSCreateBlockContext>(), - createConstant(index)); -} - -void GraphBuilder::generate_CloneBlockContext() -{ - createNode(opBuilder()->get<Meta::JSCloneBlockContext>()); -} - -void GraphBuilder::generate_PushScriptContext(int index) -{ - createNode(opBuilder()->get<Meta::JSCreateScriptContext>(), createConstant(index)); -} - -void GraphBuilder::generate_PopScriptContext() -{ - createNode(opBuilder()->get<Meta::JSPopScriptContext>()); -} - -void GraphBuilder::generate_PopContext() -{ - createNode(opBuilder()->get<Meta::PopContext>()); -} - -void GraphBuilder::generate_GetIterator(int iterator) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSGetIterator>(), - env()->accumulator(), - createConstant(iterator))); -} - -void GraphBuilder::generate_IteratorNextAndFriends_TrailingStuff(Node *iterationNode, - int resultSlot) -{ - // See generate_IteratorNext for why this method exists. - - // check that no-one messed around with the operation and made it throwing - Q_ASSERT(iterationNode->operation()->controlOutputCount() == 1); - - // check that it's in the effect chain, because HasException relies on that - Q_ASSERT(iterationNode->operation()->effectOutputCount() == 1); - - env()->bindNodeToSlot(createNode(opBuilder()->get<Meta::SelectOutput>(), - iterationNode, - createConstant(1), - graph()->undefinedNode()), - resultSlot); - // Note: the following will NOT set the accumulator, because it contains the return value of - // the runtime call! - Node *ehCheck = createNode(opBuilder()->get<Meta::HasException>(), graph()->engineNode()); - createNode(opBuilder()->get<Meta::Branch>(), ehCheck); - - { // EH path: - InterpreterSubEnvironment subEnvironment(this); - createIfTrue(); - if (auto ehOffset = env()->unwindHandlerOffset()) { - // Ok, there is an exception handler, so go there: - mergeIntoSuccessor(ehOffset); - } else { - // No Exception Handler, so keep the exception set in the engine, and leave the function - // a.s.a.p.: - bindAcc(graph()->undefinedNode()); - generate_Ret(); - } - } - - // Normal control flow: - createIfFalse(); -} - -void GraphBuilder::generate_IteratorNext(int value, int done) -{ - // The way we model exceptions in the graph is that a runtime function will either succeed and - // return a value, or it fails and throws an exception. If it throws, the return value is not - // used because the method did not complete normally, and therefore it might be tainted. - // - // This is a problem for (and only for) IteratorNext and IteratorNextForYieldStart. - // - // What would happen in the normal case, is that the return value (done) is not used/assigned - // when IteratorNext throws, because the exception handling path is chosen. However, the - // interpreter *does* assign it, and will only check for an exception *after* that assignment. - // - // So, in order to work around this odd-duck behavior, we mark the operation as NoThrow, - // override the runtime method and flag it to not throw, and insert extra exception check nodes - // after the SelectOutput that follows the IteratorNext(ForYieldStar). - // - // Also note that the IteratorNext and IteratorNextForYieldStar are the only operations that - // have an inout parameter, and thus require a SelectOutput node to retrieve this. - - Node *n = createNode(opBuilder()->get<Meta::JSIteratorNext>(), - env()->accumulator(), - graph()->undefinedNode()); - bindAcc(n); - env()->bindNodeToSlot(n, done); - generate_IteratorNextAndFriends_TrailingStuff(n, value); -} - -void GraphBuilder::generate_IteratorNextForYieldStar(int iterator, int object) -{ - // Please, PLEASE read the comment in generate_IteratorNext. - Node *n = createNode(opBuilder()->get<Meta::JSIteratorNextForYieldStar>(), - env()->accumulator(), - env()->slot(iterator), - graph()->undefinedNode()); - // Note: the following is a tiny bit different from what generate_IteratorNext does. - bindAcc(n); - generate_IteratorNextAndFriends_TrailingStuff(n, object); -} - -void GraphBuilder::generate_IteratorClose(int done) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSIteratorClose>(), - env()->accumulator(), - env()->slot(done))); -} - -void GraphBuilder::generate_DestructureRestElement() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSDestructureRestElement>(), - env()->accumulator())); -} - -void GraphBuilder::generate_DeleteProperty(int base, int index) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSDeleteProperty>(), - env()->slot(base), - env()->slot(index))); -} - -void GraphBuilder::generate_DeleteName(int name) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSDeleteName>(), - createConstant(name))); -} - -void GraphBuilder::generate_TypeofName(int name) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSTypeofName>(), - createConstant(name))); -} - -void GraphBuilder::generate_TypeofValue() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSTypeofValue>(), env()->accumulator())); -} - -void GraphBuilder::generate_DeclareVar(int varName, int isDeletable) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSDeclareVar>(), - createConstant(isDeletable), - createConstant(varName))); -} - -void GraphBuilder::generate_DefineArray(int argc, int argv) -{ - VarArgNodes args; - finalizeCall(Meta::JSDefineArray, args, argc, argv); -} - -void GraphBuilder::generate_DefineObjectLiteral(int internalClassId, int argc, int argv) -{ - VarArgNodes args; - args.append(createConstant(internalClassId)); - finalizeCall(Meta::JSDefineObjectLiteral, args, argc, argv); -} - -void GraphBuilder::generate_CreateClass(int classIndex, int heritage, int computedNames) -{ - int argc = 0; - int argv = computedNames; - - const QV4::CompiledData::Class *cls = function()->v4Function()->compilationUnit->unitData() - ->classAt(classIndex); - const CompiledData::Method *methods = cls->methodTable(); - for (uint i = 0; i < cls->nStaticMethods + cls->nMethods; ++i) { - if (methods[i].name == std::numeric_limits<unsigned>::max()) - ++argc; - } - - VarArgNodes args; - args.append(createConstant(classIndex)); - args.append(env()->slot(heritage)); - finalizeCall(Meta::JSCreateClass, args, argc, argv); -} - -void GraphBuilder::generate_CreateMappedArgumentsObject() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSCreateMappedArgumentsObject>())); -} - -void GraphBuilder::generate_CreateUnmappedArgumentsObject() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSCreateUnmappedArgumentsObject>())); -} - -void GraphBuilder::generate_CreateRestParameter(int argIndex) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSCreateRestParameter>(), - createConstant(argIndex))); -} - -void GraphBuilder::generate_ConvertThisToObject() -{ - Node* control = createNode(opBuilder()->get<Meta::JSThisToObject>(), - env()->slot(CallData::This)); - env()->bindNodeToSlot(control, CallData::This); -} - -void GraphBuilder::generate_LoadSuperConstructor() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSLoadSuperConstructor>(), - env()->slot(CallData::Function))); -} - -void GraphBuilder::generate_ToObject() -{ - bindAcc(createNode(opBuilder()->get<Meta::ToObject>(), - env()->accumulator())); -} - -void GraphBuilder::generate_CallWithSpread(int func, int thisObject, int argc, int argv, - int /*traceSlot*/) -{ - VarArgNodes args; - args.append(env()->slot(func)); - args.append(env()->slot(thisObject)); - finalizeCall(Meta::JSCallWithSpread, args, argc, argv); -} - -void GraphBuilder::generate_TailCall(int func, int thisObject, int argc, int argv) -{ - VarArgNodes args; - args.append(env()->slot(func)); - args.append(env()->slot(thisObject)); - populate(args, argc, argv); - Node *n = createAndLinkNode(opBuilder()->getJSTailCall(uint16_t(args.size())), args.data(), - size_t(args.size())); - queueFunctionExit(n); -} - -void GraphBuilder::generate_Construct(int func, int argc, int argv) -{ - VarArgNodes args; - args.append(env()->slot(func)); - args.append(env()->accumulator()); - finalizeCall(Meta::JSConstruct, args, argc, argv); -} - -void GraphBuilder::generate_ConstructWithSpread(int func, int argc, int argv) -{ - VarArgNodes args; - args.append(env()->slot(func)); - args.append(env()->accumulator()); - finalizeCall(Meta::JSConstructWithSpread, args, argc, argv); -} - -void GraphBuilder::generate_Jump(int offset) -{ - auto jumpTarget = absoluteOffset(offset); - mergeIntoSuccessor(jumpTarget); -} - -void GraphBuilder::generate_JumpTrue(int /*traceSlot*/, int offset) -{ - createNode(opBuilder()->get<Meta::Branch>(), createToBoolean(env()->accumulator())); - - { - InterpreterSubEnvironment subEnvironment(this); - auto jumpTarget = absoluteOffset(offset); - createIfTrue(); - mergeIntoSuccessor(jumpTarget); - } - - createIfFalse(); -} - -void GraphBuilder::generate_JumpFalse(int traceSlot, int offset) -{ - generate_JumpFalse(env()->accumulator(), traceSlot, offset); -} - -void GraphBuilder::generate_JumpFalse(Node *condition, int /*traceSlot*/, int offset) -{ - createNode(opBuilder()->get<Meta::Branch>(), createToBoolean(condition)); - - { - InterpreterSubEnvironment subEnvironment(this); - auto jumpTarget = absoluteOffset(offset); - createIfFalse(); - mergeIntoSuccessor(jumpTarget); - } - - createIfTrue(); -} - -void GraphBuilder::generate_JumpNoException(int offset) -{ - auto e = createNode(opBuilder()->get<Meta::HasException>(), graph()->engineNode()); - createNode(opBuilder()->get<Meta::Branch>(), e); - - { - InterpreterSubEnvironment subEnvironment(this); - auto jumpTarget = absoluteOffset(offset); - createIfFalse(); - mergeIntoSuccessor(jumpTarget); - } - - createIfTrue(); -} - -void GraphBuilder::generate_JumpNotUndefined(int offset) -{ - Node *condition = createNode(opBuilder()->get<Meta::JSStrictEqual>(), - env()->accumulator(), - graph()->undefinedNode()); - generate_JumpFalse(condition, NoTraceSlot, offset); -} - -void GraphBuilder::generate_CmpEqNull() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSEqual>(), - env()->accumulator(), - graph()->nullNode())); -} - -void GraphBuilder::generate_CmpNeNull() -{ - generate_CmpEqNull(); - bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(), - env()->accumulator())); -} - -void GraphBuilder::generate_CmpEqInt(int lhs) -{ - auto left = createConstant(lhs); - Node* control = createNode(opBuilder()->get<Meta::JSEqual>(), - left, - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpNeInt(int lhs) -{ - generate_CmpEqInt(lhs); - bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(), - env()->accumulator())); -} - -void GraphBuilder::generate_CmpEq(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSEqual>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpNe(int lhs) -{ - generate_CmpEq(lhs); - bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(), - env()->accumulator())); -} - -void GraphBuilder::generate_CmpGt(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSGreaterThan>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpGe(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSGreaterEqual>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpLt(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSLessThan>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpLe(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSLessEqual>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpStrictEqual(int lhs) -{ - Node* control = createNode(opBuilder()->get<Meta::JSStrictEqual>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_CmpStrictNotEqual(int lhs) -{ - generate_CmpStrictEqual(lhs); - bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(), - env()->accumulator())); -} - -void GraphBuilder::generate_CmpIn(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSIn>(), env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_CmpInstanceOf(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSInstanceOf>(), env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_UNot() -{ - bindAcc(createNode(opBuilder()->get<Meta::BooleanNot>(), - createToBoolean(env()->accumulator()))); -} - -void GraphBuilder::generate_UPlus(int /*traceSlot*/) -{ - Node* control = createNode(opBuilder()->get<Meta::JSToNumber>(), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_UMinus(int /*traceSlot*/) -{ - Node* control = createNode(opBuilder()->get<Meta::JSNegate>(), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_UCompl() -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitXor>(), - env()->accumulator(), - createConstant(-1))); -} - -void GraphBuilder::generate_Increment(int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSAdd>(), - env()->accumulator(), - createConstant(1))); -} - - -void GraphBuilder::generate_Decrement(int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSSubtract>(), - env()->accumulator(), - createConstant(1))); -} - -void GraphBuilder::generate_Add(int lhs, int /*traceSlot*/) -{ - Node* control = createNode(opBuilder()->get<Meta::JSAdd>(), - env()->slot(lhs), - env()->accumulator()); - bindAcc(control); -} - -void GraphBuilder::generate_BitAnd(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitAnd>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_BitOr(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitOr>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_BitXor(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitXor>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_UShr(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSUnsignedShiftRight>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Shr(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSShiftRight>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Shl(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSShiftLeft>(), - env()->slot(lhs), - env()->accumulator())); -} - - -void GraphBuilder::generate_BitAndConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitAnd>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_BitOrConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitOr>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_BitXorConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSBitXor>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_UShrConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSUnsignedShiftRight>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_ShrConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSShiftRight>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_ShlConst(int rhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSShiftLeft>(), - env()->accumulator(), - createConstant(rhs))); -} - -void GraphBuilder::generate_Exp(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSExponentiate>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Mul(int lhs, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSMultiply>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Div(int lhs) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSDivide>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Mod(int lhs, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSModulo>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_Sub(int lhs, int /*traceSlot*/) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSSubtract>(), - env()->slot(lhs), - env()->accumulator())); -} - -void GraphBuilder::generate_InitializeBlockDeadTemporalZone(int firstReg, int count) -{ - for (int reg = firstReg; reg < firstReg + count; ++reg) - env()->bindNodeToSlot(graph()->emptyNode(), reg); -} - -void GraphBuilder::generate_ThrowOnNullOrUndefined() -{ - createNode(opBuilder()->get<Meta::JSThrowOnNullOrUndefined>(), - env()->accumulator()); -} - -void GraphBuilder::generate_GetTemplateObject(int index) -{ - bindAcc(createNode(opBuilder()->get<Meta::JSGetTemplateObject>(), - createConstant(index))); -} - -GraphBuilder::Verdict GraphBuilder::startInstruction(Moth::Instr::Type /*instr*/) -{ - // This handles a couple of cases on how flow control can end up at this instruction. - - const auto off = currentInstructionOffset(); - if (auto newEnv = m_envForOffset[off]) { - // Ok, there was a jump from before to this point (which registered an environment), so we - // have two options: - if (env() != newEnv && env() != nullptr) { - // There is a current environment different from the environment active when we took the - // jump. This happens with e.g. an if-then-else: - // - // acc = condition - // JumpFalse else-block - // ... then block - // Jump end-if - // else-block: - // ... else block - // end-if: - // .. some instruction <--- we're here - // - // in that case we merge the after-else environment into the after-then environment: - newEnv->merge(env()); - } else { - // There is not a current environment. This can happen with e.g. a loop: - // loop-start: - // acc = condition - // JumpFalse loop-end - // ... loop body - // Jump loop-start - // loop-end: - // .... some instruction <--- we're here - // - // The last jump of the loop will clear the environment, so at this point we only have - // the environment registered by the JumpFalse. This is the asy case: no merges, just - // take the registered environment unchanged. - } - - // Leave the merged environment as-is, and continue with a copy. We cannot change the - // registered environment in case this point also happens to be a loop start. - setEnv(newEnv->copy()); - } - - if (env() == nullptr) { - // Ok, there is no environment, meaning nobody jumped to this instruction, and the previous - // instruction doesn't let control flow end up here. So, this is dead code. - // This can happen for JS like: - // - // if (condition) { - // return something - // } else { - // return somethingElse - // } - // someCode <--- we're here - return SkipInstruction; - } - - const LabelInfo *info = isLoopStart(off); - if (info && env()) { - // Ok, this instruction is the start of a loop, meaning there will be a jump backwards to - // this point. Make sure there is a Region node with Phi nodes here. - handleLoopStart(*info); - } - - return ProcessInstruction; -} - -void GraphBuilder::endInstruction(Moth::Instr::Type /*instr*/) {} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4graphbuilder_p.h b/src/qml/jit/qv4graphbuilder_p.h deleted file mode 100644 index 450d8640b7..0000000000 --- a/src/qml/jit/qv4graphbuilder_p.h +++ /dev/null @@ -1,298 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4GRAPHBUILDER_P_H -#define QV4GRAPHBUILDER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4global_p.h> -#include <private/qv4bytecodehandler_p.h> -#include <private/qv4ir_p.h> -#include "qv4graph_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -// The graph builder walks the byte-code, and produces a graph. The graph is a digraph, where the -// nodes have operations, and the edges are dependencies (or inputs). -class GraphBuilder: protected Moth::ByteCodeHandler -{ - Q_DISABLE_COPY_MOVE(GraphBuilder) - - enum { NoTraceSlot = -1 }; - - struct LabelInfo { //### extend this to also capture the amount of slots that are live - LabelInfo() = default; - LabelInfo(unsigned label) : labelOffset(label) {} - unsigned labelOffset = 0; - }; - -public: - static void buildGraph(IR::Function *function); - - class InterpreterEnvironment; - - void setEnv(InterpreterEnvironment *newEnv) - { m_currentEnv = newEnv; } - - InterpreterEnvironment *env() const - { return m_currentEnv; } - -private: - GraphBuilder(IR::Function *function); - ~GraphBuilder() override = default; - - void startGraph(); - void endGraph(); - - Node *bindAcc(Node *n); - Node *createAndLinkNode(Operation *op, Node *operands[], size_t opCount, bool incomplete = false); - Node *createNode(Operation *op, bool incomplete = false); - Node *createNode(Operation *op, Node *n1); - Node *createNode(Operation *op, Node *n1, Node *n2); - Node *createNode(Operation *op, Node *n1, Node *n2, Node *n3); - Node *createNode(Operation *op, Node *n1, Node *n2, Node *n3, Node *n4); - Node *createRegion(unsigned nControlInputs); - Node *createIfTrue(); - Node *createIfFalse(); - Node *createConstant(int v); - Node *createPhi(unsigned nInputs, Node *input, Node *control); - Node *createEffectPhi(unsigned nInputs, Node *input, Node *control); - Node *createHandleUnwind(int offset); - Node *mergeControl(Node *c1, Node *c2); - Node *mergeEffect(Node *e1, Node *e2, Node *control); - Node *mergeValue(Node *v1, Node *v2, Node *control); - - Node *createToBoolean(Node *input); - - using VarArgNodes = QVarLengthArray<Node *, 32>; - void populate(VarArgNodes &args, int argc, int argv); - - void queueFunctionExit(Node *exitNode); - - Function *function() const - { return m_func; } - - Graph *graph() - { return m_graph; } - - Node *mergeIntoSuccessor(int offset); - - OperationBuilder *opBuilder() const - { return m_graph->opBuilder(); } - - int absoluteOffset(int offset) const - { return offset + nextInstructionOffset(); } - - const LabelInfo *labelInfoAt(unsigned offset) const; - const LabelInfo *isLoopStart(unsigned offset) const; - void handleLoopStart(const LabelInfo &labelInfo); - void startUnwinding(); - -protected: // ByteCodeHandler - void generate_Ret() override; - void generate_Debug() override; - void generate_LoadConst(int index) override; - void generate_LoadZero() override; - void generate_LoadTrue() override; - void generate_LoadFalse() override; - void generate_LoadNull() override; - void generate_LoadUndefined() override; - void generate_LoadInt(int value) override; - void generate_MoveConst(int constIndex, int destTemp) override; - void generate_LoadReg(int reg) override; - void generate_StoreReg(int reg) override; - void generate_MoveReg(int srcReg, int destReg) override; - void generate_LoadImport(int index) override; - void generate_LoadLocal(int index, int traceSlot) override; - void generate_StoreLocal(int index) override; - void generate_LoadScopedLocal(int scope, int index, int traceSlot) override; - void generate_StoreScopedLocal(int scope, int index) override; - void generate_LoadRuntimeString(int stringId) override; - void generate_MoveRegExp(int regExpId, int destReg) override; - void generate_LoadClosure(int value) override; - void generate_LoadName(int name, int traceSlot) override; - void generate_LoadGlobalLookup(int index, int traceSlot) override; - void generate_StoreNameSloppy(int name) override; - void generate_StoreNameStrict(int name) override; - void generate_LoadElement(int base, int traceSlot) override; - void generate_StoreElement(int base, int index, int traceSlot) override; - void generate_LoadProperty(int name, int traceSlot) override; - void generate_GetLookup(int index, int traceSlot) override; - void generate_StoreProperty(int name, int base) override; - void generate_SetLookup(int index, int base) override; - void generate_LoadSuperProperty(int property) override; - void generate_StoreSuperProperty(int property) override; - void generate_LoadQmlContextPropertyLookup(int property, int traceSlot) override; - void generate_Yield() override; - void generate_YieldStar() override; - void generate_Resume(int offset) override; - void finalizeCall(Operation::Kind kind, VarArgNodes &args, int argc, int argv); - void generate_CallValue(int name, int argc, int argv, int traceSlot) override; - void generate_CallWithReceiver(int name, int thisObject, int argc, int argv, - int traceSlot) override; - void generate_CallProperty(int name, int base, int argc, int argv, int traceSlot) override; - void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, - int traceSlot) override; - void generate_CallElement(int base, int index, int argc, int argv, int traceSlot) override; - void generate_CallName(int name, int argc, int argv, int traceSlot) override; - void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override; - void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override; - void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override; - void generate_SetUnwindHandler(int offset) override; - void generate_UnwindDispatch() override; - void generate_UnwindToLabel(int level, int offset) override; - void generate_DeadTemporalZoneCheck(int name) override; - void generate_ThrowException() override; - void generate_GetException() override; - void generate_SetException() override; - void generate_CreateCallContext() override; - void generate_PushCatchContext(int index, int name) override; - void generate_PushWithContext() override; - void generate_PushBlockContext(int index) override; - void generate_CloneBlockContext() override; - void generate_PushScriptContext(int index) override; - void generate_PopScriptContext() override; - void generate_PopContext() override; - void generate_GetIterator(int iterator) override; - void generate_IteratorNextAndFriends_TrailingStuff(Node *iterationNode, int resultSlot); - void generate_IteratorNext(int value, int done) override; - void generate_IteratorNextForYieldStar(int iterator, int object) override; - void generate_IteratorClose(int done) override; - void generate_DestructureRestElement() override; - void generate_DeleteProperty(int base, int index) override; - void generate_DeleteName(int name) override; - void generate_TypeofName(int name) override; - void generate_TypeofValue() override; - void generate_DeclareVar(int varName, int isDeletable) override; - void generate_DefineArray(int argc, int argv) override; - void generate_DefineObjectLiteral(int internalClassId, int argc, int argv) override; - void generate_CreateClass(int classIndex, int heritage, int computedNames) override; - void generate_CreateMappedArgumentsObject() override; - void generate_CreateUnmappedArgumentsObject() override; - void generate_CreateRestParameter(int argIndex) override; - void generate_ConvertThisToObject() override; - void generate_LoadSuperConstructor() override; - void generate_ToObject() override; - void generate_CallWithSpread(int func, int thisObject, int argc, int argv, - int traceSlot) override; - void generate_TailCall(int func, int thisObject, int argc, int argv) override; - void generate_Construct(int func, int argc, int argv) override; - void generate_ConstructWithSpread(int func, int argc, int argv) override; - void generate_Jump(int offset) override; - void generate_JumpTrue(int traceSlot, int offset) override; - void generate_JumpFalse(int traceSlot, int offset) override; - void generate_JumpFalse(Node *condition, int traceSlot, int offset); - void generate_JumpNoException(int offset) override; - void generate_JumpNotUndefined(int offset) override; - void generate_CmpEqNull() override; - void generate_CmpNeNull() override; - void generate_CmpEqInt(int lhs) override; - void generate_CmpNeInt(int lhs) override; - void generate_CmpEq(int lhs) override; - void generate_CmpNe(int lhs) override; - void generate_CmpGt(int lhs) override; - void generate_CmpGe(int lhs) override; - void generate_CmpLt(int lhs) override; - void generate_CmpLe(int lhs) override; - void generate_CmpStrictEqual(int lhs) override; - void generate_CmpStrictNotEqual(int lhs) override; - void generate_CmpIn(int lhs) override; - void generate_CmpInstanceOf(int lhs) override; - void generate_UNot() override; - void generate_UPlus(int traceSlot) override; - void generate_UMinus(int traceSlot) override; - void generate_UCompl() override; - void generate_Increment(int traceSlot) override; - void generate_Decrement(int traceSlot) override; - void generate_Add(int lhs, int traceSlot) override; - void generate_BitAnd(int lhs) override; - void generate_BitOr(int lhs) override; - void generate_BitXor(int lhs) override; - void generate_UShr(int lhs) override; - void generate_Shr(int lhs) override; - void generate_Shl(int lhs) override; - void generate_BitAndConst(int rhs) override; - void generate_BitOrConst(int rhs) override; - void generate_BitXorConst(int rhs) override; - void generate_UShrConst(int rhs) override; - void generate_ShrConst(int rhs) override; - void generate_ShlConst(int rhs) override; - void generate_Exp(int lhs) override; - void generate_Mul(int lhs, int traceSlot) override; - void generate_Div(int lhs) override; - void generate_Mod(int lhs, int traceSlot) override; - void generate_Sub(int lhs, int traceSlot) override; - void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; - void generate_ThrowOnNullOrUndefined() override; - void generate_GetTemplateObject(int index) override; - - Verdict startInstruction(Moth::Instr::Type instr) override; - void endInstruction(Moth::Instr::Type instr) override; - -private: - IR::Function *m_func; - Graph *m_graph; - InterpreterEnvironment *m_currentEnv; - std::vector<Node *> m_exitControls; - QHash<int, InterpreterEnvironment *> m_envForOffset; - std::vector<LabelInfo> m_labelInfos; - int m_currentUnwindHandlerOffset = 0; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4GRAPHBUILDER_P_H diff --git a/src/qml/jit/qv4ir.cpp b/src/qml/jit/qv4ir.cpp deleted file mode 100644 index cb3eeeec60..0000000000 --- a/src/qml/jit/qv4ir.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <private/qqmlglobal_p.h> -#include "qv4ir_p.h" -#include "qv4node_p.h" -#include "qv4function_p.h" -#include <qv4graph_p.h> -#include "qv4stackframe_p.h" -#include "qv4operation_p.h" -#include "qv4util_p.h" - -#include <QtCore/qloggingcategory.h> -#include <QtCore/qjsonobject.h> -#include <QtCore/qjsonarray.h> -#include <QtCore/qfile.h> - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcJsonIR, "qt.v4.ir.json"); -Q_LOGGING_CATEGORY(lcDotIR, "qt.v4.ir.dot"); -Q_LOGGING_CATEGORY(lcVerify, "qt.v4.ir.verify"); - -Function::Function(QV4::Function *qv4Function) - : qv4Function(qv4Function) - , m_graph(Graph::create(this)) - , m_dumper(nullptr) - , m_nodeInfo(128, nullptr) -{ -} - -Function::~Function() -{ - delete m_dumper; -} - -QString Function::name() const -{ - QString name; - if (auto n = v4Function()->name()) - name = n->toQString(); - if (name.isEmpty()) - name = QString::asprintf("%p", v4Function()); - auto loc = v4Function()->sourceLocation(); - return name + QStringLiteral(" (%1:%2:%3)").arg(loc.sourceFile, QString::number(loc.line), - QString::number(loc.column)); -} - -void Function::dump(const QString &description) const -{ - Dumper::dump(this, description); -} - -void Function::dump() const -{ - dump(QStringLiteral("Debug:")); -} - -Dumper *Function::dumper() const -{ - if (!m_dumper) - m_dumper = new Dumper(this); - return m_dumper; -} - -Function::StringId Function::addString(const QString &s) -{ - m_stringPool.push_back(s); - return m_stringPool.size() - 1; -} - -NodeInfo *Function::nodeInfo(Node *n, bool createIfNecessary) const -{ - if (n->id() >= m_nodeInfo.size()) - m_nodeInfo.resize(n->id() * 2, nullptr); - - NodeInfo *&info = m_nodeInfo[n->id()]; - if (info == nullptr && createIfNecessary) { - info = m_pool.New<NodeInfo>(); - info->setType(n->operation()->type()); - } - return info; -} - -void Function::copyBytecodeOffsets(Node *from, Node *to) -{ - auto toInfo = nodeInfo(to); - if (auto fromInfo = nodeInfo(from)) { - toInfo->setBytecodeOffsets(fromInfo->currentInstructionOffset(), - fromInfo->nextInstructionOffset()); - } -} - -Dumper::Dumper(const Function *f) -{ - if (!f) - return; -} - -void Dumper::dump(const Function *f, const QString &description) -{ - if (false && lcJsonIR().isDebugEnabled()) { - Dumper *dumper = f->dumper(); - - qCDebug(lcJsonIR).noquote().nospace() << description + QLatin1String(":\n"); - for (const auto &line : dumper->dump(f).split('\n')) - qCDebug(lcJsonIR).noquote().nospace() << line; - } - - if (lcDotIR().isDebugEnabled()) - dot(f, description); -} - -QByteArray Dumper::dump(const Function *f) -{ - QJsonObject fo; - - { - QString name; - if (auto n = f->v4Function()->name()) - name = n->toQString(); - fo[QLatin1String("_searchKey")] = QStringLiteral("function %1").arg(name); - if (name.isEmpty()) - name = QString::asprintf("%p", f->v4Function()); - fo[QLatin1String("name")] = name; - } - - auto loc = f->v4Function()->sourceLocation(); - fo[QLatin1String("source")] = loc.sourceFile; - fo[QLatin1String("line")] = loc.line; - fo[QLatin1String("column")] = loc.column; - - { - QJsonArray gn; - QJsonArray ge; - NodeCollector nodes(f->graph(), /*collectUses =*/ true); - nodes.sortById(); - for (Node *n : nodes.reachable()) { - gn.append(dump(n, f)); - int inputIndex = 0; - for (Node *input : n->inputs()) { - QJsonObject edge; - edge[QLatin1String("from")] = int(input->id()); - edge[QLatin1String("to")] = int(n->id()); - edge[QLatin1String("index")] = inputIndex; - if (inputIndex < n->operation()->valueInputCount()) { - edge[QLatin1String("type")] = QLatin1String("value"); - } else if (inputIndex < n->operation()->valueInputCount() - + n->operation()->effectInputCount()) { - edge[QLatin1String("type")] = QLatin1String("effect"); - } else { - edge[QLatin1String("type")] = QLatin1String("control"); - } - Q_ASSERT(inputIndex < n->operation()->valueInputCount() - + n->operation()->effectInputCount() - + n->operation()->controlInputCount()); - ge.append(edge); - ++inputIndex; - } - } - QJsonObject g; - g[QLatin1String("nodes")] = gn; - g[QLatin1String("edges")] = ge; - fo[QLatin1String("graph")] = g; - } - - m_doc.setObject(fo); - return m_doc.toJson(QJsonDocument::Indented); -} - -QJsonValue toJSonValue(QV4::Value v) -{ - switch (v.type()) { - case QV4::Value::Undefined_Type: return QJsonValue(QJsonValue::Undefined); - case QV4::Value::Null_Type: return QJsonValue(QJsonValue::Null); - case QV4::Value::Boolean_Type: return QJsonValue(v.booleanValue()); - case QV4::Value::Integer_Type: return QJsonValue(v.int_32()); - case QV4::Value::Managed_Type: - if (String *s = v.stringValue()) - return QJsonValue(s->toQString()); - else - return QJsonValue(QLatin1String("<managed>")); - default: return QJsonValue(v.doubleValue()); - } -} - -QJsonValue Dumper::dump(const Node * const node, const Function *f) -{ - QJsonObject n; - n[QLatin1String("id")] = int(node->id()); - n[QLatin1String("kind")] = node->operation()->debugString(); - switch (node->operation()->kind()) { - case Meta::Parameter: { - auto info = ParameterPayload::get(*node->operation()); - n[QLatin1String("name")] = f->string(info->stringId()); - n[QLatin1String("index")] = int(info->parameterIndex()); - break; - } - case Meta::Constant: { - auto info = ConstantPayload::get(*node->operation()); - n[QLatin1String("value")] = toJSonValue(info->value()); - break; - } - default: - break; - } - return n; -} - -void Dumper::dot(const Function *f, const QString &description) -{ - static const bool skipFramestate = qEnvironmentVariableIsSet("QV4_JIT_DOT_SKIP_FRAMESTATE"); - - auto node = [](Node *n) { - return QStringLiteral("n%1[label=\"%1: %2%3\"];\n").arg(QString::number(n->id()), - n->operation()->debugString(), - n->isDead() ? QStringLiteral(" (dead)") - : QString()); - }; - - Graph *g = f->graph(); - QString out; - out += QLatin1Char('\n'); - out += QStringLiteral("digraph{root=\"n%1\" label=\"%2\";" - "node[shape=rect];" - "edge[dir=back fontsize=10];\n") - .arg(g->startNode()->id()) - .arg(description); - out += node(g->startNode()); - const bool dumpUses = false; // set to true to see all nodes - NodeCollector nodes(g, dumpUses, skipFramestate); - for (Node *n : nodes.reachable()) { - if (n == g->startNode()) - continue; - - out += node(n); - - unsigned inputIndex = 0; - for (Node *input : n->inputs()) { - if (input == nullptr) - continue; - out += QStringLiteral("n%2->n%1[style=").arg(QString::number(n->id()), - QString::number(input->id())); - if (inputIndex < n->operation()->valueInputCount() || - inputIndex == n->operation()->indexOfFrameStateInput()) { - out += QStringLiteral("solid headlabel=\"%1\"").arg(inputIndex); - } else if (inputIndex < unsigned(n->operation()->valueInputCount() - + n->operation()->effectInputCount())) { - out += QStringLiteral("dotted headlabel=\"%1\"").arg(inputIndex); - } else { - out += QStringLiteral("dashed headlabel=\"%1\"").arg(inputIndex); - } - out += QStringLiteral("];\n"); - ++inputIndex; - } - } - out += QStringLiteral("}\n"); - qCDebug(lcDotIR).nospace().noquote() << out; - - QFile of(description + QStringLiteral(".dot")); - of.open(QIODevice::WriteOnly); - of.write(out.toUtf8()); - of.close(); -} - -void Function::verify() const -{ -#ifndef QT_NO_DEBUG - unsigned problemsFound = 0; - - auto verifyNodeAgainstOperation = [&problemsFound](const Node *n) { - const Operation *op = n->operation(); - if (op->totalInputCount() != n->inputCount()) { - ++problemsFound; - qCDebug(lcVerify()) << "Node" << n->id() << "has" << n->inputCount() - << "inputs, but it's operation" << op->debugString() - << "requires" << op->totalInputCount() << "inputs"; - } - - if (n->opcode() == Meta::Phi || n->opcode() == Meta::EffectPhi) { - if (n->controlInput()->opcode() != Meta::Region) { - ++problemsFound; - qCDebug(lcVerify()) << "Control input of phi node" << n->id() << "is not a region"; - } - if (n->controlInput()->inputCount() + 1 != n->inputCount()) { - ++problemsFound; - qCDebug(lcVerify()) << "Control input of phi node" << n->id() - << "has" << n->controlInput()->inputCount() - << "inputs while phi node has" << n->inputCount() - << "inputs"; - } - } - - //### todo: verify outputs: value outputs are allowed to be unused, but the effect and - // control outputs have to be linked up, except: - //### todo: verify if no use is a nullptr, except for operations that can throw, where the - // last one is allowed to be a nullptr when an unwind handler is missing. - }; - - NodeWorkList todo(graph()); - todo.enqueue(graph()->endNode()); - while (Node *n = todo.dequeueNextNodeForVisiting()) { - todo.enqueueAllInputs(n); - todo.enqueueAllUses(n); - - verifyNodeAgainstOperation(n); - } - //### TODO: - if (problemsFound != 0) { - dump(QStringLiteral("Problematic graph")); - qFatal("Found %u problems during graph verification!", problemsFound); - } -#endif // QT_NO_xDEBUG -} - -QString Type::debugString() const -{ - if (isNone()) - return QStringLiteral("none"); - if (isInvalid()) - return QStringLiteral("invalid"); - - QStringList s; - if (m_t & Bool) - s += QStringLiteral("boolean"); - if (m_t & Int32) - s += QStringLiteral("int32"); - if (m_t & Double) - s += QStringLiteral("double"); - if (m_t & Undefined) - s += QStringLiteral("undefined"); - if (m_t & Null) - s += QStringLiteral("null"); - if (m_t & Empty) - s += QStringLiteral("empty"); - if (m_t & RawPointer) - s += QStringLiteral("raw pointer"); - - return s.join(QLatin1String(" ")); -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4ir_p.h b/src/qml/jit/qv4ir_p.h deleted file mode 100644 index e21a80528d..0000000000 --- a/src/qml/jit/qv4ir_p.h +++ /dev/null @@ -1,228 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4IR_P_H -#define QV4IR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4function_p.h> -#include <QtCore/qjsondocument.h> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class Dumper; -class Graph; - -class Node; -class NodeInfo; - -class Function -{ - Q_DISABLE_COPY_MOVE(Function) -public: - Function(QV4::Function *qv4Function); - ~Function(); - - void verify() const; - - QV4::Function *v4Function() const - { return qv4Function; } - - QString name() const; - - QQmlJS::MemoryPool *pool() - { return &m_pool; } - - Graph *graph() const - { return m_graph; } - - void dump(const QString &description) const; - void dump() const; // for calling in the debugger - Dumper *dumper() const; - - using StringId = size_t; - StringId addString(const QString &s); - QString string(StringId id) const - { return m_stringPool[id]; } - - NodeInfo *nodeInfo(Node *n, bool createIfNecessary = true) const; - void copyBytecodeOffsets(Node *from, Node *to); - - void addUnwindLabelOffset(int absoluteOffset) - { m_unwindLabelOffsets.push_back(absoluteOffset); } - - const std::vector<int> &unwindLabelOffsets() const - { return m_unwindLabelOffsets; } - -private: - QV4::Function *qv4Function; - mutable QQmlJS::MemoryPool m_pool; - Graph *m_graph; - mutable Dumper *m_dumper; - std::vector<QString> m_stringPool; - mutable std::vector<NodeInfo *> m_nodeInfo; //### move the into the _pool - std::vector<int> m_unwindLabelOffsets; -}; - -class Dumper -{ - Q_DISABLE_COPY_MOVE(Dumper) - -public: - Dumper(const Function *f); - ~Dumper() = default; - - static void dump(const Function *f, const QString &description); - static void dot(const Function *f, const QString &description); - -private: - QByteArray dump(const Function *f); - QJsonValue dump(const Node *node, const Function *f); - -private: - QJsonDocument m_doc; -}; - -class Type -{ - // None is for nodes with no type (e.g. a Return) - // The others form a lattice: - // Any -> Object -> Invalid - // ^^^ -> Number -> Integral -> Int32 -> ^^^^^^^ - // ^^^ -> Number -> Integral -> UInt32 -> ^^^^^^^ - // ^^^ -> Number -> Integral -> Bool -> ^^^^^^^ - // ^^^ -> Number -> Double -> ^^^^^^^ - // ^^^ -> Undefined -> ^^^^^^^ - // ^^^ -> Null -> ^^^^^^^ - // ^^^ -> Empty -> ^^^^^^^ - enum InternalType: int16_t { - None = 0, - - Object = 1 << 0, - Bool = 1 << 1, - Int32 = 1 << 2, - UInt32 = 1 << 3, - Double = 1 << 4, - Undefined = 1 << 5, - Null = 1 << 6, - Empty = 1 << 7, - RawPointer = 1 << 8, - Invalid = -1, - - Integral = Int32 | UInt32 | Bool, - Number = Integral | Double, - Any = Object | Number | Undefined | Empty | Null, - }; - - Type(InternalType t) : m_t(t) {} - -public: - Type() = default; - - bool operator==(const Type &other) const - { return m_t == other.m_t; } - - static Type noneType() { return Type(None); } - static Type anyType() { return Type(Any); } - static Type undefinedType() { return Type(Undefined); } - static Type emptyType() { return Type(Empty); } - static Type booleanType() { return Type(Bool); } - static Type int32Type() { return Type(Int32); } - static Type doubleType() { return Type(Double); } - static Type numberType() { return Type(Number); } - static Type nullType() { return Type(Null); } - static Type objectType() { return Type(Object); } - static Type rawPointerType() { return Type(RawPointer); } - - bool isAny() const { return m_t == Any; } - bool isBoolean() const { return m_t == Bool; } - bool isInt32() const { return m_t == Int32; } - bool isInvalid() const { return m_t == Invalid; } - bool isNone() const { return m_t == None; } - bool isDouble() const { return m_t == Double; } - bool isUndefined() const { return m_t == Undefined; } - bool isNull() const { return m_t == Null; } - bool isEmpty() const { return m_t == Empty; } - bool isObject() const { return m_t == Object; } - bool isRawPointer() const { return m_t == RawPointer; } - bool isIntegral() const { return matches(Integral); } - bool isNumber() const { return matches(Number); } - - Type operator|(Type other) const - { return Type(InternalType(int16_t(m_t) | int16_t(other.m_t))); } - - Type &operator|=(Type other) - { - m_t = (InternalType(int16_t(m_t) | int16_t(other.m_t))); - return *this; - } - - QString debugString() const; - -private: - bool matches(InternalType it) const - { - return (m_t & ~it) == 0 && (m_t & it) != 0; - } - -private: - InternalType m_t = None; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4IR_P_H diff --git a/src/qml/jit/qv4loopinfo.cpp b/src/qml/jit/qv4loopinfo.cpp deleted file mode 100644 index 0366c49e30..0000000000 --- a/src/qml/jit/qv4loopinfo.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> - -#include "qv4loopinfo_p.h" -#include "qv4domtree_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcLoopinfo, "qt.v4.ir.loopinfo") - -void LoopInfo::detectLoops() -{ - blockInfos.resize(dt.function()->blockCount()); - - std::vector<MIBlock *> backedges; - backedges.reserve(4); - - const auto order = dt.calculateDFNodeIterOrder(); - for (MIBlock *bb : order) { - if (bb->isDeoptBlock()) - continue; - - backedges.clear(); - - for (MIBlock *pred : bb->inEdges()) { - if (bb == pred || dt.dominates(bb->index(), pred->index())) - backedges.push_back(pred); - } - - if (!backedges.empty()) - subLoop(bb, backedges); - } - - collectLoopExits(); - - dump(); -} - -void LoopInfo::collectLoopExits() -{ - for (MIBlock::Index i = 0, ei = MIBlock::Index(blockInfos.size()); i != ei; ++i) { - BlockInfo &bi = blockInfos[i]; - MIBlock *currentBlock = dt.function()->block(i); - if (bi.isLoopHeader) { - for (MIBlock *outEdge : currentBlock->outEdges()) { - if (outEdge != currentBlock && !inLoopOrSubLoop(outEdge, currentBlock)) - bi.loopExits.push_back(outEdge); - } - } - if (MIBlock *containingLoop = bi.loopHeader) { - BlockInfo &loopInfo = blockInfos[containingLoop->index()]; - for (MIBlock *outEdge : currentBlock->outEdges()) { - if (outEdge != containingLoop && !inLoopOrSubLoop(outEdge, containingLoop)) - loopInfo.loopExits.push_back(outEdge); - } - } - } -} - -bool LoopInfo::inLoopOrSubLoop(MIBlock *block, MIBlock *loopHeader) const -{ - const BlockInfo &bi = blockInfos[block->index()]; - MIBlock *loopHeaderForBlock = bi.loopHeader; - if (loopHeaderForBlock == nullptr) - return false; // block is not in any loop - - while (loopHeader) { - if (loopHeader == loopHeaderForBlock) - return true; - // look into the parent loop of loopHeader to see if block is contained there - loopHeader = blockInfos[loopHeader->index()].loopHeader; - } - - return false; -} - -void LoopInfo::subLoop(MIBlock *loopHead, const std::vector<MIBlock *> &backedges) -{ - blockInfos[loopHead->index()].isLoopHeader = true; - - std::vector<MIBlock *> worklist; - worklist.reserve(backedges.size() + 8); - worklist.insert(worklist.end(), backedges.begin(), backedges.end()); - while (!worklist.empty()) { - MIBlock *predIt = worklist.back(); - worklist.pop_back(); - - MIBlock *subloop = blockInfos[predIt->index()].loopHeader; - if (subloop) { - // This is a discovered block. Find its outermost discovered loop. - while (MIBlock *parentLoop = blockInfos[subloop->index()].loopHeader) - subloop = parentLoop; - - // If it is already discovered to be a subloop of this loop, continue. - if (subloop == loopHead) - continue; - - // Yay, it's a subloop of this loop. - blockInfos[subloop->index()].loopHeader = loopHead; - predIt = subloop; - - // Add all predecessors of the subloop header to the worklist, as long as - // those predecessors are not in the current subloop. It might be the case - // that they are in other loops, which we will then add as a subloop to the - // current loop. - for (MIBlock *predIn : predIt->inEdges()) - if (blockInfos[predIn->index()].loopHeader != subloop) - worklist.push_back(predIn); - } else { - if (predIt == loopHead) - continue; - - // This is an undiscovered block. Map it to the current loop. - blockInfos[predIt->index()].loopHeader = loopHead; - - // Add all incoming edges to the worklist. - for (MIBlock *bb : predIt->inEdges()) - worklist.push_back(bb); - } - } -} - -void LoopInfo::dump() const -{ - if (!lcLoopinfo().isDebugEnabled()) - return; - - QString s = QStringLiteral("Loop information:\n"); - for (size_t i = 0, ei = blockInfos.size(); i != ei; ++i) { - const BlockInfo &bi = blockInfos[i]; - s += QStringLiteral(" %1 : is loop header: %2, contained in loop header's loop: ") - .arg(i).arg(bi.isLoopHeader ? QLatin1String("yes") : QLatin1String("no")); - if (bi.loopHeader) - s += QString::number(bi.loopHeader->index()); - else - s += QLatin1String("<none>"); - if (bi.isLoopHeader) { - s += QStringLiteral(", loop exits: "); - if (bi.loopExits.empty()) { - s += QLatin1String("<none>"); - } else { - bool first = true; - for (MIBlock *exit : bi.loopExits) { - if (first) - first = false; - else - s += QStringLiteral(", "); - s += QString::number(exit->index()); - } - } - } - s += QLatin1Char('\n'); - } - qCDebug(lcLoopinfo).noquote().nospace() << s; -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4loopinfo_p.h b/src/qml/jit/qv4loopinfo_p.h deleted file mode 100644 index 6a865e6dc6..0000000000 --- a/src/qml/jit/qv4loopinfo_p.h +++ /dev/null @@ -1,159 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4LOOPINFO_P_H -#define QV4LOOPINFO_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4mi_p.h" - -#include <QHash> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class DominatorTree; - -// Detect all (sub-)loops in a function. -// -// Doing loop detection on the CFG is better than relying on the statement information in -// order to mark loops. Although JavaScript only has natural loops, it can still be the case -// that something is not a loop even though a loop-like-statement is in the source. For -// example: -// while (true) { -// if (i > 0) -// break; -// else -// break; -// } -// -// Algorithm: -// - do a DFS on the dominator tree, where for each node: -// - collect all back-edges -// - if there are back-edges, the node is a loop-header for a new loop, so: -// - walk the CFG is reverse-direction, and for every node: -// - if the node already belongs to a loop, we've found a nested loop: -// - get the loop-header for the (outermost) nested loop -// - add that loop-header to the current loop -// - continue by walking all incoming edges that do not yet belong to the current loop -// - if the node does not belong to a loop yet, add it to the current loop, and -// go on with all incoming edges -// -// Loop-header detection by checking for back-edges is very straight forward: a back-edge is -// an incoming edge where the other node is dominated by the current node. Meaning: all -// execution paths that reach that other node have to go through the current node, that other -// node ends with a (conditional) jump back to the loop header. -// -// The exact order of the DFS on the dominator tree is not important. The only property has to -// be that a node is only visited when all the nodes it dominates have been visited before. -// The reason for the DFS is that for nested loops, the inner loop's loop-header is dominated -// by the outer loop's header. So, by visiting depth-first, sub-loops are identified before -// their containing loops, which makes nested-loop identification free. An added benefit is -// that the nodes for those sub-loops are only processed once. -// -// Note: independent loops that share the same header are merged together. For example, in -// the code snippet below, there are 2 back-edges into the loop-header, but only one single -// loop will be detected. -// while (a) { -// if (b) -// continue; -// else -// continue; -// } -class LoopInfo -{ - Q_DISABLE_COPY_MOVE(LoopInfo) - - struct BlockInfo - { - MIBlock *loopHeader = nullptr; - bool isLoopHeader = false; - std::vector<MIBlock *> loopExits; - }; - -public: - LoopInfo(const DominatorTree &dt) - : dt(dt) - {} - - ~LoopInfo() = default; - - void detectLoops(); - - MIBlock *loopHeaderFor(MIBlock *bodyBlock) const - { return blockInfos[bodyBlock->index()].loopHeader; } - - bool isLoopHeader(MIBlock *block) const - { return blockInfos[block->index()].isLoopHeader; } - - const std::vector<MIBlock *> loopExitsForLoop(MIBlock *loopHeader) const - { return blockInfos[loopHeader->index()].loopExits; } - -private: - void subLoop(MIBlock *loopHead, const std::vector<MIBlock *> &backedges); - void collectLoopExits(); - bool inLoopOrSubLoop(MIBlock *block, MIBlock *loopHeader) const; - - void dump() const; - -private: - const DominatorTree &dt; - std::vector<BlockInfo> blockInfos; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4LOOPINFO_P_H diff --git a/src/qml/jit/qv4lowering.cpp b/src/qml/jit/qv4lowering.cpp deleted file mode 100644 index 3b3711e7fa..0000000000 --- a/src/qml/jit/qv4lowering.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QLoggingCategory> - -#include "qv4lowering_p.h" -#include "qv4graph_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcLower, "qt.v4.ir.lowering") - -GenericLowering::GenericLowering(Function &f) - : m_function(f) -{} - -void GenericLowering::lower() -{ - NodeWorkList worklist(graph()); - // The order doesn't really matter for generic lowering, as long as it's done in 1 pass, and - // have any clean-up done afterwards. - worklist.enqueueAllInputs(graph()->endNode()); - - while (Node *n = worklist.dequeueNextNodeForVisiting()) { - worklist.enqueueAllInputs(n); - - if (!CallPayload::isRuntimeCall(n->opcode())) - continue; - - if (CallPayload::isVarArgsCall(n->opcode())) - replaceWithVarArgsCall(n); - else - replaceWithCall(n); - } -} - -void GenericLowering::replaceWithCall(Node *n) -{ - auto newOp = opBuilder()->getCall(n->opcode()); - - QVarLengthArray<Node *, 32> args; - if (CallPayload::takesEngineAsArg(n->opcode(), 0)) - args.append(graph()->engineNode()); - if (CallPayload::takesFunctionAsArg(n->opcode(), args.size())) - args.append(graph()->functionNode()); - if (CallPayload::takesFrameAsArg(n->opcode(), args.size())) - args.append(graph()->cppFrameNode()); - const int extraLeadingArguments = args.size(); - - for (unsigned arg = 0, earg = n->inputCount(); arg != earg; ++arg) { - Node *input = n->input(arg); - if (input->opcode() == Meta::FrameState) - continue; - - if (arg >= n->operation()->valueInputCount()) { - // effect or control input - args.append(input); - continue; - } - - if (CallPayload::needsStorageOnJSStack(n->opcode(), args.size(), input->operation(), - function().nodeInfo(input)->type())) - input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input); - - args.append(input); - } - - Node *newCall = graph()->createNode(newOp, args.data(), args.size()); - - qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString() - << "with node" << newCall->id() << newOp->debugString(); - qCDebug(lcLower) << "... old node #inputs:" << n->inputCount(); - qCDebug(lcLower) << "... old node #uses:" << n->useCount(); - - function().nodeInfo(newCall)->setType(CallPayload::returnType(n->opcode())); - n->replaceAllUsesWith(newCall); - n->kill(); - - qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount(); - qCDebug(lcLower) << "... new node #uses:" << newCall->useCount(); - - for (Node *use : newCall->uses()) { - // fix-up indices for SelectOutput: - if (use->opcode() == Meta::SelectOutput) { - const int oldIndex = ConstantPayload::get(*use->input(1)->operation())->value().int_32(); - const int newIndex = oldIndex + extraLeadingArguments; - use->replaceInput(1, graph()->createConstantIntNode(newIndex)); - use->replaceInput(2, newCall->input(newIndex)); - break; - } - } -} - -void GenericLowering::replaceWithVarArgsCall(Node *n) -{ - const bool isTailCall = n->opcode() == Meta::JSTailCall; - Operation *newOp = isTailCall ? opBuilder()->getTailCall() - : opBuilder()->getCall(n->opcode()); - - //### optimize this for 0 and 1 argument: we don't need to create a VarArgs array for these cases - - const unsigned varArgsStart = CallPayload::varArgsStart(n->opcode()) - 1; // subtract 1 because the runtime calls all take the engine argument as arg0, which isn't in the graph before lowering. - Node *vaAlloc = graph()->createNode( - opBuilder()->get<Meta::VAAlloc>(), - graph()->createConstantIntNode(n->operation()->valueInputCount() - varArgsStart), - n->effectInput()); - QVarLengthArray<Node *, 32> vaSealIn; - vaSealIn.append(vaAlloc); - for (unsigned i = varArgsStart, ei = n->operation()->valueInputCount(); i != ei; ++i) { - vaSealIn.append(graph()->createNode(opBuilder()->get<Meta::VAStore>(), vaAlloc, - graph()->createConstantIntNode(vaSealIn.size() - 1), - n->input(i))); - } - vaSealIn.append(vaAlloc); - Node *vaSeal = graph()->createNode(opBuilder()->getVASeal(vaSealIn.size() - 2), - vaSealIn.data(), - vaSealIn.size()); - QVarLengthArray<Node *, 8> callArgs; - if (isTailCall) - callArgs.append(graph()->cppFrameNode()); - callArgs.append(graph()->engineNode()); - for (unsigned i = 0; i != varArgsStart; ++i) { - Node *input = n->input(i); - if (CallPayload::needsStorageOnJSStack(n->opcode(), callArgs.size(), input->operation(), - function().nodeInfo(input)->type())) - input = graph()->createNode(opBuilder()->get<Meta::Alloca>(), input); - callArgs.append(input); - } - callArgs.append(vaSeal); // args - if (n->opcode() != Meta::JSCreateClass) // JSCreateClass is the odd duck - callArgs.append(graph()->createConstantIntNode(vaSealIn.size() - 2)); // argc - callArgs.append(vaSeal); // effect - callArgs.append(n->controlInput(0)); // control flow - Node *newCall = graph()->createNode(newOp, callArgs.data(), unsigned(callArgs.size())); - - qCDebug(lcLower) << "replacing node" << n->id() << n->operation()->debugString() - << "with node" << newCall->id() << newOp->debugString(); - qCDebug(lcLower) << "... old node #inputs:" << n->inputCount(); - qCDebug(lcLower) << "... old node #uses:" << n->useCount(); - - n->replaceAllUsesWith(newCall); - n->kill(); - - qCDebug(lcLower) << "... new node #inputs:" << newCall->inputCount(); - qCDebug(lcLower) << "... new node #uses:" << newCall->useCount(); -} - -bool GenericLowering::allUsesAsUnboxedBool(Node *n) -{ - for (Node *use : n->uses()) { - if (use->operation()->kind() != Meta::Branch) - return false; - } - - return true; -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4lowering_p.h b/src/qml/jit/qv4lowering_p.h deleted file mode 100644 index 0b482bc9f0..0000000000 --- a/src/qml/jit/qv4lowering_p.h +++ /dev/null @@ -1,107 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4LOWERING_P_H -#define QV4LOWERING_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmljsmemorypool_p.h> -#include <private/qv4global_p.h> -#include <private/qv4ir_p.h> -#include <private/qv4util_p.h> -#include <private/qv4node_p.h> -#include <private/qv4graph_p.h> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -// Lowering replaces JS level operations with lower level ones. E.g. a JSAdd is lowered to an AddI32 -// if both inputs and the output are 32bit integers, or to a runtime call in all other cases. This -// transforms the graph into something that is closer to actual executable code. - - -// Last lowering phase: replace all JSOperations that are left with runtime calls. There is nothing -// smart here, all that should have been done before this phase. -class GenericLowering final -{ - Q_DISABLE_COPY(GenericLowering) - -public: - GenericLowering(Function &f); - - void lower(); - -private: - void replaceWithCall(Node *n); - void replaceWithVarArgsCall(Node *n); - static bool allUsesAsUnboxedBool(Node *n); - - Function &function() - { return m_function; } - - Graph *graph() - { return function().graph(); } - - OperationBuilder *opBuilder() - { return graph()->opBuilder(); } - -private: - Function &m_function; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4LOWERING_P_H diff --git a/src/qml/jit/qv4mi.cpp b/src/qml/jit/qv4mi.cpp deleted file mode 100644 index f0b172243d..0000000000 --- a/src/qml/jit/qv4mi.cpp +++ /dev/null @@ -1,251 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> -#include <private/qqmlglobal_p.h> - -#include "qv4mi_p.h" -#include "qv4node_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcMI, "qt.v4.ir.mi") - -QString MIOperand::debugString() const -{ - switch (kind()) { - case Invalid: return QStringLiteral("<<INVALID>>"); - case Constant: return ConstantPayload::debugString(constantValue()); - case VirtualRegister: return QStringLiteral("vreg%1").arg(virtualRegister()); - case EngineRegister: return QStringLiteral("engine"); - case CppFrameRegister: return QStringLiteral("cppFrame"); - case Function: return QStringLiteral("function"); - case JSStackSlot: return QStringLiteral("jsstack[%1]").arg(stackSlot()); - case BoolStackSlot: return QStringLiteral("bstack[%1]").arg(stackSlot()); - case JumpTarget: return targetBlock() ? QStringLiteral("L%1").arg(targetBlock()->index()) - : QStringLiteral("<<INVALID BLOCK>>"); - default: Q_UNREACHABLE(); - } -} - -MIInstr *MIInstr::create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands) -{ - return pool->New<MIInstr>(irNode, pool, nOperands); -} - -static QString commentIndent(const QString &line) -{ - int spacing = std::max(70 - line.length(), 1); - return line + QString(spacing, QLatin1Char(' ')); -} - -static QString indent(int nr) -{ - QString s = nr == -1 ? QString() : QString::number(nr); - int padding = 6 - s.size(); - if (padding > 0) - s = QString(padding, QLatin1Char(' ')) + s; - return s; -} - -MIFunction::MIFunction(Function *irFunction) - : m_irFunction(irFunction) -{} - -void MIFunction::renumberBlocks() -{ - for (size_t i = 0, ei = m_blocks.size(); i != ei; ++i) { - MIBlock *b = m_blocks[i]; - b->setIndex(unsigned(i)); - } -} - -void MIFunction::renumberInstructions() -{ - int pos = 0; - for (MIBlock *b : m_blocks) { - for (MIInstr &instr : b->instructions()) { - pos += 2; - instr.setPosition(pos); - } - } -} - -void MIFunction::dump(const QString &description) const -{ - if (!lcMI().isDebugEnabled()) - return; - - QString s = description + QLatin1String(":\n"); - QString name; - if (auto n = irFunction()->v4Function()->name()) - name = n->toQString(); - if (name.isEmpty()) - QString::asprintf("%p", static_cast<void *>(irFunction()->v4Function())); - QString line = QStringLiteral("function %1 {").arg(name); - auto loc = irFunction()->v4Function()->sourceLocation(); - s += commentIndent(line) + QStringLiteral("; %1:%2:%3\n").arg(loc.sourceFile, - QString::number(loc.line), - QString::number(loc.column)); - for (const MIBlock *b : blocks()) { - line = QStringLiteral("L%1").arg(b->index()); - bool first = true; - if (!b->arguments().empty()) { - line += QLatin1Char('('); - for (const MIOperand &arg : b->arguments()) { - if (first) - first = false; - else - line += QStringLiteral(", "); - line += arg.debugString(); - } - line += QLatin1Char(')'); - } - line += QLatin1Char(':'); - line = commentIndent(line) + QStringLiteral("; preds: "); - if (b->inEdges().isEmpty()) { - line += QStringLiteral("<none>"); - } else { - bool first = true; - for (MIBlock *in : b->inEdges()) { - if (first) - first = false; - else - line += QStringLiteral(", "); - line += QStringLiteral("L%1").arg(in->index()); - } - } - s += line + QLatin1Char('\n'); - for (const MIInstr &i : b->instructions()) { - line = indent(i.position()) + QLatin1String(": "); - if (i.hasDestination()) - line += i.destination().debugString() + QStringLiteral(" = "); - line += i.irNode()->operation()->debugString(); - bool first = true; - for (const MIOperand &op : i.operands()) { - if (first) - first = false; - else - line += QLatin1Char(','); - line += QLatin1Char(' ') + op.debugString(); - } - line = commentIndent(line) + QStringLiteral("; node-id: %1").arg(i.irNode()->id()); - if (i.irNode()->operation()->needsBytecodeOffsets()) - line += QStringLiteral(", bytecode-offset: %1").arg(irFunction()->nodeInfo(i.irNode())->currentInstructionOffset()); - s += line + QLatin1Char('\n'); - } - s += commentIndent(QString()) + QStringLiteral("; succs: "); - if (b->outEdges().isEmpty()) { - s += QStringLiteral("<none>"); - } else { - bool first = true; - for (MIBlock *succ : b->outEdges()) { - if (first) - first = false; - else - s += QStringLiteral(", "); - s += QStringLiteral("L%1").arg(succ->index()); - } - } - s += QLatin1Char('\n'); - } - s += QLatin1Char('}'); - - for (const QStringRef &line : s.splitRef('\n')) - qCDebug(lcMI).noquote().nospace() << line; -} - -unsigned MIFunction::extraJSSlots() const -{ - uint interpreterFrameSize = CppStackFrame::requiredJSStackFrameSize(irFunction()->v4Function()); - if (m_jsSlotCount <= interpreterFrameSize) - return 0; - return m_jsSlotCount - interpreterFrameSize; -} - -void MIFunction::setStartBlock(MIBlock *newStartBlock) -{ - auto it = std::find(m_blocks.begin(), m_blocks.end(), newStartBlock); - Q_ASSERT(it != m_blocks.end()); - std::swap(*m_blocks.begin(), *it); -} - -void MIFunction::setStackSlotCounts(unsigned dword, unsigned qword, unsigned js) -{ - m_vregCount = 0; - m_dwordSlotCount = dword; - m_qwordSlotCount = qword; - m_jsSlotCount = js; -} - -void MIFunction::verifyCFG() const -{ - if (block(MIFunction::StartBlockIndex)->instructions().front().opcode() != Meta::Start) - qFatal("MIFunction block 0 is not the start block"); - - for (MIBlock *b : m_blocks) { - for (MIBlock *in : b->inEdges()) { - if (!in->outEdges().contains(b)) - qFatal("block %u has incoming edge from block %u, " - "but does not appear in that block's outgoing edges", - b->index(), in->index()); - } - for (MIBlock *out : b->outEdges()) { - if (!out->inEdges().contains(b)) - qFatal("block %u has outgoing edge from block %u, " - "but does not appear in that block's incoming edges", - b->index(), out->index()); - } - } -} - -MIBlock *MIBlock::findEdgeTo(Operation::Kind target) const -{ - for (MIBlock *outEdge : outEdges()) { - if (outEdge->instructions().front().opcode() == target) - return outEdge; - } - return nullptr; -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4mi_p.h b/src/qml/jit/qv4mi_p.h deleted file mode 100644 index f976d1dc94..0000000000 --- a/src/qml/jit/qv4mi_p.h +++ /dev/null @@ -1,627 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4MI_P_H -#define QV4MI_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4global_p.h> -#include <private/qv4ir_p.h> -#include <private/qv4node_p.h> -#include <private/qv4operation_p.h> - -#include <llvm/ADT/iterator.h> -#include <llvm/ADT/iterator_range.h> -#include <llvm/ADT/ilist.h> -#include <llvm/ADT/ilist_node.h> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -// This file contains the Machine Interface (MI) data structures, on which ultimately the assembler -// will operate: - -class MIFunction; // containing all basic blocks, and a reference to the IR function - -class MIBlock; // containing an ordered sequence of instructions - -class MIInstr; // containing operands, and a reference to the IR node, that indicates which - // operation is represented by an instruction - -class MIOperand; // contains a description of where to get/put the input/result of an operation - -// A detail about the stack slots: there two stacks, the JS stack and the native stack. A frame on -// the native stack is divided in two parts: the quad-word part and the double-word part. The -// qword part holds 64bit values, like doubles, and pointers on 64bit architectures. The dword part -// holds 32bit values, like int32s, booleans, and pointers on 32bit architectures. We need to know -// the type of value a slot holds, because if we have to move it to the JS stack, we have to box it -// correctly. -class MIOperand final -{ -public: - enum Kind { - Invalid = 0, - Constant, - VirtualRegister, - - EngineRegister, - CppFrameRegister, - Function, - - JSStackSlot, - BoolStackSlot, - - JumpTarget, - }; - - using List = QQmlJS::FixedPoolArray<MIOperand>; - -public: - MIOperand() = default; - - static MIOperand createConstant(Node *irNode) - { - MIOperand op; - op.m_kind = Constant; - op.m_irNode = irNode; - return op; - } - - static MIOperand createVirtualRegister(Node *irNode, unsigned vreg) - { - MIOperand op; - op.m_kind = VirtualRegister; - op.m_irNode = irNode; - op.m_vreg = vreg; - return op; - } - - static MIOperand createEngineRegister(Node *irNode) - { - MIOperand op; - op.m_kind = EngineRegister; - op.m_irNode = irNode; - return op; - } - - static MIOperand createCppFrameRegister(Node *irNode) - { - MIOperand op; - op.m_kind = CppFrameRegister; - op.m_irNode = irNode; - return op; - } - - static MIOperand createFunction(Node *irNode) - { - MIOperand op; - op.m_kind = Function; - op.m_irNode = irNode; - return op; - } - - static MIOperand createJSStackSlot(Node *irNode, unsigned slot) - { - MIOperand op; - op.m_kind = JSStackSlot; - op.m_irNode = irNode; - op.m_slot = slot; - return op; - } - - static MIOperand createBoolStackSlot(Node *irNode, unsigned slot) - { - MIOperand op; - op.m_kind = BoolStackSlot; - op.m_irNode = irNode; - op.m_slot = slot; - return op; - } - - //### or name this createDeoptBlock? - static MIOperand createJumpTarget(Node *irNode, MIBlock *targetBlock) - { - MIOperand op; - op.m_kind = JumpTarget; - op.m_irNode = irNode; - op.m_targetBlock = targetBlock; - return op; - } - - Kind kind() const - { return m_kind; } - - bool isValid() const - { return m_kind != Invalid; } - - bool isConstant() const - { return m_kind == Constant; } - - bool isVirtualRegister() const - { return kind() == VirtualRegister; } - - bool isEngineRegister() const - { return kind() == EngineRegister; } - - bool isCppFrameRegister() const - { return kind() == CppFrameRegister; } - - bool isFunction() const - { return kind() == Function; } - - bool isJSStackSlot() const - { return kind() == JSStackSlot; } - - bool isBoolStackSlot() const - { return kind() == BoolStackSlot; } - - bool isStackSlot() const - { return isJSStackSlot() || isDWordSlot() || isQWordSlot(); } - - bool isJumpTarget() const - { return kind() == JumpTarget; } - - Node *irNode() const - { return m_irNode; } - - inline Type nodeType(MIFunction *f) const; - - QString debugString() const; - - QV4::Value constantValue() const - { - Q_ASSERT(isConstant()); - if (irNode()->opcode() == Meta::Undefined) - return QV4::Value::undefinedValue(); - if (irNode()->opcode() == Meta::Empty) - return QV4::Value::emptyValue(); - return ConstantPayload::get(*irNode()->operation())->value(); - } - - unsigned virtualRegister() const - { Q_ASSERT(isVirtualRegister()); return m_vreg; } - - unsigned stackSlot() const - { Q_ASSERT(isStackSlot()); return m_slot; } - - MIBlock *targetBlock() const - { Q_ASSERT(isJumpTarget()); return m_targetBlock; } - - inline bool operator==(const MIOperand &other) const - { - if (kind() != other.kind()) - return false; - - if (isStackSlot()) - return stackSlot() == other.stackSlot(); - - switch (kind()) { - case MIOperand::Invalid: - return !other.isValid(); - case MIOperand::Constant: - return constantValue().asReturnedValue() == other.constantValue().asReturnedValue(); - case MIOperand::VirtualRegister: - return virtualRegister() == other.virtualRegister(); - case MIOperand::JumpTarget: - return targetBlock() == other.targetBlock(); - default: - Q_UNREACHABLE(); - return false; - } - } - - bool isDWordSlot() const - { - switch (kind()) { - case BoolStackSlot: - return true; - default: - return false; - } - } - - bool isQWordSlot() const - { - switch (kind()) { - //### TODO: double slots - default: - return false; - } - } - - bool overlaps(const MIOperand &other) const - { - if ((isDWordSlot() && other.isDWordSlot()) || (isQWordSlot() && other.isQWordSlot())) - ; // fine, these are the same - else if (kind() != other.kind()) - return false; - - if (isStackSlot()) - return stackSlot() == other.stackSlot(); - - return false; - } - -private: - Node *m_irNode = nullptr; - union { - unsigned m_vreg; - unsigned m_slot; - MIBlock *m_targetBlock = nullptr; - }; - Kind m_kind = Invalid; -}; - -template <typename NodeTy> struct MIInstrListParentType {}; -template <> struct MIInstrListParentType<MIInstr> { using type = MIBlock; }; - -template <typename NodeTy> class MIInstrList; - -template <typename MISubClass> -class MIInstrListTraits : public llvm::ilist_noalloc_traits<MISubClass> -{ -protected: - using ListTy = MIInstrList<MISubClass>; - using iterator = typename llvm::simple_ilist<MISubClass>::iterator; - using ItemParentClass = typename MIInstrListParentType<MISubClass>::type; - -public: - MIInstrListTraits() = default; - -protected: - void setListOwner(ItemParentClass *listOwner) - { m_owner = listOwner; } - -private: - ItemParentClass *m_owner = nullptr; - - /// getListOwner - Return the object that owns this list. If this is a list - /// of instructions, it returns the BasicBlock that owns them. - ItemParentClass *getListOwner() const { - return m_owner; - } - - static ListTy &getList(ItemParentClass *Par) { - return Par->*(Par->getSublistAccess()); - } - - static MIInstrListTraits<MISubClass> *getSymTab(ItemParentClass *Par) { - return Par ? toPtr(Par->getValueSymbolTable()) : nullptr; - } - -public: - void addNodeToList(MISubClass *V) { V->setParent(getListOwner()); } - void removeNodeFromList(MISubClass *V) { V->setParent(nullptr); } - void transferNodesFromList(MIInstrListTraits &L2, iterator first, - iterator last); -}; - -template <class T> -class MIInstrList: public llvm::iplist_impl<llvm::simple_ilist<T>, MIInstrListTraits<T>> -{ -public: - MIInstrList(typename MIInstrListTraits<T>::ItemParentClass *owner) - { this->setListOwner(owner); } -}; - -class MIInstr final : public llvm::ilist_node_with_parent<MIInstr, MIBlock> -{ - Q_DISABLE_COPY_MOVE(MIInstr) // heap use only! - -protected: - friend class QQmlJS::MemoryPool; - MIInstr() : m_operands(nullptr, 0) {} - - explicit MIInstr(Node *irNode, QQmlJS::MemoryPool *pool, unsigned nOperands) - : m_irNode(irNode) - , m_operands(pool, nOperands) - {} - - ~MIInstr() = default; - -public: - static MIInstr *create(QQmlJS::MemoryPool *pool, Node *irNode, unsigned nOperands); - - MIBlock *parent() const - { return m_parent; } - - MIBlock *getParent() const // for ilist_node_with_parent - { return parent(); } - - void setParent(MIBlock *parent) - { m_parent = parent; } - - Node *irNode() const - { return m_irNode; } - - Operation::Kind opcode() const - { return m_irNode->opcode(); } - - int position() const - { return m_position; } - - inline void insertBefore(MIInstr *insertPos); - inline void insertAfter(MIInstr *insertPos); - inline MIInstrList<MIInstr>::iterator eraseFromParent(); - - bool hasDestination() const - { return m_destination.isValid(); } - - MIOperand destination() const - { return m_destination; } - - void setDestination(const MIOperand &dest) - { m_destination = dest; } - - const MIOperand &operand(unsigned index) const - { return m_operands.at(index); } - - void setOperand(unsigned index, const MIOperand &op) - { m_operands.at(index) = op; } - - MIOperand &operand(unsigned index) - { return m_operands.at(index); } - - const MIOperand::List &operands() const - { return m_operands; } - - MIOperand::List &operands() - { return m_operands; } - -private: - friend MIFunction; - void setPosition(int newPosition) - { m_position = newPosition; } - -private: - MIBlock *m_parent = nullptr; - Node *m_irNode = nullptr; - int m_position = -1; - MIOperand m_destination; - MIOperand::List m_operands; -}; - -class MIBlock final -{ - Q_DISABLE_COPY_MOVE(MIBlock) - -public: - using Index = unsigned; - enum : Index { InvalidIndex = std::numeric_limits<Index>::max() }; - - using MIInstructionList = MIInstrList<MIInstr>; - - using InEdges = QVarLengthArray<MIBlock *, 4>; - using OutEdges = QVarLengthArray<MIBlock *, 2>; - -protected: - friend MIFunction; - explicit MIBlock(Index index) - : m_instructions(this), - m_index(index) - {} - - void setIndex(Index newIndex) - { m_index = newIndex; } - -public: - ~MIBlock() = default; - - const MIInstructionList &instructions() const - { return m_instructions; } - - MIInstructionList &instructions() - { return m_instructions; } - - static MIInstructionList MIBlock::*getSublistAccess(MIInstr * = nullptr) - { return &MIBlock::m_instructions; } - - void addArgument(MIOperand &&arg) - { m_arguments.push_back(arg); } - - const std::vector<MIOperand> &arguments() const - { return m_arguments; } - - std::vector<MIOperand> &arguments() - { return m_arguments; } - - void clearArguments() - { m_arguments.resize(0); } - - const InEdges &inEdges() const - { return m_inEdges; } - - void addInEdge(MIBlock *edge) - { m_inEdges.append(edge); } - - const OutEdges &outEdges() const - { return m_outEdges; } - - void addOutEdge(MIBlock *edge) - { m_outEdges.append(edge); } - - Index index() const - { return m_index; } - - MIBlock *findEdgeTo(Operation::Kind target) const; - - bool isDeoptBlock() const - { return m_isDeoptBlock; } - - void markAsDeoptBlock() - { m_isDeoptBlock = true; } - -private: - std::vector<MIOperand> m_arguments; - MIInstructionList m_instructions; - InEdges m_inEdges; - OutEdges m_outEdges; - Index m_index; - bool m_isDeoptBlock = false; -}; - -class MIFunction final -{ - Q_DISABLE_COPY_MOVE(MIFunction) - -public: - static constexpr MIBlock::Index StartBlockIndex = 0; - -public: - MIFunction(Function *irFunction); - ~MIFunction() - { qDeleteAll(m_blocks); } - - Function *irFunction() const - { return m_irFunction; } - - void setStartBlock(MIBlock *newStartBlock); - void renumberBlocks(); - void renumberInstructions(); - - void dump(const QString &description) const; - - size_t blockCount() const - { return blocks().size(); } - - MIBlock *block(MIBlock::Index index) const - { return m_blocks[index]; } - - const std::vector<MIBlock *> &blocks() const - { return m_blocks; } - - MIBlock *addBlock() - { - auto *b = new MIBlock(unsigned(m_blocks.size())); - m_blocks.push_back(b); - return b; - } - - void setBlockOrder(const std::vector<MIBlock *> &newSequence) - { m_blocks = newSequence; } - - unsigned vregCount() const - { return m_vregCount; } - - void setVregCount(unsigned vregCount) - { m_vregCount = vregCount; } - - unsigned dwordSlotCount() const - { return m_dwordSlotCount; } - - unsigned qwordSlotCount() const - { return m_qwordSlotCount; } - - unsigned jsSlotCount() const - { return m_jsSlotCount; } - - unsigned extraJSSlots() const; - - void setStackSlotCounts(unsigned dword, unsigned qword, unsigned js); - - void verifyCFG() const; - -private: - Function *m_irFunction = nullptr; - std::vector<MIBlock *> m_blocks; - unsigned m_vregCount = 0; - unsigned m_dwordSlotCount = 0; - unsigned m_qwordSlotCount = 0; - unsigned m_jsSlotCount = 0; -}; - -Type MIOperand::nodeType(MIFunction *f) const -{ - return f->irFunction()->nodeInfo(irNode())->type(); -} - -inline uint qHash(const MIOperand &key, uint seed) -{ - uint h = ::qHash(key.kind(), seed); - switch (key.kind()) { - case MIOperand::VirtualRegister: - h ^= key.virtualRegister(); - break; - case MIOperand::BoolStackSlot: Q_FALLTHROUGH(); - case MIOperand::JSStackSlot: - h ^= key.stackSlot(); - break; - default: - qFatal("%s: cannot hash %s", Q_FUNC_INFO, key.debugString().toUtf8().constData()); - } - return h; -} - -void MIInstr::insertBefore(MIInstr *insertPos) -{ - insertPos->parent()->instructions().insert(insertPos->getIterator(), this); -} - -void MIInstr::insertAfter(MIInstr *insertPos) -{ - insertPos->parent()->instructions().insert(++insertPos->getIterator(), this); -} - -MIInstrList<MIInstr>::iterator MIInstr::eraseFromParent() -{ - auto p = parent(); - setParent(nullptr); - return p->instructions().erase(getIterator()); -} - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4MI_P_H diff --git a/src/qml/jit/qv4miblockset_p.h b/src/qml/jit/qv4miblockset_p.h deleted file mode 100644 index 5f814b99e0..0000000000 --- a/src/qml/jit/qv4miblockset_p.h +++ /dev/null @@ -1,291 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4MIBLOCKSET_P_H -#define QV4MIBLOCKSET_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4mi_p.h" -#include "qv4util_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class MIBlockSet -{ - using Flags = BitVector; - - QVarLengthArray<MIBlock::Index, 8> blockNumbers; - Flags *blockFlags = nullptr; - MIFunction *function = nullptr; - enum { MaxVectorCapacity = 8 }; - -public: - class const_iterator; - friend class const_iterator; - -public: - MIBlockSet(MIFunction *f = nullptr) - { - if (f) - init(f); - } - - MIBlockSet(MIBlockSet &&other) noexcept - { - std::swap(blockNumbers, other.blockNumbers); - std::swap(blockFlags, other.blockFlags); - std::swap(function, other.function); - } - - MIBlockSet(const MIBlockSet &other) - : function(other.function) - { - if (other.blockFlags) - blockFlags = new Flags(*other.blockFlags); - blockNumbers = other.blockNumbers; - } - - MIBlockSet &operator=(const MIBlockSet &other) - { - if (blockFlags) { - delete blockFlags; - blockFlags = nullptr; - } - function = other.function; - if (other.blockFlags) - blockFlags = new Flags(*other.blockFlags); - blockNumbers = other.blockNumbers; - return *this; - } - - MIBlockSet &operator=(MIBlockSet &&other) noexcept - { - if (&other != this) { - std::swap(blockNumbers, other.blockNumbers); - - delete blockFlags; - blockFlags = other.blockFlags; - other.blockFlags = nullptr; - - function = other.function; - } - return *this; - } - - ~MIBlockSet() - { - delete blockFlags; - } - - void init(MIFunction *f) - { - Q_ASSERT(!function); - Q_ASSERT(f); - function = f; - } - - bool empty() const; - - void insert(MIBlock *bb) - { - Q_ASSERT(function); - - if (blockFlags) { - blockFlags->setBit(bb->index()); - return; - } - - for (unsigned int blockNumber : qAsConst(blockNumbers)) { - if (blockNumber == bb->index()) - return; - } - - if (blockNumbers.size() == MaxVectorCapacity) { - blockFlags = new Flags(int(function->blockCount()), false); - for (unsigned int blockNumber : qAsConst(blockNumbers)) { - blockFlags->setBit(int(blockNumber)); - } - blockNumbers.clear(); - blockFlags->setBit(int(bb->index())); - } else { - blockNumbers.append(bb->index()); - } - } - - void remove(MIBlock *bb) - { - Q_ASSERT(function); - - if (blockFlags) { - blockFlags->clearBit(bb->index()); - return; - } - - for (int i = 0; i < blockNumbers.size(); ++i) { - if (blockNumbers[i] == bb->index()) { - blockNumbers.remove(i); - return; - } - } - } - - const_iterator begin() const; - const_iterator end() const; - - void collectValues(std::vector<MIBlock *> &bbs) const; - - bool contains(MIBlock *bb) const - { - Q_ASSERT(function); - - if (blockFlags) - return blockFlags->at(bb->index()); - - for (unsigned int blockNumber : blockNumbers) { - if (blockNumber == bb->index()) - return true; - } - - return false; - } -}; - -class MIBlockSet::const_iterator -{ - const MIBlockSet &set; - // ### These two members could go into a union, but clang won't compile - // (https://codereview.qt-project.org/#change,74259) - QVarLengthArray<MIBlock::Index, 8>::const_iterator numberIt; - MIBlock::Index flagIt; - - friend class MIBlockSet; - const_iterator(const MIBlockSet &set, bool end) - : set(set) - { - if (end || !set.function) { - if (!set.blockFlags) - numberIt = set.blockNumbers.end(); - else - flagIt = set.blockFlags->size(); - } else { - if (!set.blockFlags) - numberIt = set.blockNumbers.begin(); - else - findNextWithFlags(0); - } - } - - void findNextWithFlags(int start) - { - flagIt = MIBlock::Index(set.blockFlags->findNext(start, true, /*wrapAround = */false)); - Q_ASSERT(flagIt <= MIBlock::Index(set.blockFlags->size())); - } - -public: - MIBlock *operator*() const - { - if (!set.blockFlags) - return set.function->block(*numberIt); - - Q_ASSERT(flagIt <= set.function->blockCount()); - return set.function->block(flagIt); - - } - - bool operator==(const const_iterator &other) const - { - if (&set != &other.set) - return false; - if (!set.blockFlags) - return numberIt == other.numberIt; - return flagIt == other.flagIt; - } - - bool operator!=(const const_iterator &other) const - { return !(*this == other); } - - const_iterator &operator++() - { - if (!set.blockFlags) - ++numberIt; - else - findNextWithFlags(flagIt + 1); - - return *this; - } -}; - -inline bool MIBlockSet::empty() const -{ return begin() == end(); } - -inline MIBlockSet::const_iterator MIBlockSet::begin() const -{ return const_iterator(*this, false); } - -inline MIBlockSet::const_iterator MIBlockSet::end() const -{ return const_iterator(*this, true); } - -inline void MIBlockSet::collectValues(std::vector<MIBlock *> &bbs) const -{ - Q_ASSERT(function); - - for (auto it : *this) - bbs.push_back(it); -} - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4MIBLOCKSET_P_H diff --git a/src/qml/jit/qv4node.cpp b/src/qml/jit/qv4node.cpp deleted file mode 100644 index e059e9fef6..0000000000 --- a/src/qml/jit/qv4node.cpp +++ /dev/null @@ -1,215 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4node_p.h" -#include "qv4graph_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Node *Node::create(Node::MemoryPool *pool, Node::Id id, const Operation *op, size_t nInputs, - Node *const *inputs, bool inputsAreExtensible) -{ - size_t capacity = nInputs; - if (inputsAreExtensible) - capacity += 3; - - Node *node = new (pool->allocate(sizeof(Node))) Node(pool, id, op, unsigned(nInputs), - int(capacity)); - for (uint i = 0; i < capacity; ++i) - new (&node->m_inputs[int(i)]) Use(node); - for (size_t i = 0; i < nInputs; ++i) { - Q_ASSERT(inputs[i] != nullptr); - node->replaceInput(unsigned(i), inputs[i]); - } - - return node; -} - -void Node::addInput(MemoryPool *pool, Node *in) -{ - Q_ASSERT(in); - ++m_nInputs; - if (m_nInputs >= unsigned(m_inputs.size())) { - QQmlJS::FixedPoolArray<Use> oldInputs = m_inputs; - m_inputs = QQmlJS::FixedPoolArray<Use>(pool, int(m_nInputs + 3)); - for (Use &input : m_inputs) - new (&input) Use(this); - for (int i = 0, ei = oldInputs.size(); i != ei; ++i) { - Node *in = oldInputs[i].m_input; - oldInputs[i].set(nullptr); - m_inputs[i].set(in); - } - } - m_inputs.at(int(m_nInputs - 1)).set(in); -} - -void Node::removeInput(unsigned index) -{ - Q_ASSERT(index < inputCount()); - for (unsigned i = index, ei = inputCount(); i < ei - 1; ++i) - replaceInput(i, input(i + 1)); - trimInputCount(inputCount() - 1); -} - -void Node::removeInputs(unsigned start, unsigned count) -{ - for (unsigned idx = start; idx < start + count; ++idx) - m_inputs.at(int(idx)).set(nullptr); -} - -void Node::removeAllInputs() -{ - removeInputs(0, inputCount()); -} - -void Node::trimInputCount(unsigned newCount) -{ - unsigned currentCount = inputCount(); - if (newCount == currentCount) - return; - Q_ASSERT(newCount < currentCount); - removeInputs(newCount, currentCount - newCount); - m_nInputs = newCount; -} - -void Node::removeExceptionHandlerUse() -{ - for (Use* use = m_firstUse; use; use = use->m_next) { - if (use->m_input->opcode() == Meta::OnException) { - use->set(nullptr); - break; - } - } -} - -void Node::insertInput(Node::MemoryPool *pool, unsigned index, Node *newInput) -{ - Q_ASSERT(index < inputCount()); - addInput(pool, input(inputCount() - 1)); - for (unsigned i = inputCount() - 1; i > index; --i) - replaceInput(i, input(i - 1)); - replaceInput(index, newInput); -} - -void Node::replaceAllUsesWith(Node *replacement) -{ - for (Use *use = m_firstUse; use; ) { - Use *next = use->m_next; - const unsigned inIdx = use->inputIndex(); - use->user()->replaceInput(inIdx, replacement); - use = next; - } -} - -void Node::replaceUses(Node *newValueInput, Node *newEffectInput, Node *newControlInput) -{ - for (Use *use = m_firstUse; use; ) { - Use *next = use->m_next; - const Operation *inOp = use->user()->operation(); - const unsigned inIdx = use->inputIndex(); - if (inIdx < inOp->valueInputCount()) - use->user()->replaceInput(inIdx, newValueInput); - else if (inIdx < inOp->indexOfFirstControl()) - use->user()->replaceInput(inIdx, newEffectInput); - else - use->user()->replaceInput(inIdx, newControlInput); - use = next; - } -} - -Node *Node::firstValueUse() -{ - for (auto it = uses().begin(), eit = uses().end(); it != eit; ++it) { - if (it.isUsedAsValue()) - return *it; - } - return nullptr; -} - -Node::Node(MemoryPool *pool, Node::Id id, const Operation *op, unsigned nInputs, int capacity) - : m_op(op) - , m_inputs(pool, capacity) - , m_nInputs(nInputs) - , m_id(id) -{ -} - -NodeWorkList::NodeWorkList(const Graph *g) - : m_nodeState(g->nodeCount(), Unvisited) -{ m_worklist.reserve(64); } - -void NodeWorkList::reset() -{ - std::fill(m_nodeState.begin(), m_nodeState.end(), Unvisited); - m_worklist.clear(); - if (m_worklist.capacity() < 64) - m_worklist.reserve(64); -} - -NodeCollector::NodeCollector(const Graph *g, bool collectUses, bool skipFramestate) -{ - markReachable(g->endNode()); - for (size_t i = 0; i < m_reachable.size(); ++i) { // _reachable.size() is on purpose! - Node *n = m_reachable.at(i); - for (auto input : n->inputs()) { - if (input == nullptr) - continue; - if (isReachable(input->id())) - continue; - if (skipFramestate && input->opcode() == Meta::FrameState) - continue; - markReachable(input); - } - - if (collectUses) { - for (Node *use : n->uses()) { - if (use && !isReachable(use->id())) - markReachable(use); - } - } - } -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4node_p.h b/src/qml/jit/qv4node_p.h deleted file mode 100644 index 76065fb1bc..0000000000 --- a/src/qml/jit/qv4node_p.h +++ /dev/null @@ -1,626 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4NODE_P_H -#define QV4NODE_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qqmljsmemorypool_p.h> -#include <private/qv4global_p.h> -#include <private/qv4operation_p.h> -#include "qv4util_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -class Use -{ - Q_DISABLE_COPY_MOVE(Use) - -public: - Use(Node *user) - : m_user(user) - {} - - ~Use() - { - if (m_input) - removeFromList(); - } - - operator Node *() const { return m_input; } - Node *input() const { return m_input; } - Node *user() const { return m_user; } - - inline void set(Node *newInput); - - void validate() const - { - Q_ASSERT(m_user); - if (m_input) { - if (m_prev != nullptr) - Q_ASSERT(*m_prev == this); - if (m_next) { - Q_ASSERT(m_next->m_input == m_input); - Q_ASSERT(m_next->m_prev == &m_next); - m_next->validate(); - } - } - } - - inline int inputIndex() const; - -protected: - friend class Node; - - void addToList(Use **list) { - validate(); - m_next = *list; - if (m_next) - m_next->m_prev = &m_next; - m_prev = list; - *list = this; - validate(); - } - - void removeFromList() { - validate(); - Use **newPrev = m_prev; - *newPrev = m_next; - m_prev = nullptr; - if (m_next) - m_next->m_prev = newPrev; - m_next = nullptr; - m_input = nullptr; - validate(); - } - -private: - Node *m_input = nullptr; - Node *m_user = nullptr; - Use *m_next = nullptr; - Use **m_prev = nullptr; -}; - -// A node represents an calculation, action, or marker in the graph. Each node has an operation, -// input dependencies and uses. The operation indicates what kind of node it is, e.g.: JSAdd, -// Constant, Region, and so on. Two nodes can have the same operation, but different inputs. -// For example, the expressions 1 + 2 and 3 + 4 will each have a node with an JSAdd operation -// (which is exactly the same operation for both nodes), but the nodes have different inputs (1, and -// 2 in the first expression, while the second operation has 3 and 4 as inputs). -class Node final -{ - Q_DISABLE_COPY_MOVE(Node) - -public: - using Id = uint32_t; - using MemoryPool = QQmlJS::MemoryPool; - class Inputs; - -public: - static Node *create(MemoryPool *pool, Id id, const Operation *op, size_t nInputs, - Node * const *inputs, bool inputsAreExtensible = false); - ~Node() = delete; - - inline bool isDead() const; - inline void kill(); - - Id id() const { return m_id; } - - const Operation *operation() const - { return m_op; } - - void setOperation(const Operation *op) - { m_op = op; } - - Operation::Kind opcode() const - { return operation()->kind(); } - - inline Inputs inputs() const; - void addInput(MemoryPool *pool, Node *in); - void removeInput(unsigned index); - void removeInputs(unsigned start, unsigned count); - void removeAllInputs(); - uint32_t inputCount() const - { return m_nInputs; } - void trimInputCount(unsigned newCount); - - void removeExceptionHandlerUse(); - - Node *input(unsigned idx) const - { - Q_ASSERT(idx < inputCount()); - return m_inputs.at(idx); - } - - Node *effectInput(unsigned effectIndex = 0) const - { - if (operation()->effectInputCount() == 0) - return nullptr; - Q_ASSERT(effectIndex < operation()->effectInputCount()); - return input(operation()->indexOfFirstEffect() + effectIndex); - } - - Node *controlInput(unsigned controlIndex = 0) const - { - if (operation()->controlInputCount() == 0) - return nullptr; - Q_ASSERT(controlIndex < operation()->controlInputCount()); - return input(operation()->indexOfFirstControl() + controlIndex); - } - - Node *frameStateInput() const - { - if (operation()->hasFrameStateInput()) - return input(operation()->indexOfFrameStateInput()); - return nullptr; - } - - void setFrameStateInput(Node *newFramestate) - { - if (operation()->hasFrameStateInput()) - replaceInput(operation()->indexOfFrameStateInput(), newFramestate); - } - - void insertInput(MemoryPool *pool, unsigned index, Node *newInput); - - void replaceInput(Node *oldIn, Node *newIn) - { - for (unsigned i = 0, ei = inputCount(); i != ei; ++i) { - if (input(i) == oldIn) - replaceInput(i, newIn); - } - } - - void replaceInput(unsigned idx, Node *newIn) - { - m_inputs[idx].set(newIn); - } - - class Uses - { - public: - explicit Uses(Node *node) - : m_node(node) - {} - - class const_iterator; - inline const_iterator begin() const; - inline const_iterator end() const; - - bool isEmpty() const; - - private: - Node *m_node; - }; - - Uses uses() { return Uses(this); } - bool hasUses() const { return m_firstUse != nullptr; } - unsigned useCount() const - { - unsigned cnt = 0; - for (Use *it = m_firstUse; it; it = it->m_next) - ++cnt; - return cnt; - } - void replaceAllUsesWith(Node *replacement); - void replaceUses(Node *newValueInput, Node *newEffectInput, Node *newControlInput); - - Node *firstValueUse(); - -private: // types and utility methods - friend class Use; - Node(MemoryPool *pool, Id id, const Operation *op, unsigned nInputs, int capacity); - -private: // fields - Use *m_firstUse = nullptr; - const Operation *m_op = nullptr; - QQmlJS::FixedPoolArray<Use> m_inputs; - unsigned m_nInputs = 0; - Id m_id = 0; -}; - -void Use::set(Node *newInput) -{ - if (m_input) - removeFromList(); - m_input = newInput; - if (newInput) - addToList(&newInput->m_firstUse); -} - -class Node::Inputs final -{ -public: - using value_type = Node *; - - class const_iterator; - inline const_iterator begin() const; - inline const_iterator end() const; - - bool empty() const - { return m_nInputs == 0; } - - unsigned count() const - { return m_nInputs; } - - explicit Inputs(const Use *inputs, unsigned nInputs) - : m_inputs(inputs), m_nInputs(nInputs) - {} - -private: - const Use *m_inputs = nullptr; - unsigned m_nInputs = 0; -}; - -class Node::Inputs::const_iterator final -{ -public: - using iterator_category = std::forward_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = Node *; - using pointer = const value_type *; - using reference = value_type &; - - Node *operator*() const - { return m_inputs->m_input; } - - bool operator==(const const_iterator &other) const - { return m_inputs == other.m_inputs; } - - bool operator!=(const const_iterator &other) const - { return !(*this == other); } - - const_iterator &operator++() - { ++m_inputs; return *this; } - - const_iterator& operator+=(difference_type offset) - { m_inputs += offset; return *this; } - - const_iterator operator+(difference_type offset) const - { return const_iterator(m_inputs + offset); } - - difference_type operator-(const const_iterator &other) const - { return m_inputs - other.m_inputs; } - -private: - friend class Node::Inputs; - - explicit const_iterator(const Use *inputs) - : m_inputs(inputs) - {} - - const Use *m_inputs; -}; - -Node::Inputs::const_iterator Node::Inputs::begin() const -{ return const_iterator(m_inputs); } - -Node::Inputs::const_iterator Node::Inputs::end() const -{ return const_iterator(m_inputs + m_nInputs); } - -Node::Inputs Node::inputs() const -{ - return Inputs(m_inputs.begin(), m_nInputs); -} - -class Node::Uses::const_iterator final -{ -public: - using iterator_category = std::forward_iterator_tag; - using difference_type = int; - using value_type = Node *; - using pointer = Node **; - using reference = Node *&; - - Node *operator*() const - { return m_current->user(); } - - bool operator==(const const_iterator &other) const - { return other.m_current == m_current; } - - bool operator!=(const const_iterator &other) const - { return other.m_current != m_current; } - - const_iterator &operator++() - { m_current = m_next; setNext(); return *this; } - - unsigned inputIndex() const - { return m_current->inputIndex(); } - - bool isUsedAsValue() const - { return inputIndex() < operator*()->operation()->valueInputCount(); } - - bool isUsedAsControl() const - { return operator*()->operation()->indexOfFirstControl() <= inputIndex(); } - -private: - friend class Node::Uses; - - const_iterator() = default; - - explicit const_iterator(Node* node) - : m_current(node->m_firstUse) - { setNext(); } - - void setNext() - { - if (m_current) - m_next = m_current->m_next; - else - m_next = nullptr; - } - -private: - Use *m_current = nullptr; - Use *m_next = nullptr; -}; - -Node::Uses::const_iterator Node::Uses::begin() const -{ return const_iterator(this->m_node); } - -Node::Uses::const_iterator Node::Uses::end() const -{ return const_iterator(); } - -int Use::inputIndex() const -{ - if (!m_user) - return -1; - return int(this - m_user->m_inputs.begin()); -} - -bool Node::isDead() const -{ - Inputs in = inputs(); - return !in.empty() && *in.begin() == nullptr; -} - -void Node::kill() -{ - removeAllInputs(); -} - -class NodeWorkList final -{ - enum State: uint8_t { - Unvisited = 0, - Queued, - Visited, - }; - -public: - NodeWorkList(const Graph *g); - - void reset(); - - bool enqueue(Node *n) - { - State &s = nodeState(n); - if (s == Queued || s == Visited) - return false; - - m_worklist.push_back(n); - s = Queued; - return true; - } - - void enqueue(const std::vector<Node *> &nodes) - { - m_worklist.insert(m_worklist.end(), nodes.begin(), nodes.end()); - for (Node *n : nodes) - nodeState(n) = Queued; - } - - void reEnqueue(Node *n) - { - if (!n) - return; - State &s = nodeState(n); - if (s == Queued) - return; - s = Queued; - m_worklist.push_back(n); - } - - void enqueueAllInputs(Node *n) - { - for (Node *input : n->inputs()) - enqueue(input); - } - - void reEnqueueAllInputs(Node *n) - { - for (Node *input : n->inputs()) - reEnqueue(input); - } - - void enqueueValueInputs(Node *n) - { - for (unsigned i = 0, ei = n->operation()->valueInputCount(); i != ei; ++i) - enqueue(n->input(i)); - } - - void enqueueEffectInputs(Node *n) - { - for (unsigned i = n->operation()->indexOfFirstEffect(), ei = n->operation()->effectInputCount(); i != ei; ++i) - enqueue(n->input(i)); - } - - void enqueueAllUses(Node *n) - { - for (Node *use : n->uses()) - enqueue(use); - } - - Node *dequeueNextNodeForVisiting() - { - while (!m_worklist.empty()) { - Node *n = m_worklist.back(); - m_worklist.pop_back(); - State &s = nodeState(n); - Q_ASSERT(s == Queued); - s = Visited; - return n; - } - - return nullptr; - } - - bool isVisited(Node *n) const - { return nodeState(n) == Visited; } - - bool isEmpty() const - { return m_worklist.empty(); } - - QString status(Node *n) const - { - QString s = QStringLiteral("status for node %1: ").arg(n->id()); - switch (nodeState(n)) { - case Queued: s += QLatin1String("queued"); break; - case Visited: s += QLatin1String("visited"); break; - case Unvisited: s += QLatin1String("unvisited"); break; - } - return s; - } - -private: - State &nodeState(Node *n) - { - const unsigned position(n->id()); - if (position >= m_nodeState.size()) - m_nodeState.resize(position + 1, Unvisited); - - return m_nodeState[position]; - } - - State nodeState(Node *n) const - { return m_nodeState[unsigned(n->id())]; } - -private: - std::vector<Node *> m_worklist; - std::vector<State> m_nodeState; -}; - -class NodeInfo -{ -public: - enum { NoInstructionOffset = -1 }; - -public: - NodeInfo() = default; - - Type type() const { return m_type; } - void setType(Type t) { m_type = t; } - - int currentInstructionOffset() const - { return m_currentInstructionOffset; } - - int nextInstructionOffset() const - { return m_nextInstructionOffset; } - - void setBytecodeOffsets(int current, int next) - { - Q_ASSERT(current != NoInstructionOffset); - Q_ASSERT(next != NoInstructionOffset); - m_currentInstructionOffset = current; - m_nextInstructionOffset = next; - } - -private: - Type m_type; - int m_currentInstructionOffset = NoInstructionOffset; - int m_nextInstructionOffset = NoInstructionOffset; -}; - -class NodeCollector -{ -public: - NodeCollector(const Graph *g, bool collectUses = false, bool skipFramestate = false); - - const std::vector<Node *> &reachable() const - { return m_reachable; } - - void sortById() - { - std::sort(m_reachable.begin(), m_reachable.end(), [](Node *n1, Node *n2) { - return n1->id() < n2->id(); - }); - } - - bool isReachable(Node::Id nodeId) const - { - if (nodeId >= Node::Id(m_isReachable.size())) - return false; - return m_isReachable.at(int(nodeId)); - } - - void markReachable(Node *node) - { - auto nodeId = node->id(); - m_reachable.push_back(node); - if (nodeId >= Node::Id(m_isReachable.size())) - m_isReachable.resize(int(nodeId + 1), false); - m_isReachable.setBit(int(nodeId)); - } - -private: - std::vector<Node *> m_reachable; - BitVector m_isReachable; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4NODE_P_H diff --git a/src/qml/jit/qv4operation.cpp b/src/qml/jit/qv4operation.cpp deleted file mode 100644 index acd5328fd0..0000000000 --- a/src/qml/jit/qv4operation.cpp +++ /dev/null @@ -1,770 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qv4operation_p.h" -#include "qv4runtimesupport_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -OperationBuilder::OperationBuilder(QQmlJS::MemoryPool *graphPool) - : m_graphPool(graphPool) -{} - -OperationBuilder *OperationBuilder::create(QQmlJS::MemoryPool *pool) -{ - return pool->New<OperationBuilder>(pool); -} - -Operation *OperationBuilder::getConstant(Value v) -{ - Type t; - switch (v.type()) { - case Value::Undefined_Type: t = Type::undefinedType(); break; - case Value::Integer_Type: t = Type::int32Type(); break; - case Value::Boolean_Type: t = Type::booleanType(); break; - case Value::Null_Type: t = Type::nullType(); break; - case Value::Double_Type: t = Type::doubleType(); break; - case Value::Managed_Type: t = Type::objectType(); break; - default: - if (v.isEmpty()) - t = Type::emptyType(); - else - Q_UNREACHABLE(); - } - return OperationWithPayload<ConstantPayload>::create( - m_graphPool, Meta::Constant, 0, 0, 0, 1, 0, 0, t, Operation::NoFlags, - ConstantPayload(v)); -} - -Operation *OperationBuilder::getParam(unsigned index, const Function::StringId name) -{ - return OperationWithPayload<ParameterPayload>::create(m_graphPool, Meta::Parameter, - 1, 0, 0, - 1, 0, 0, - Type::anyType(), Operation::NoFlags, - ParameterPayload(index, name)); -} - -Operation *OperationBuilder::getRegion(unsigned nControlInputs) //### cache common operands in the static pool -{ - return Operation::create(m_graphPool, Meta::Region, - 0, 0, uint16_t(nControlInputs), - 0, 0, 1, - Type(), Operation::NoFlags); -} - -Operation *OperationBuilder::getPhi(unsigned nValueInputs) //### cache common operands in the static pool -{ - return Operation::create(m_graphPool, Meta::Phi, - uint16_t(nValueInputs), 0, 1, - 1, 0, 0, - Type::anyType(), Operation::NoFlags); -} - -Operation *OperationBuilder::getEffectPhi(unsigned nEffectInputs) //### cache common operands in the static pool -{ - return Operation::create(m_graphPool, Meta::EffectPhi, - 0, uint16_t(nEffectInputs), 1, - 0, 1, 0, - Type(), Operation::NoFlags); -} - -Operation *OperationBuilder::getUnwindDispatch(unsigned nContinuations, int unwindHandlerOffset, - int fallthroughSuccessor) -{ - return OperationWithPayload<UnwindDispatchPayload>::create( - m_graphPool, Meta::UnwindDispatch, - 0, 1, 1, 0, nContinuations, nContinuations, - Type(), Operation::NoFlags, - UnwindDispatchPayload(unwindHandlerOffset, - fallthroughSuccessor)); -} - -Operation *OperationBuilder::getHandleUnwind(int unwindHandlerOffset) -{ - return OperationWithPayload<HandleUnwindPayload>::create(m_graphPool, Meta::HandleUnwind, - 0, 1, 1, 0, 1, 1, - Type(), Operation::NoFlags, - HandleUnwindPayload(unwindHandlerOffset)); -} - -Operation *OperationBuilder::getFrameState(uint16_t frameSize) -{ - if (m_opFrameState == nullptr) - m_opFrameState = Operation::create(m_graphPool, Meta::FrameState, - frameSize, 0, 0, 0, 0, 1, - Type(), Operation::NoFlags); - else - Q_ASSERT(frameSize == m_opFrameState->valueInputCount()); - - return m_opFrameState; -} - -Operation *OperationBuilder::getStart(uint16_t outputCount) -{ - return Operation::create(m_graphPool, Meta::Start, - 0, 0, 0, - outputCount, 1, 1, - Type(), Operation::NoFlags); -} - -Operation *OperationBuilder::getEnd(uint16_t controlInputCount) -{ - return Operation::create(m_graphPool, Meta::End, - 0, 0, controlInputCount, - 0, 0, 0, - Type(), Operation::NoFlags); -} - -inline Operation *createOperation(Operation::Kind kind, QQmlJS::MemoryPool *staticPool) -{ - auto get = [&](uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount, - uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount, - Type (*typeCreator)(), uint8_t flags) { - return Operation::create(staticPool, kind, inValueCount, inEffectCount, inControlCount, - outValueCount, outEffectCount, outControlCount, typeCreator(), - flags); - }; - - using K = Operation::Kind; - using F = Operation::Flags; - const auto none = &Type::noneType; - const auto any = &Type::anyType; - const auto number = &Type::numberType; - const auto boolean = &Type::booleanType; - - switch (kind) { - case K::Undefined: - return OperationWithPayload<ConstantPayload>::create( - staticPool, K::Undefined, 0, 0, 0, 1, 0, 0, Type::undefinedType(), F::NoFlags, - ConstantPayload(Primitive::undefinedValue())); - case K::Empty: - return OperationWithPayload<ConstantPayload>::create( - staticPool, K::Constant, 0, 0, 0, 1, 0, 0, Type::emptyType(), Operation::NoFlags, - ConstantPayload(Primitive::emptyValue())); - case K::Engine: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags); - case K::CppFrame: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags); - case K::Function: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags); - case K::Jump: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags); - case K::Return: return get(1, 1, 1, 0, 0, 1, none, F::NoFlags); - case K::Branch: return get(1, 0, 1, 0, 0, 2, none, F::HasFrameStateInput | F::NeedsBytecodeOffsets); - case K::IfTrue: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags); - case K::IfFalse: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags); - case K::SelectOutput: return get(3, 1, 1, 1, 1, 1, any, F::NoFlags); - case K::Throw: return get(1, 1, 1, 0, 1, 1, any, F::NeedsBytecodeOffsets); - case K::OnException: return get(0, 0, 1, 0, 0, 1, none, F::NoFlags); - case K::ThrowReferenceError: return get(1, 1, 1, 0, 1, 1, any, F::NeedsBytecodeOffsets); - case K::UnwindToLabel: return get(2, 1, 1, 0, 1, 1, none, F::NoFlags); - case K::LoadRegExp: return get(1, 0, 0, 1, 0, 0, any, F::NoFlags); - case K::ScopedLoad: return get(2, 1, 0, 1, 1, 0, any, F::NoFlags); - case K::ScopedStore: return get(3, 1, 0, 0, 1, 0, none, F::NoFlags); - case K::JSLoadElement: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSGetLookup: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSLoadProperty: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSStoreElement: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSSetLookupStrict: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSSetLookupSloppy: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSStoreProperty: return get(3, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSLoadName: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSLoadGlobalLookup: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSStoreNameSloppy: return get(2, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSStoreNameStrict: return get(2, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSLoadSuperProperty: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSStoreSuperProperty: return get(2, 1, 1, 0, 1, 2, any, F::CanThrow); - case K::JSLoadClosure: return get(1, 1, 0, 1, 1, 0, any, F::Pure); - case K::JSGetIterator: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - - // special case: see GraphBuilder::generate_IteratorNext - case K::JSIteratorNext: return get(2, 1, 1, 2, 1, 1, any, F::NoFlags); - - // special case: see GraphBuilder::generate_IteratorNext - case K::JSIteratorNextForYieldStar: return get(3, 1, 1, 2, 1, 1, any, F::NoFlags); - - case K::JSIteratorClose: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSDeleteProperty: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSDeleteName: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSIn: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSInstanceOf: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::QMLLoadQmlContextPropertyLookup: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - - case K::JSEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - case K::JSGreaterThan: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - case K::JSGreaterEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - case K::JSLessThan: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - case K::JSLessEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - case K::JSStrictEqual: return get(2, 1, 1, 1, 1, 2, boolean, F::CanThrow); - - case K::JSAdd: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSSubtract: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::JSMultiply: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::JSDivide: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::JSModulo: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::JSExponentiate: return get(2, 1, 1, 1, 1, 2, number, F::CanThrow); - - case K::JSBitAnd: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSBitOr: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSBitXor: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSUnsignedShiftRight: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSShiftRight: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSShiftLeft: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - - case K::JSNegate: return get(1, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::JSToNumber: return get(1, 1, 1, 1, 1, 2, number, F::CanThrow); - case K::Alloca: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags); - - //### it is questionable if VAAlloc/VASeal need effect edges - case K::VAAlloc: return get(1, 1, 0, 1, 1, 0, none, F::NoFlags); - - case K::VAStore: return get(3, 0, 0, 1, 0, 0, none, F::NoFlags); - - case K::JSTypeofName: return get(1, 1, 0, 1, 1, 0, any, F::NoFlags); - case K::JSTypeofValue: return get(1, 0, 0, 1, 0, 0, any, F::Pure); - case K::JSDeclareVar: return get(2, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::JSDestructureRestElement: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - - case K::JSCreateCallContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags); - case K::JSCreateCatchContext: return get(2, 1, 1, 1, 1, 1, none, F::NoFlags); - case K::JSCreateWithContext: return get(1, 1, 1, 1, 1, 1, any, F::NoFlags); - case K::JSCreateBlockContext: return get(1, 1, 1, 1, 1, 1, none, F::NoFlags); - case K::JSCloneBlockContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags); - case K::JSCreateScriptContext: return get(1, 1, 1, 1, 1, 1, none, F::NoFlags); - case K::JSPopScriptContext: return get(0, 1, 1, 1, 1, 1, none, F::NoFlags); - case K::PopContext: return get(0, 1, 1, 0, 1, 1, none, F::NoFlags); - - case K::JSThisToObject: return get(1, 1, 1, 0, 1, 2, any, F::NoFlags); - case K::JSCreateMappedArgumentsObject: return get(0, 1, 0, 1, 1, 0, any, F::NoFlags); - case K::JSCreateUnmappedArgumentsObject: return get(0, 1, 0, 1, 1, 0, any, F::NoFlags); - case K::JSCreateRestParameter: return get(1, 0, 0, 1, 0, 0, any, F::NoFlags); - case K::JSLoadSuperConstructor: return get(1, 1, 1, 1, 1, 2, any, F::NoFlags); - case K::JSThrowOnNullOrUndefined: return get(1, 1, 1, 0, 1, 2, none, F::CanThrow); - case K::JSGetTemplateObject: return get(1, 0, 0, 1, 0, 0, any, F::NoFlags); - case K::StoreThis: return get(1, 1, 0, 1, 1, 0, any, F::NoFlags); - - case K::GetException: return get(0, 1, 0, 1, 1, 0, any, F::NoFlags); - case K::SetException: return get(1, 1, 0, 0, 1, 0, any, F::NoFlags); - - case K::ToObject: return get(1, 1, 1, 1, 1, 2, any, F::CanThrow); - case K::ToBoolean: return get(1, 0, 0, 1, 0, 0, boolean, F::Pure); - - case K::IsEmpty: return get(1, 0, 0, 1, 0, 0, boolean, F::Pure); - - case K::BooleanNot: return get(1, 0, 0, 1, 0, 0, boolean, F::NoFlags); - case K::HasException: return get(1, 1, 0, 1, 1, 0, boolean, F::NoFlags); - - case K::Swap: return get(0, 0, 0, 0, 0, 0, none, F::NoFlags); - case K::Move: return get(1, 0, 0, 1, 0, 0, none, F::NoFlags); - - default: // Non-static operations: - return nullptr; - } -} - -Operation *OperationBuilder::staticOperation(Operation::Kind kind) -{ - static QAtomicPointer<Operation *> ops; - if (Operation **staticOps = ops.load()) - return staticOps[kind]; - - static QAtomicInt initializing = 0; - if (initializing.testAndSetOrdered(0, 1)) { - // This is safe now, because we can only run this piece of code once during the life time - // of the application as we can only change initializing from 0 to 1 once. - Operation **staticOps = new Operation *[Meta::KindsEnd]; - static QQmlJS::MemoryPool pool; - for (int i = 0; i < Meta::KindsEnd; ++i) - staticOps[i] = createOperation(Operation::Kind(i), &pool); - bool success = ops.testAndSetOrdered(nullptr, staticOps); - Q_ASSERT(success); - } else { - // Unfortunately we need to busy wait now until the other thread finishes the static - // initialization; - while (!ops.load()) {} - } - - return ops.load()[kind]; -} - -Operation *OperationBuilder::getJSVarArgsCall(Operation::Kind kind, uint16_t argc) -{ - return Operation::create(m_graphPool, kind, - argc, 1, 1, 1, 1, 2, - Type::anyType(), Operation::CanThrow); -} - -Operation *OperationBuilder::getJSTailCall(uint16_t argc) -{ - return Operation::create(m_graphPool, Meta::JSTailCall, - argc, 1, 1, 0, 0, 1, - Type(), Operation::NoFlags); -} - -Operation *OperationBuilder::getTailCall() -{ - // special varargs call, takes cppframe, engine, func, thisObject, argv, argc - return Operation::create(m_graphPool, Meta::TailCall, - 6, 1, 1, 0, 0, 1, - Type(), Operation::NoFlags); -} - -Operation *OperationBuilder::getCall(Operation::Kind callee) -{ - const bool canThrow = CallPayload::canThrow(callee); - const Type retTy = CallPayload::returnType(callee); - uint16_t nControlInputs = 0; - uint16_t nControlOutputs = 0; - if (canThrow) { - nControlInputs = 1; - nControlOutputs += 2; - } - if (CallPayload::changesContext(callee)) { - nControlInputs = 1; - nControlOutputs = std::max<uint16_t>(nControlInputs, 1); - } - if (callee == Meta::Throw || callee == Meta::ThrowReferenceError || - callee == Meta::JSIteratorNext || callee == Meta::JSIteratorNextForYieldStar) { - nControlInputs = 1; - nControlOutputs = 1; - } - Operation::Flags flags = Operation::NoFlags; - if (canThrow) - flags = Operation::Flags(flags | Operation::CanThrow); - if (CallPayload::isPure(callee)) - flags = Operation::Flags(flags | Operation::Pure); - const uint16_t nEffects = (flags & Operation::Pure) ? 0 : 1; - const uint16_t nValueOutputs = retTy.isNone() ? 0 : 1; - const uint16_t nValueInputs = CallPayload::argc(callee); - - return OperationWithPayload<CallPayload>::create( - m_graphPool, Meta::Call, - nValueInputs, nEffects, nControlInputs, - nValueOutputs, nEffects, nControlOutputs, - retTy, flags, - CallPayload(callee)); -} - -Operation *OperationBuilder::getVASeal(uint16_t nElements) -{ - return Operation::create(m_graphPool, Meta::VASeal, - nElements + 1, 1, 0, 1, 1, 0, - Type::anyType(), Operation::NoFlags); -} - -QString Operation::debugString() const -{ - switch (kind()) { - - case Meta::Constant: - return QStringLiteral("Constant[%1]").arg(ConstantPayload::get(*this)->debugString()); - case Meta::Parameter: - return QStringLiteral("Parameter[%1]").arg(ParameterPayload::get(*this)->debugString()); - case Meta::Call: - return QStringLiteral("Call[%1]").arg(CallPayload::get(*this)->debugString()); - case Meta::UnwindDispatch: - return QStringLiteral("UnwindDispatch[%1]").arg(UnwindDispatchPayload::get(*this) - ->debugString()); - case Meta::HandleUnwind: - return QStringLiteral("HandleUnwind[%1]").arg(HandleUnwindPayload::get(*this) - ->debugString()); - - default: - return QString::fromLatin1(QMetaEnum::fromType<Meta::OpKind>().valueToKey(kind())); - } -} - -QString ConstantPayload::debugString() const -{ - return debugString(m_value); -} - -QString ConstantPayload::debugString(QV4::Value v) -{ - if (v.isManaged()) - return QString::asprintf("Ptr: %p", v.heapObject()); - if (v.isEmpty()) - return QStringLiteral("empty"); - return v.toQStringNoThrow(); -} - -QString ParameterPayload::debugString() const -{ - return QStringLiteral("%1").arg(m_index); -} - -QString UnwindDispatchPayload::debugString() const -{ - return QStringLiteral("%1, %2").arg(QString::number(m_fallthroughSuccessor), - QString::number(m_unwindHandlerOffset)); -} - -QString HandleUnwindPayload::debugString() const -{ - return QStringLiteral("%1").arg(m_unwindHandlerOffset); -} - -static Type translateType(RuntimeSupport::ArgumentType t) -{ - switch (t) { - case RuntimeSupport::ArgumentType::Int: return Type::int32Type(); - case RuntimeSupport::ArgumentType::Bool: return Type::booleanType(); - case RuntimeSupport::ArgumentType::Void: return Type(); - case RuntimeSupport::ArgumentType::Engine: return Type::rawPointerType(); - case RuntimeSupport::ArgumentType::ValueRef: return Type::anyType(); - case RuntimeSupport::ArgumentType::ValueArray: return Type::anyType(); - case RuntimeSupport::ArgumentType::ReturnedValue: return Type::anyType(); - default: Q_UNREACHABLE(); - } -} - -template<template<typename Operation> class M /* MetaOperation */, typename ReturnValue> -static ReturnValue operateOnRuntimeCall(Operation::Kind kind, bool abortOnMissingCall = true) -{ - using K = Operation::Kind; - using R = Runtime; - - switch (kind) { - case K::Throw: return M<R::ThrowException>::doIt(); - case K::ThrowReferenceError: return M<R::ThrowReferenceError>::doIt(); - - case K::JSEqual: return M<R::CompareEqual>::doIt(); - case K::JSGreaterThan: return M<R::CompareGreaterThan>::doIt(); - case K::JSGreaterEqual: return M<R::CompareGreaterEqual>::doIt(); - case K::JSLessThan: return M<R::CompareLessThan>::doIt(); - case K::JSLessEqual: return M<R::CompareLessEqual>::doIt(); - case K::JSStrictEqual: return M<R::CompareStrictEqual>::doIt(); - - case K::JSBitAnd: return M<R::BitAnd>::doIt(); - case K::JSBitOr: return M<R::BitOr>::doIt(); - case K::JSBitXor: return M<R::BitXor>::doIt(); - case K::JSUnsignedShiftRight: return M<R::UShr>::doIt(); - case K::JSShiftRight: return M<R::Shr>::doIt(); - case K::JSShiftLeft: return M<R::Shl>::doIt(); - - case K::JSAdd: return M<R::Add>::doIt(); - case K::JSSubtract: return M<R::Sub>::doIt(); - case K::JSMultiply: return M<R::Mul>::doIt(); - case K::JSDivide: return M<R::Div>::doIt(); - case K::JSModulo: return M<R::Mod>::doIt(); - case K::JSExponentiate: return M<R::Exp>::doIt(); - - case K::ToBoolean: return M<R::ToBoolean>::doIt(); - case K::ToObject: return M<R::ToObject>::doIt(); - - case K::JSNegate: return M<R::UMinus>::doIt(); - case K::JSToNumber: return M<R::ToNumber>::doIt(); - - case K::JSLoadName: return M<R::LoadName>::doIt(); - case K::JSLoadElement: return M<R::LoadElement>::doIt(); - case K::JSStoreElement: return M<R::StoreElement>::doIt(); - case K::JSGetLookup: return M<R::GetLookup>::doIt(); - case K::JSSetLookupStrict: return M<R::SetLookupStrict>::doIt(); - case K::JSSetLookupSloppy: return M<R::SetLookupSloppy>::doIt(); - case K::JSLoadProperty: return M<R::LoadProperty>::doIt(); - case K::JSStoreProperty: return M<R::StoreProperty>::doIt(); - case K::JSLoadGlobalLookup: return M<R::LoadGlobalLookup>::doIt(); - case K::JSStoreNameSloppy: return M<R::StoreNameSloppy>::doIt(); - case K::JSStoreNameStrict: return M<R::StoreNameStrict>::doIt(); - case K::JSLoadSuperProperty: return M<R::LoadSuperProperty>::doIt(); - case K::JSStoreSuperProperty: return M<R::StoreSuperProperty>::doIt(); - case K::JSLoadClosure: return M<R::Closure>::doIt(); - case K::JSGetIterator: return M<R::GetIterator>::doIt(); - case K::JSIteratorNext: return M<R::IteratorNext>::doIt(); - case K::JSIteratorNextForYieldStar: return M<R::IteratorNextForYieldStar>::doIt(); - case K::JSIteratorClose: return M<R::IteratorClose>::doIt(); - case K::JSDeleteProperty: return M<R::DeleteProperty>::doIt(); - case K::JSDeleteName: return M<R::DeleteName>::doIt(); - case K::JSIn: return M<R::In>::doIt(); - case K::JSInstanceOf: return M<R::Instanceof>::doIt(); - case K::QMLLoadQmlContextPropertyLookup: return M<R::LoadQmlContextPropertyLookup>::doIt(); - - case K::JSTypeofName: return M<R::TypeofName>::doIt(); - case K::JSTypeofValue: return M<R::TypeofValue>::doIt(); - case K::JSDeclareVar: return M<R::DeclareVar>::doIt(); - case K::JSDestructureRestElement: return M<R::DestructureRestElement>::doIt(); - case K::JSThisToObject: return M<R::ConvertThisToObject>::doIt(); - case K::JSCreateMappedArgumentsObject: return M<R::CreateMappedArgumentsObject>::doIt(); - case K::JSCreateUnmappedArgumentsObject: return M<R::CreateUnmappedArgumentsObject>::doIt(); - case K::JSCreateRestParameter: return M<R::CreateRestParameter>::doIt(); - case K::JSLoadSuperConstructor: return M<R::LoadSuperConstructor>::doIt(); - case K::JSThrowOnNullOrUndefined: return M<R::ThrowOnNullOrUndefined>::doIt(); - - case K::JSCreateCallContext: return M<R::PushCallContext>::doIt(); - case K::JSCreateCatchContext: return M<R::PushCatchContext>::doIt(); - case K::JSCreateWithContext: return M<R::PushWithContext>::doIt(); - case K::JSCreateBlockContext: return M<R::PushBlockContext>::doIt(); - case K::JSCloneBlockContext: return M<R::CloneBlockContext>::doIt(); - case K::JSCreateScriptContext: return M<R::PushScriptContext>::doIt(); - case K::JSPopScriptContext: return M<R::PopScriptContext>::doIt(); - - case K::LoadRegExp: return M<R::RegexpLiteral>::doIt(); - case K::JSGetTemplateObject: return M<R::GetTemplateObject>::doIt(); - - case K::JSCallName: return M<R::CallName>::doIt(); - case K::JSCallValue: return M<R::CallValue>::doIt(); - case K::JSCallElement: return M<R::CallElement>::doIt(); - case K::JSCallLookup: return M<R::CallPropertyLookup>::doIt(); - case K::JSCallProperty: return M<R::CallProperty>::doIt(); - case K::JSCallGlobalLookup: return M<R::CallGlobalLookup>::doIt(); - case K::JSCallPossiblyDirectEval: return M<R::CallPossiblyDirectEval>::doIt(); - case K::JSCallWithReceiver: return M<R::CallWithReceiver>::doIt(); - case K::JSDefineObjectLiteral: return M<R::ObjectLiteral>::doIt(); - case K::JSDefineArray: return M<R::ArrayLiteral>::doIt(); - case K::JSCallWithSpread: return M<R::CallWithSpread>::doIt(); - case K::JSConstruct: return M<R::Construct>::doIt(); - case K::JSConstructWithSpread: return M<R::ConstructWithSpread>::doIt(); - case K::JSTailCall: return M<R::TailCall>::doIt(); - case K::JSCreateClass: return M<R::CreateClass>::doIt(); - default: - if (abortOnMissingCall) - Q_UNREACHABLE(); - else - return ReturnValue(); - } -} - -template<typename Method> -struct IsRuntimeMethodOperation -{ - static constexpr bool doIt() { return true; } -}; - -bool CallPayload::isRuntimeCall(Operation::Kind m) -{ - return operateOnRuntimeCall<IsRuntimeMethodOperation, bool>(m, false); -} - -QString CallPayload::debugString() const -{ - return QString::fromLatin1(QMetaEnum::fromType<Meta::OpKind>().valueToKey(m_callee)); -} - -template<typename Method> -struct MethodArgcOperation -{ - static constexpr unsigned doIt() { return RuntimeSupport::argumentCount<Method>(); } -}; - -unsigned CallPayload::argc(Operation::Kind callee) -{ - return operateOnRuntimeCall<MethodArgcOperation, unsigned>(callee); -} - -template<typename Method> struct MethodArg1TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg1Type<Method>(); } }; -template<typename Method> struct MethodArg2TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg2Type<Method>(); } }; -template<typename Method> struct MethodArg3TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg3Type<Method>(); } }; -template<typename Method> struct MethodArg4TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg4Type<Method>(); } }; -template<typename Method> struct MethodArg5TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg5Type<Method>(); } }; -template<typename Method> struct MethodArg6TyOperation { static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::arg6Type<Method>(); } }; - -static RuntimeSupport::ArgumentType untranslatedArgumentType(Operation::Kind m, unsigned arg) -{ - if (m == Meta::JSTailCall) { - if (arg < 4) - return RuntimeSupport::ArgumentType::ValueRef; - else - return RuntimeSupport::ArgumentType::Invalid; - } - - switch (arg) { - case 0: return operateOnRuntimeCall<MethodArg1TyOperation, RuntimeSupport::ArgumentType>(m); - case 1: return operateOnRuntimeCall<MethodArg2TyOperation, RuntimeSupport::ArgumentType>(m); - case 2: return operateOnRuntimeCall<MethodArg3TyOperation, RuntimeSupport::ArgumentType>(m); - case 3: return operateOnRuntimeCall<MethodArg4TyOperation, RuntimeSupport::ArgumentType>(m); - case 4: return operateOnRuntimeCall<MethodArg5TyOperation, RuntimeSupport::ArgumentType>(m); - case 5: return operateOnRuntimeCall<MethodArg6TyOperation, RuntimeSupport::ArgumentType>(m); - default: return RuntimeSupport::ArgumentType::Invalid; - } -} - -bool CallPayload::needsStorageOnJSStack(Operation::Kind m, unsigned arg, const Operation *op, - Type nodeType) -{ - auto argTy = untranslatedArgumentType(m, arg); - if (argTy == RuntimeSupport::ArgumentType::ValueArray) - return true; - if (argTy != RuntimeSupport::ArgumentType::ValueRef) - return false; - - if (op->kind() == Meta::Constant) - return true; - - return !nodeType.isObject() && !nodeType.isRawPointer() && !nodeType.isAny(); -} - -template<typename Method> -struct MethodRetTyOperation -{ - static constexpr RuntimeSupport::ArgumentType doIt() { return RuntimeSupport::retType<Method>(); } -}; - -Type CallPayload::returnType(Operation::Kind m) -{ - if (m == Meta::JSTailCall) - return Type(); - - auto t = operateOnRuntimeCall<MethodRetTyOperation, RuntimeSupport::ArgumentType>(m); - return translateType(t); -} - -static int firstArgumentPositionForType(Operation::Kind m, RuntimeSupport::ArgumentType type) -{ - if (operateOnRuntimeCall<MethodArg1TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 1; - if (operateOnRuntimeCall<MethodArg2TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 2; - if (operateOnRuntimeCall<MethodArg3TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 3; - if (operateOnRuntimeCall<MethodArg4TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 4; - if (operateOnRuntimeCall<MethodArg5TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 5; - if (operateOnRuntimeCall<MethodArg6TyOperation, RuntimeSupport::ArgumentType>(m) == type) - return 6; - return -1; -} - -unsigned CallPayload::varArgsStart(Operation::Kind m) -{ - if (m == Meta::JSTailCall) - return 4 - 1; - - int pos = firstArgumentPositionForType(m, RuntimeSupport::ArgumentType::ValueArray) - 1; - Q_ASSERT(pos >= 0); - return pos; -} - -bool CallPayload::isVarArgsCall(Operation::Kind m) -{ - if (m == Meta::JSTailCall) - return true; - if (lastArgumentIsOutputValue(m)) - return false; - return firstArgumentPositionForType(m, RuntimeSupport::ArgumentType::ValueArray) != -1; -} - -bool CallPayload::isVarArgsCall() const -{ - return isVarArgsCall(m_callee); -} - -template<typename Method> -struct MethodsLastArgumentIsOutputValue -{ - static constexpr bool doIt() { return Method::lastArgumentIsOutputValue; } -}; - -bool CallPayload::lastArgumentIsOutputValue(Operation::Kind m) -{ - return operateOnRuntimeCall<MethodsLastArgumentIsOutputValue, bool>(m); -} - -template<typename Method> -struct MethodChangesContext -{ - static constexpr bool doIt() { return Method::changesContext; } -}; - -bool CallPayload::changesContext(Operation::Kind m) -{ - return operateOnRuntimeCall<MethodChangesContext, bool>(m); -} - -template<typename Method> -struct MethodIsPure -{ - static constexpr bool doIt() { return Method::pure; } -}; - -bool CallPayload::isPure(Operation::Kind m) -{ - return operateOnRuntimeCall<MethodIsPure, bool>(m); -} - -template<typename Method> -struct MethodCanThrow -{ - static constexpr bool doIt() { return Method::throws; } -}; - -bool CallPayload::canThrow(Operation::Kind m) -{ - switch (m) { - case Meta::Throw: Q_FALLTHROUGH(); - case Meta::ThrowReferenceError: - // the execution path following these instructions is already linked up to the exception handler - return false; - case Meta::JSIteratorNext: Q_FALLTHROUGH(); - case Meta::JSIteratorNextForYieldStar: - // special case: see GraphBuilder::generate_IteratorNext - return false; - default: - return operateOnRuntimeCall<MethodCanThrow, bool>(m); - } -} - -bool CallPayload::takesEngineAsArg(Operation::Kind m, int arg) -{ - return untranslatedArgumentType(m, arg) == RuntimeSupport::ArgumentType::Engine; -} - -bool CallPayload::takesFunctionAsArg(Operation::Kind m, int arg) -{ - return untranslatedArgumentType(m, arg) == RuntimeSupport::ArgumentType::Function; -} - -bool CallPayload::takesFrameAsArg(Operation::Kind m, int arg) -{ - return untranslatedArgumentType(m, arg) == RuntimeSupport::ArgumentType::Frame; -} - -template<typename Method> -struct GetMethodPtr -{ - static constexpr void *doIt() { return reinterpret_cast<void *>(&Method::call); } -}; - -void *CallPayload::getMethodPtr(Operation::Kind m) -{ - return operateOnRuntimeCall<GetMethodPtr, void *>(m); -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4operation_p.h b/src/qml/jit/qv4operation_p.h deleted file mode 100644 index 43214023e8..0000000000 --- a/src/qml/jit/qv4operation_p.h +++ /dev/null @@ -1,567 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4OPERATION_P_H -#define QV4OPERATION_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qv4ir_p.h> -#include <private/qqmljsmemorypool_p.h> - -#include <QtCore/qatomic.h> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -namespace Meta { -enum OpKind: uint16_t { - FrameState, - Start, - End, - - Undefined, - Constant, - Parameter, - Empty, - Engine, - CppFrame, - Function, - - Jump, - Return, - JSTailCall, - TailCall, - Branch, - IfTrue, - IfFalse, - Region, - OnException, - Phi, - EffectPhi, - SelectOutput, - UnwindDispatch, - UnwindToLabel, - HandleUnwind, - Throw, - ThrowReferenceError, - - Call, - - LoadRegExp, - ScopedLoad, - ScopedStore, - - JSLoadElement, - JSStoreElement, - JSGetLookup, - JSSetLookupStrict, - JSSetLookupSloppy, - JSLoadProperty, - JSStoreProperty, - JSLoadName, - JSLoadGlobalLookup, - JSStoreNameSloppy, - JSStoreNameStrict, - JSLoadSuperProperty, - JSStoreSuperProperty, - JSLoadClosure, - JSGetIterator, - JSIteratorNext, - JSIteratorNextForYieldStar, - JSIteratorClose, - JSDeleteProperty, - JSDeleteName, - JSIn, - JSInstanceOf, - - /* ok, these are qml object ops, but we don't care for now and treat them as JS */ - QMLLoadQmlContextPropertyLookup, - QMLCallQmlContextPropertyLookup, - - JSEqual, - JSGreaterThan, - JSGreaterEqual, - JSLessThan, - JSLessEqual, - JSStrictEqual, - - JSAdd, - JSSubtract, - JSMultiply, - JSDivide, - JSModulo, - JSExponentiate, - - JSBitAnd, - JSBitOr, - JSBitXor, - JSUnsignedShiftRight, - JSShiftRight, - JSShiftLeft, - - JSNegate, - JSToNumber, - - JSCallName, - JSCallValue, - JSCallElement, - JSCallProperty, - JSCallLookup, - JSCallGlobalLookup, - JSCallPossiblyDirectEval, - JSCallWithReceiver, - JSCallWithSpread, - JSDefineObjectLiteral, - JSDefineArray, - JSCreateClass, - JSConstruct, - JSConstructWithSpread, - - JSTypeofName, - JSTypeofValue, - JSDeclareVar, - JSDestructureRestElement, - JSThisToObject, - JSCreateMappedArgumentsObject, - JSCreateUnmappedArgumentsObject, - JSCreateRestParameter, - JSLoadSuperConstructor, - JSThrowOnNullOrUndefined, - JSGetTemplateObject, - StoreThis, - - JSCreateCallContext, - JSCreateCatchContext, - JSCreateWithContext, - JSCreateBlockContext, - JSCloneBlockContext, - JSCreateScriptContext, - JSPopScriptContext, - PopContext, - - GetException, - SetException, - - ToObject, - ToBoolean, - - //### do we need this? Or should a later phase generate JumpIsEmpty? - IsEmpty, - - Alloca, - VAAlloc, - VAStore, - VASeal, - - BooleanNot, - HasException, - - // Low level, used by the register allocator and stack allocator: - Swap, - Move, - KindsEnd -}; -Q_NAMESPACE -Q_ENUM_NS(OpKind) -} // namespace Ops - -class Operation -{ - Q_DISABLE_COPY_MOVE(Operation) - -public: - using Kind = Meta::OpKind; - - enum Flags: uint8_t { - NoFlags = 0, - ThrowsFlag = 1 << 0, - Pure = 1 << 1, // no read/write side effect, cannot throw, cannot deopt, and is idempotent - NeedsBytecodeOffsets = 1 << 2, - - CanThrow = ThrowsFlag | NeedsBytecodeOffsets, - - HasFrameStateInput = 1 << 3, - }; - -public: - static Operation *create(QQmlJS::MemoryPool *pool, Kind kind, uint16_t inValueCount, - uint16_t inEffectCount, uint16_t inControlCount, - uint16_t outValueCount, uint16_t outEffectCount, - uint16_t outControlCount, Type type, uint8_t flags) - { - return pool->New<Operation>(kind, inValueCount, inEffectCount, inControlCount, - outValueCount, outEffectCount, outControlCount, - type, Flags(flags)); - } - - Kind kind() const - { return m_kind; } - - bool isConstant() const - { - switch (kind()) { - case Meta::Undefined: Q_FALLTHROUGH(); - case Meta::Constant: - case Meta::Empty: - return true; - default: - return false; - } - } - - QString debugString() const; - - uint16_t valueInputCount() const { return m_inValueCount; } - uint16_t effectInputCount() const { return m_inEffectCount; } - uint16_t controlInputCount() const { return m_inControlCount; } - uint16_t valueOutputCount() const { return m_outValueCount; } - uint16_t effectOutputCount() const { return m_outEffectCount; } - uint16_t controlOutputCount() const { return m_outControlCount; } - - unsigned indexOfFirstEffect() const { return m_inValueCount; } - unsigned indexOfFirstControl() const { return m_inValueCount + m_inEffectCount; } - unsigned indexOfFrameStateInput() const - { - return hasFrameStateInput() ? indexOfFirstControl() + m_inControlCount - : std::numeric_limits<unsigned>::max(); - } - - Type type() const - { return m_type; } - - bool canThrow() const - { return m_flags & ThrowsFlag; } - - bool isPure() const - { return m_flags & Pure; } - - bool needsBytecodeOffsets() const - { return m_flags & NeedsBytecodeOffsets; } - - bool hasFrameStateInput() const - { return m_flags & HasFrameStateInput; } - - unsigned totalInputCount() const - { - return valueInputCount() + effectInputCount() + controlInputCount() + - (hasFrameStateInput() ? 1 : 0); - } - unsigned totalOutputCount() const { return valueOutputCount() + effectOutputCount() + controlOutputCount(); } - -protected: - friend class QQmlJS::MemoryPool; - Operation(Kind kind, - uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount, - uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount, - Type type, uint8_t flags) - : m_kind(kind) - , m_inValueCount(inValueCount) - , m_inEffectCount(inEffectCount) - , m_inControlCount(inControlCount) - , m_outValueCount(outValueCount) - , m_outEffectCount(outEffectCount) - , m_outControlCount(outControlCount) - , m_type(type) - , m_flags(Flags(flags)) - { - } - - ~Operation() = default; - -private: - Kind m_kind; - uint16_t m_inValueCount; - uint16_t m_inEffectCount; - uint16_t m_inControlCount; - uint16_t m_outValueCount; - uint16_t m_outEffectCount; - uint16_t m_outControlCount; - Type m_type; - Flags m_flags; -}; - -template <typename Payload> -class OperationWithPayload: public Operation -{ -public: - static OperationWithPayload *create(QQmlJS::MemoryPool *pool, Kind kind, - uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount, - uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount, - Type type, Flags flags, Payload payload) - { - return pool->New<OperationWithPayload>(kind, inValueCount, inEffectCount, inControlCount, - outValueCount, outEffectCount, outControlCount, - type, flags, payload); - } - - const Payload &payload() const - { return m_payload; } - -protected: - friend class QQmlJS::MemoryPool; - OperationWithPayload(Kind kind, - uint16_t inValueCount, uint16_t inEffectCount, uint16_t inControlCount, - uint16_t outValueCount, uint16_t outEffectCount, uint16_t outControlCount, - Type type, Flags flags, Payload payload) - : Operation(kind, - inValueCount, inEffectCount, inControlCount, - outValueCount, outEffectCount, outControlCount, - type, flags) - , m_payload(payload) - {} - - ~OperationWithPayload() = default; - -private: - Payload m_payload; -}; - -class ConstantPayload -{ -public: - explicit ConstantPayload(QV4::Value v) - : m_value(v) - {} - - QV4::Value value() const - { return m_value; } - - static const ConstantPayload *get(const Operation &op) - { - if (op.kind() != Meta::Constant) - return nullptr; - - return &static_cast<const OperationWithPayload<ConstantPayload>&>(op).payload(); - } - - QString debugString() const; - static QString debugString(QV4::Value v); - -private: - QV4::Value m_value; -}; - -class ParameterPayload -{ -public: - ParameterPayload(size_t index, Function::StringId stringId) - : m_index(index) - , m_stringId(stringId) - {} - - size_t parameterIndex() const - { return m_index; } - - Function::StringId stringId() const - { return m_stringId; } - - static const ParameterPayload *get(const Operation &op) - { - if (op.kind() != Meta::Parameter) - return nullptr; - - return &static_cast<const OperationWithPayload<ParameterPayload>&>(op).payload(); - } - - QString debugString() const; - -private: - size_t m_index; - Function::StringId m_stringId; -}; - -class CallPayload -{ -public: - CallPayload(Operation::Kind callee) - : m_callee(callee) - {} - - static const CallPayload *get(const Operation &op) - { - if (op.kind() != Meta::Call) - return nullptr; - - return &static_cast<const OperationWithPayload<CallPayload>&>(op).payload(); - } - - static bool isRuntimeCall(Operation::Kind m); - - Operation::Kind callee() const { return m_callee; } - QString debugString() const; - - unsigned argc() const { return argc(m_callee); } - static unsigned argc(Operation::Kind callee); - static bool needsStorageOnJSStack(Operation::Kind m, unsigned arg, const Operation *op, - Type nodeType); - static Type returnType(Operation::Kind m); - static int engineArgumentPosition(Operation::Kind m); - static int functionArgumentPosition(Operation::Kind m); - - static constexpr unsigned NoVarArgs = std::numeric_limits<unsigned>::max(); - static unsigned varArgsStart(Operation::Kind m); - static bool isVarArgsCall(Operation::Kind m); - bool isVarArgsCall() const; - static bool lastArgumentIsOutputValue(Operation::Kind m); - static bool changesContext(Operation::Kind m); - static bool isPure(Operation::Kind m); - static bool canThrow(Operation::Kind m); - static bool takesEngineAsArg(Operation::Kind m, int arg); - static bool takesFunctionAsArg(Operation::Kind m, int arg); - static bool takesFrameAsArg(Operation::Kind m, int arg); - static void *getMethodPtr(Operation::Kind m); - -private: - Operation::Kind m_callee; -}; - -class UnwindDispatchPayload -{ -public: - UnwindDispatchPayload(int unwindHandlerOffset, int fallthroughSuccessor) - : m_unwindHandlerOffset(unwindHandlerOffset) - , m_fallthroughSuccessor(fallthroughSuccessor) - {} - - int unwindHandlerOffset() const - { return m_unwindHandlerOffset; } - - int fallthroughSuccessor() const //### unused... - { return m_fallthroughSuccessor; } - - static const UnwindDispatchPayload *get(const Operation &op) - { - if (op.kind() != Meta::UnwindDispatch) - return nullptr; - - return &static_cast<const OperationWithPayload<UnwindDispatchPayload>&>(op).payload(); - } - - QString debugString() const; - -private: - int m_unwindHandlerOffset; - int m_fallthroughSuccessor; -}; - -class HandleUnwindPayload -{ -public: - HandleUnwindPayload(int unwindHandlerOffset) - : m_unwindHandlerOffset(unwindHandlerOffset) - {} - - int unwindHandlerOffset() const - { return m_unwindHandlerOffset; } - - static const HandleUnwindPayload *get(const Operation &op) - { - if (op.kind() != Meta::HandleUnwind) - return nullptr; - - return &static_cast<const OperationWithPayload<HandleUnwindPayload>&>(op).payload(); - } - - QString debugString() const; - -private: - int m_unwindHandlerOffset; -}; - -class OperationBuilder -{ - Q_DISABLE_COPY_MOVE(OperationBuilder) - - friend class QQmlJS::MemoryPool; - OperationBuilder(QQmlJS::MemoryPool *graphPool); - -public: - static OperationBuilder *create(QQmlJS::MemoryPool *pool); - ~OperationBuilder() = delete; - - Operation *getConstant(QV4::Value v); - Operation *getFrameState(uint16_t frameSize); - Operation *getStart(uint16_t outputCount); - Operation *getEnd(uint16_t controlInputCount); - Operation *getParam(unsigned index, Function::StringId name); - Operation *getRegion(unsigned nControlInputs); - Operation *getPhi(unsigned nValueInputs); - Operation *getEffectPhi(unsigned nEffectInputs); - Operation *getUnwindDispatch(unsigned nControlOutputs, int unwindHandlerOffset, int fallthroughSuccessor); - Operation *getHandleUnwind(int unwindHandlerOffset); - - template<Operation::Kind kind> - Operation *get() { - return staticOperation(kind); - } - - Operation *getVASeal(uint16_t nElements); - - Operation *getJSVarArgsCall(Operation::Kind kind, uint16_t argc); - Operation *getJSTailCall(uint16_t argc); - Operation *getTailCall(); - - Operation *getCall(Operation::Kind callee); - -private: - QQmlJS::MemoryPool *m_graphPool; // used to store per-graph nodes - Operation *m_opFrameState = nullptr; - static Operation *staticOperation(Operation::Kind kind); -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4OPERATION_P_H diff --git a/src/qml/jit/qv4runtimesupport_p.h b/src/qml/jit/qv4runtimesupport_p.h deleted file mode 100644 index 0dc6022331..0000000000 --- a/src/qml/jit/qv4runtimesupport_p.h +++ /dev/null @@ -1,255 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4RUNTIMESUPPORT_P_H -#define QV4RUNTIMESUPPORT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qv4runtimeapi_p.h> - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { -namespace RuntimeSupport { - -template <typename T> -struct CountArguments { - static constexpr unsigned count = 0; -}; -template <typename RetTy, typename... Args> -struct CountArguments<RetTy (*)(Args... args)> { - static constexpr unsigned count = sizeof...(Args) ; -}; - -template<typename M> -static constexpr unsigned argumentCount() { - using type = decltype(&M::call); - return CountArguments<type>::count; -} - -enum class ArgumentType { - Invalid, - Engine, - Frame, - Function, - ValueRef, - ValueArray, - ReturnedValue, - Int, - Bool, - Void, -}; - - -template <typename T> -struct JavaScriptType -{ - // No default type. We want to make sure everything we do is actually recognized. -}; - -template <typename T> -struct ReturnValue -{ - // No default type. -}; - -template <int I, typename T> -struct Argument -{ - // For simplicity, we add a default here. Otherwise we would need to spell out more - // combinations of I and number of arguments of T. - static constexpr ArgumentType type = ArgumentType::Invalid; -}; - -template <typename RetTy, typename T, typename... Args> -struct Argument<1, RetTy (*)(T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename Arg1, typename T, typename... Args> -struct Argument<2, RetTy (*)(Arg1, T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename Arg1, typename Arg2, typename T, - typename... Args> -struct Argument<3, RetTy (*)(Arg1, Arg2, T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename Arg1, typename Arg2, - typename Arg3, typename T, typename... Args> -struct Argument<4, RetTy (*)(Arg1, Arg2, Arg3, T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename Arg1, typename Arg2, - typename Arg3, typename Arg4, typename T, typename... Args> -struct Argument<5, RetTy (*)(Arg1, Arg2, Arg3, Arg4, T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename Arg1, typename Arg2, - typename Arg3, typename Arg4, typename Arg5, typename T, typename... Args> -struct Argument<6, RetTy (*)(Arg1, Arg2, Arg3, Arg4, Arg5, T, Args... args)> { - static constexpr ArgumentType type = JavaScriptType<T>::type; -}; - -template <typename RetTy, typename... Args> -struct ReturnValue<RetTy (*)(Args... args)> { - static constexpr ArgumentType type = JavaScriptType<RetTy>::type; -}; - -template<> -struct JavaScriptType<QV4::ExecutionEngine *> -{ - static constexpr ArgumentType type = ArgumentType::Engine; -}; - -template<> -struct JavaScriptType<QV4::CppStackFrame *> -{ - static constexpr ArgumentType type = ArgumentType::Frame; -}; - -template<> -struct JavaScriptType<QV4::Function *> -{ - static constexpr ArgumentType type = ArgumentType::Function; -}; - -template<> -struct JavaScriptType<const QV4::Value &> -{ - static constexpr ArgumentType type = ArgumentType::ValueRef; -}; - -template<> -// We need to pass Value * in order to match a parmeter Value[]. -struct JavaScriptType<QV4::Value *> -{ - static constexpr ArgumentType type = ArgumentType::ValueArray; -}; - -template<> -struct JavaScriptType<int> -{ - static constexpr ArgumentType type = ArgumentType::Int; -}; - -template<> -struct JavaScriptType<QV4::Bool> -{ - static constexpr ArgumentType type = ArgumentType::Bool; -}; - -template<> -struct JavaScriptType<QV4::ReturnedValue> -{ - static constexpr ArgumentType type = ArgumentType::ReturnedValue; -}; - -template<> -struct JavaScriptType<void> -{ - static constexpr ArgumentType type = ArgumentType::Void; -}; - -template<typename M> -static constexpr ArgumentType retType() { - using Type = decltype(&M::call); - return ReturnValue<Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg1Type() { - using Type = decltype(&M::call); - return Argument<1, Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg2Type() { - using Type = decltype(&M::call); - return Argument<2, Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg3Type() { - using Type = decltype(&M::call); - return Argument<3, Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg4Type() { - using Type = decltype(&M::call); - return Argument<4, Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg5Type() { - using Type = decltype(&M::call); - return Argument<5, Type>::type; -} - -template<typename M> -static constexpr ArgumentType arg6Type() { - using Type = decltype(&M::call); - return Argument<6, Type>::type; -} - -} // namespace RuntimeSupport -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4RUNTIMESUPPORT_P_H diff --git a/src/qml/jit/qv4schedulers.cpp b/src/qml/jit/qv4schedulers.cpp deleted file mode 100644 index 0dffefa951..0000000000 --- a/src/qml/jit/qv4schedulers.cpp +++ /dev/null @@ -1,912 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> - -#include "qv4schedulers_p.h" -#include "qv4util_p.h" -#include "qv4graph_p.h" -#include "qv4blockscheduler_p.h" -#include "qv4stackframe_p.h" - -QT_BEGIN_NAMESPACE -namespace QV4 { -namespace IR { - -Q_LOGGING_CATEGORY(lcSched, "qt.v4.ir.scheduling") -Q_LOGGING_CATEGORY(lcDotCFG, "qt.v4.ir.scheduling.cfg") - -static bool needsScheduling(Node *n) -{ - if (n->operation()->isConstant()) - return false; - switch (n->opcode()) { - case Meta::Function: Q_FALLTHROUGH(); - case Meta::CppFrame: - case Meta::Phi: - case Meta::EffectPhi: - return false; - default: - return true; - } -} - -bool NodeScheduler::canStartBlock(Node *node) const -{ - switch (node->operation()->kind()) { - case Meta::Start: Q_FALLTHROUGH(); - case Meta::IfTrue: - case Meta::IfFalse: - case Meta::Region: - case Meta::HandleUnwind: - case Meta::OnException: - return true; - - default: - return false; - } -} - -bool NodeScheduler::isControlFlowSplit(Node *node) const -{ - int nOutputs = node->operation()->controlOutputCount(); - if (nOutputs == 2) { - // if there is a "missing" control output, it's for exception flow without unwinder - int controlUses = 0; - auto uses = node->uses(); - for (auto it = uses.begin(), eit = uses.end(); it != eit; ++it) { - if (isLive(*it) && it.isUsedAsControl()) - ++controlUses; - } - return controlUses == 2; - } - return nOutputs > 2; -} - -bool NodeScheduler::isBlockTerminator(Node *node) const -{ - switch (node->operation()->kind()) { - case Meta::Branch: Q_FALLTHROUGH(); - case Meta::Jump: - case Meta::Return: - case Meta::TailCall: - case Meta::UnwindDispatch: - case Meta::End: - return true; - case Meta::Call: - return isControlFlowSplit(node); - default: - return false; - } -} - -MIBlock *NodeScheduler::getCommonDominator(MIBlock *one, MIBlock *other) const -{ - MIBlock::Index a = one->index(); - MIBlock::Index b = other->index(); - - while (a != b) { - if (m_dominatorDepthForBlock[a] < m_dominatorDepthForBlock[b]) - b = m_domTree->immediateDominator(b); - else - a = m_domTree->immediateDominator(a); - } - - return m_miFunction->block(a); -} - -// For Nodes that end up inside loops, it'd be great if we can move (hoist) them out of the loop. -// To do that, we need a block that preceeds the loop. (So the block before the loop header.) -// This function calculates that hoist block if the original block is in a loop. -MIBlock *NodeScheduler::getHoistBlock(MIBlock *block) const -{ - if (m_loopInfo->isLoopHeader(block)) - return m_miFunction->block(m_domTree->immediateDominator(block->index())); - - // make the loop header a candidate: - MIBlock *loopHeader = m_loopInfo->loopHeaderFor(block); - if (loopHeader == nullptr) - return nullptr; // block is not in a loop - - // And now the tricky part: block has to dominate all exits from the loop. If it does not do - // that, it meanse that there is an exit from the loop that can be reached before block. In - // that case, hoisting from "block" to "loopHeader" would mean there now is an extra calculation - // that is not needed for a certain loop exit. - for (MIBlock *outEdge : m_loopInfo->loopExitsForLoop(loopHeader)) { - if (getCommonDominator(block, outEdge) != block) - return nullptr; - } - - return m_miFunction->block(m_domTree->immediateDominator(loopHeader->index())); -} - -NodeScheduler::NodeScheduler(Function *irFunction) - : m_irFunction(irFunction) - , m_vregs(irFunction->graph()->nodeCount(), std::numeric_limits<unsigned>::max()) - , m_live(irFunction->graph(), /*collectUses =*/ false /* do explicitly NOT collect uses! */) -{ -} - -MIFunction *NodeScheduler::buildMIFunction() -{ - m_miFunction = new MIFunction(m_irFunction); - - // step 1: build the CFG - auto roots = buildCFG(); - m_miFunction->renumberBlocks(); - m_miFunction->dump(QStringLiteral("CFG after renumbering")); - - Q_ASSERT(m_miFunction->block(MIFunction::StartBlockIndex)->index() - == MIFunction::StartBlockIndex); - Q_ASSERT(m_miFunction->block(MIFunction::StartBlockIndex)->instructions().front().opcode() - == Meta::Start); - - // step 2: build the dominator tree - if (lcDotCFG().isDebugEnabled()) - dumpDotCFG(); - m_domTree.reset(new DominatorTree(m_miFunction)); - m_dominatorDepthForBlock = m_domTree->calculateNodeDepths(); - - // step 3: find loops - m_loopInfo.reset(new LoopInfo(*m_domTree.data())); - m_loopInfo->detectLoops(); - - // step 4: schedule early - scheduleEarly(roots); - showNodesByBlock(QStringLiteral("nodes per block after early scheduling")); - - // step 5: schedule late - scheduleLate(roots); - showNodesByBlock(QStringLiteral("nodes per block after late scheduling")); - - // step 6: schedule instructions in each block - scheduleNodesInBlock(); - - m_miFunction->dump(QStringLiteral("MI before block scheduling")); - - // step 7: order the basic blocks in the CFG - BlockScheduler blockScheduler(*m_domTree.data(), *m_loopInfo.data()); - m_miFunction->setBlockOrder(blockScheduler.scheduledBlockSequence()); - - // we're done - m_miFunction->renumberInstructions(); - m_miFunction->setVregCount(m_nextVReg); - m_miFunction->dump(QStringLiteral("MI after scheduling")); - return m_miFunction; -} - -static Node *splitEdge(Function *irFunction, Node *node, unsigned inputIndex) -{ - Graph *g = irFunction->graph(); - Node *in = node->input(inputIndex); - Node *region = g->createNode(g->opBuilder()->getRegion(1), &in, 1); - Node *jump = g->createNode(g->opBuilder()->get<Meta::Jump>(), ®ion, 1); - - qCDebug(lcSched) << "splitting critical edge from node" << node->id() - << "to node" << node->input(inputIndex)->id() - << "by inserting jump node" << jump->id() - << "and region node" << region->id(); - - node->replaceInput(inputIndex, jump); - return jump; -} - -// See Chapter 6.3.1 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for -// a description of the algorithm. -std::vector<Node *> NodeScheduler::buildCFG() -{ - std::vector<Node *> roots; - roots.reserve(32); - NodeWorkList todo(m_irFunction->graph()); - - auto enqueueControlInputs = [this, &todo](Node *node) { - for (unsigned i = 0, ei = node->operation()->controlInputCount(); i != ei; ++i) { - const auto inputIndex = node->operation()->indexOfFirstControl() + i; - Node *input = node->input(inputIndex); - Q_ASSERT(input); - if (node->operation()->kind() == Meta::Region - && node->operation()->controlInputCount() > 1 - && isControlFlowSplit(input)) { - // critical edge! - input = splitEdge(m_irFunction, node, inputIndex); - m_live.markReachable(input); - m_live.markReachable(input->controlInput(0)); - } - if (!isBlockTerminator(input)) { - auto g = m_irFunction->graph(); - Node *jump = g->createNode(g->opBuilder()->get<Meta::Jump>(), &input, 1); - node->replaceInput(inputIndex, jump); - m_live.markReachable(jump); - qCDebug(lcSched) << "inserting jump node" << jump->id() - << "between node" << node->id() - << "and node" << input->id(); - input = jump; - } - todo.enqueue(input); - } - }; - - // create the CFG by scheduling control dependencies that start/end blocks: - todo.enqueue(m_irFunction->graph()->endNode()); - while (Node *node = todo.dequeueNextNodeForVisiting()) { - Q_ASSERT(isBlockTerminator(node)); - - if (schedulerData(node)->minimumBlock) - continue; - - MIBlock *b = m_miFunction->addBlock(); - - qCDebug(lcSched) << "scheduling node" << node->id() << "as terminator for new block" - << b->index(); - b->instructions().push_front(createMIInstruction(node)); - placeFixed(node, b, Schedule); - roots.push_back(node); - - if (Node *framestate = node->frameStateInput()) { - placeFixed(framestate, b, DontSchedule); - qCDebug(lcSched) << ".. also scheduling framestate dependency node" << node->id() - << "in block" << b->index(); - } - - if (node->opcode() == Meta::End) { - enqueueControlInputs(node); - continue; - } - - while (true) { - Node *controlDependency = node->controlInput(0); - if (!controlDependency) - break; - if (todo.isVisited(controlDependency)) - break; - if (schedulerData(controlDependency)->isFixed) - break; - - if (controlDependency->opcode() == Meta::Start) { - qCDebug(lcSched) << "placing start node" << controlDependency->id() - << "in block" << b->index(); - handleStartNode(controlDependency, b); - placeFixed(controlDependency, b, Schedule); - roots.push_back(controlDependency); - break; // we're done with this block - } - if (isBlockTerminator(controlDependency)) { - qCDebug(lcSched) << "found terminator node" << controlDependency->id() - << "for another block, so finish block" << b->index(); - Node *merge = m_irFunction->graph()->createNode( - m_irFunction->graph()->opBuilder()->getRegion(1), &controlDependency, 1); - node->replaceInput(node->operation()->indexOfFirstControl(), merge); - addBlockStart(roots, merge, b); - placeFixed(merge, b, Schedule); - m_live.markReachable(merge); - todo.enqueue(controlDependency); - break; // we're done with this block - } - if (canStartBlock(controlDependency) - || schedulerData(controlDependency->controlInput())->isFixed) { - qCDebug(lcSched) << "found block start node" << controlDependency->id() - << "for this block, so finish block" << b->index(); - addBlockStart(roots, controlDependency, b); - placeFixed(controlDependency, b, Schedule); - roots.push_back(controlDependency); - enqueueControlInputs(controlDependency); - break; // we're done with this block - } - qCDebug(lcSched) << "skipping node" << controlDependency->id(); - node = controlDependency; - } - } - - // link the edges of the MIBlocks, and add basic-block arguments: - for (MIBlock *toBlock : m_miFunction->blocks()) { - Q_ASSERT(!toBlock->instructions().empty()); - MIInstr &instr = toBlock->instructions().front(); - Node *toNode = instr.irNode(); - const auto opcode = toNode->operation()->kind(); - if (opcode == Meta::Region) { - unsigned inputNr = 0; - for (Node *input : toNode->inputs()) { - MIBlock *fromBlock = schedulerData(input)->minimumBlock; - fromBlock->addOutEdge(toBlock); - toBlock->addInEdge(fromBlock); - MIInstr &fromTerminator = fromBlock->instructions().back(); - if (fromTerminator.irNode()->opcode() == Meta::Jump || - fromTerminator.irNode()->opcode() == Meta::UnwindDispatch) { - unsigned arg = 0; - for (const MIOperand &bbArg : toBlock->arguments()) { - fromTerminator.setOperand(arg++, - createMIOperand(bbArg.irNode()->input(inputNr))); - } - } - ++inputNr; - } - } else if (opcode == Meta::End) { - for (Node *input : toNode->inputs()) { - MIBlock *fromBlock = schedulerData(input)->minimumBlock; - fromBlock->addOutEdge(toBlock); - toBlock->addInEdge(fromBlock); - } - } else if (Node *fromNode = toNode->controlInput()) { - MIBlock *fromBlock = schedulerData(fromNode)->minimumBlock; - fromBlock->addOutEdge(toBlock); - toBlock->addInEdge(fromBlock); - } - } - - m_irFunction->dump(QStringLiteral("graph after building CFG")); - - auto startBlock = schedulerData(m_irFunction->graph()->startNode())->minimumBlock; - m_miFunction->setStartBlock(startBlock); - - if (lcSched().isDebugEnabled()) - m_miFunction->dump(QStringLiteral("control flow graph before renumbering")); - m_miFunction->verifyCFG(); - - return roots; -} - -// See Chapter 6.3.3 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for -// a description of the algorithm. -void NodeScheduler::scheduleEarly(const std::vector<Node *> &roots) -{ - // scheduling one node might have the effect of queueing its dependencies - NodeWorkList todo(m_irFunction->graph()); - for (Node *root : roots) { - todo.enqueue(root); - while (Node *node = todo.dequeueNextNodeForVisiting()) - scheduleEarly(node, todo); - } -} - -void NodeScheduler::scheduleEarly(Node *node, NodeWorkList &todo) -{ - qCDebug(lcSched) << "Scheduling node" << node->id() << "early..."; - - SchedulerData *sd = schedulerData(node); - - if (sd->isFixed) { - // Fixed nodes already know their schedule early position. - qCDebug(lcSched) << ".. Fixed node" << node->id() << "is on minimum block" - << sd->minimumBlock->index() - << "which has dominator depth" - << m_dominatorDepthForBlock[sd->minimumBlock->index()]; - } - - for (Node *use : node->uses()) { - if (isLive(use)) - propagateMinimumPosition(sd->minimumBlock, use, todo); - else - qCDebug(lcSched) << ".. Skipping node" << use->id() << "as it's not live"; - } -} - -void NodeScheduler::propagateMinimumPosition(MIBlock *newMinimumPosition, Node *toNode, - NodeWorkList &todo) -{ - Q_ASSERT(newMinimumPosition); - - SchedulerData *sd = schedulerData(toNode); - if (sd->isFixed) // nothing to do - return; - - MIBlock::Index minimumBlockIndex = sd->minimumBlock - ? sd->minimumBlock->index() - : MIFunction::StartBlockIndex; - Q_ASSERT(m_domTree->insideSameDominatorChain(newMinimumPosition->index(), minimumBlockIndex)); - if (sd->minimumBlock == nullptr - || m_dominatorDepthForBlock[newMinimumPosition->index()] - > m_dominatorDepthForBlock[minimumBlockIndex]) { - // ok, some input for toNode is scheduled *after* our current minimum depth, so we need - // to adjust out minimal position. (This might involve rescheduling toNode's uses.) - place(toNode, newMinimumPosition); - todo.reEnqueue(toNode); - qCDebug(lcSched) << ".. Propagating minimum block" << sd->minimumBlock->index() - << "which has dominator depth" - << m_dominatorDepthForBlock[newMinimumPosition->index()] - << "to use node" << toNode->id(); - } else { - qCDebug(lcSched) << ".. Minimum position" << newMinimumPosition->index() - << "is not better than" << minimumBlockIndex - << "for node" << toNode->id(); - } -} - -// See Chapter 6.3.4 of https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf for -// a description of the algorithm. -// -// There is one extra detail not described in the thesis mentioned above: loop hoisting. Before we -// place a node in the latest block that dominates all uses, we check if we accidentally sink it -// *into* a loop (meaning the latest block is inside a loop, where it is not if the earliest -// possible block would be chosen). If we detect that a nodes is going to sink into a loop, we walk -// the dominator path from the latest block up to the earliest block, and pick the first block that -// is in the same loop (if any) as the earlieast block. -// -// As noted in the thesis, this strategy might enlongen life times, which could be harmful for -// values that are simple to re-materialized or re-calculate. -void NodeScheduler::scheduleLate(const std::vector<Node *> &roots) -{ - NodeWorkList todo(m_irFunction->graph()); - for (Node *root : roots) - todo.enqueue(root); - - while (Node *node = todo.dequeueNextNodeForVisiting()) - scheduleNodeLate(node, todo); -} - -void NodeScheduler::scheduleNodeLate(Node *node, NodeWorkList &todo) -{ - if (!needsScheduling(node)) - return; - qCDebug(lcSched) << "Scheduling node" << node->id() << "late..."; - - auto sd = schedulerData(node); - if (sd->unscheduledUses == SchedulerData::NotYetCalculated) { - sd->unscheduledUses = 0; - for (Node *use : node->uses()) { - if (!isLive(use)) - continue; - if (!needsScheduling(use)) - continue; - if (schedulerData(use)->isFixed) - continue; - todo.enqueue(use); - ++sd->unscheduledUses; - } - } - - if (sd->isFixed) { - qCDebug(lcSched) << ".. it's fixed"; - enqueueInputs(node, todo); - return; - } - - if (sd->unscheduledUses) { - qCDebug(lcSched).noquote() << ".. not all uses are fixed, postpone it."<< todo.status(node); - return; - } - - MIBlock *&minBlock = sd->minimumBlock; - if (minBlock == nullptr) - minBlock = m_miFunction->block(MIFunction::StartBlockIndex); - MIBlock *commonUseDominator = commonDominatorOfUses(node); - qCDebug(lcSched) << ".. common use dominator: block" << commonUseDominator->index(); - - // the minBlock has to dominate the uses, *and* the common dominator of the uses. - Q_ASSERT(minBlock->index() == commonUseDominator->index() || - m_domTree->dominates(minBlock->index(), commonUseDominator->index())); - - // we now found the deepest block, so use it as the target block: - MIBlock *targetBlock = commonUseDominator; - - if (node->opcode() == Meta::FrameState) { - // never hoist framestates: they're used (among other things) to keep their inputs alive, so - // hoisting them out would end the life-time of those inputs prematurely - } else { - // but we want to prevent blocks sinking into loops unnecessary - MIBlock *hoistBlock = getHoistBlock(targetBlock); - while (hoistBlock - && m_dominatorDepthForBlock[hoistBlock->index()] - >= m_dominatorDepthForBlock[minBlock->index()]) { - qCDebug(lcSched) << ".. hoisting node" << node->id() << "from block" - << targetBlock->index() << "to block" << hoistBlock->index(); - // ok, so there a) is a hoist block and b) it's deeper than the minimum block, - // so lift it up one level ... - targetBlock = hoistBlock; - // ... and see if we can lift it one more level - hoistBlock = getHoistBlock(targetBlock); - } - } - - qCDebug(lcSched) << ".. fixating it in block" << targetBlock->index() - << "where the minimum block was" << minBlock->index(); - - placeFixed(node, targetBlock, DontSchedule); - enqueueInputs(node, todo); -} - -void NodeScheduler::enqueueInputs(Node *node, NodeWorkList &todo) -{ - for (Node *input : node->inputs()) { - if (!input) - continue; - if (!needsScheduling(input)) - continue; - if (!isLive(input)) - continue; - auto sd = schedulerData(input); - if (sd->isFixed) - continue; - qCDebug(lcSched).noquote() << "... enqueueing input node" << input->id() - << todo.status(input); - if (sd->unscheduledUses != SchedulerData::NotYetCalculated) { - if (sd->unscheduledUses > 0) - --sd->unscheduledUses; - if (sd->unscheduledUses == 0) - todo.reEnqueue(input); - } else { - todo.reEnqueue(input); - } - } -} - -Node *NodeScheduler::firstNotFixedUse(Node *node) -{ - for (Node *use : node->uses()) { - if (!isLive(use)) - continue; - if (!schedulerData(use)->isFixed) - return use; - } - return nullptr; -} - -MIBlock *NodeScheduler::commonDominatorOfUses(Node *node) -{ - MIBlock *commonDominator = nullptr; - for (auto useIt = node->uses().begin(), useEIt = node->uses().end(); useIt != useEIt; ++useIt) { - Node *use = *useIt; - if (!isLive(use)) - continue; - // region nodes use other nodes through their control dependency. But those nodes should - // already have been placed as block terminators before. - Q_ASSERT(use->opcode() != Meta::Region); - if (use->opcode() == Meta::Phi || use->opcode() == Meta::EffectPhi) { - // find the predecessor block defining this input - Node *region = use->controlInput(0); - Node *input = region->controlInput(useIt.inputIndex()); - use = input; - } - auto minBlock = schedulerData(use)->minimumBlock; - if (commonDominator == nullptr) - commonDominator = minBlock; - else - commonDominator = getCommonDominator(commonDominator, minBlock); - } - return commonDominator; -} - -void NodeScheduler::scheduleNodesInBlock() -{ - auto startBlock = m_miFunction->block(MIFunction::StartBlockIndex); - for (Node *n : m_live.reachable()) { - auto sd = schedulerData(n); - if (!sd->minimumBlock) - sd->minimumBlock = startBlock; - } - - std::vector<std::vector<SchedulerData *>> nodesForBlock; - nodesForBlock.resize(m_miFunction->blockCount()); - - for (auto sd : m_schedulerData) { - if (sd == nullptr) - continue; - if (!isLive(sd->node)) - continue; - sd->unscheduledUses = 0; - for (Node *use : sd->node->uses()) { - if (!needsScheduling(use)) - continue; - if (schedulerData(use)->isScheduledInBlock) - continue; - if (schedulerData(use)->minimumBlock == sd->minimumBlock) - ++sd->unscheduledUses; - } - if (sd->unscheduledUses == 0) - nodesForBlock[sd->minimumBlock->index()].push_back(sd); - } - - NodeWorkList todo(m_irFunction->graph()); - for (MIBlock *b : m_miFunction->blocks()) { - qCDebug(lcSched) << "Scheduling inside block" << b->index(); - MIInstr *insertionPoint = &b->instructions().back(); - todo.enqueue(insertionPoint->irNode()); - scheduleNodesInBlock(insertionPoint, b, todo); - Q_ASSERT(todo.isEmpty()); - for (auto sd : nodesForBlock[b->index()]) { - if (!sd->isScheduledInBlock) - todo.enqueue(sd->node); - } - scheduleNodesInBlock(insertionPoint, b, todo); - Q_ASSERT(todo.isEmpty()); - todo.reset(); - } -} - -void NodeScheduler::scheduleNodesInBlock(MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo) -{ - while (Node *n = todo.dequeueNextNodeForVisiting()) - scheduleNodeInBlock(n, insertionPoint, b, todo); -} - -void NodeScheduler::scheduleNodeInBlock(Node *node, MIInstr *&insertionPoint, MIBlock *b, - NodeWorkList &todo) -{ - Q_ASSERT(!node->isDead()); - - if (!isLive(node)) - return; - - if (!needsScheduling(node)) - return; - - auto nodeData = schedulerData(node); - if (nodeData->minimumBlock != b) - return; - - const bool wasAlreadyScheduled = nodeData->isScheduledInBlock; - if (!wasAlreadyScheduled) { - if (nodeData->unscheduledUses) - return; - - scheduleNodeNow(node, insertionPoint); - } - - if (Node *framestate = node->frameStateInput()) - scheduleNodeInBlock(framestate, insertionPoint, b, todo); - - for (Node *input : node->inputs()) { - if (!input) - continue; - if (!needsScheduling(input)) - continue; - if (!isLive(input)) - continue; - auto inputInfo = schedulerData(input); - if (inputInfo->isScheduledInBlock) - continue; - Q_ASSERT(inputInfo->minimumBlock != nullptr); - if (inputInfo->minimumBlock != b) - continue; - Q_ASSERT(!input->isDead()); - Q_ASSERT(inputInfo->unscheduledUses != SchedulerData::NotYetCalculated); - if (!wasAlreadyScheduled && inputInfo->unscheduledUses > 0) - --inputInfo->unscheduledUses; - if (inputInfo->unscheduledUses == 0) - todo.enqueue(input); - } -} - -void NodeScheduler::scheduleNodeNow(Node *node, MIInstr *&insertionPoint) -{ - qCDebug(lcSched) << ".. scheduling node" << node->id() - << "in block" << insertionPoint->parent()->index() - << "before node" << insertionPoint->irNode()->id(); - - MIInstr *newInstr = createMIInstruction(node); - newInstr->insertBefore(insertionPoint); - insertionPoint = newInstr; -} - -void NodeScheduler::place(Node *node, MIBlock *b) -{ - Q_ASSERT(!node->isDead()); - - if (b == nullptr) - return; - - schedulerData(node)->minimumBlock = b; -} - -void NodeScheduler::placeFixed(Node *node, MIBlock *b, ScheduleOrNot markScheduled) -{ - place(node, b); - auto sd = schedulerData(node); - Q_ASSERT(!sd->isFixed); - sd->isFixed = true; - sd->isScheduledInBlock = markScheduled == Schedule; -} - -unsigned NodeScheduler::vregForNode(Node *node) -{ - unsigned &vreg = m_vregs[unsigned(node->id())]; - if (vreg == std::numeric_limits<unsigned>::max()) - vreg = m_nextVReg++; - return vreg; -} - -void NodeScheduler::addBlockStart(std::vector<Node *> &roots, Node *startNode, MIBlock *block) -{ - block->instructions().insert(block->instructions().begin(), createMIInstruction(startNode)); - if (startNode->opcode() == Meta::Region) { - for (Node *use : startNode->uses()) { - if (use->opcode() == Meta::Phi && isLive(use)) { - block->addArgument(MIOperand::createVirtualRegister(use, vregForNode(use))); - placeFixed(use, block, Schedule); - roots.push_back(use); - } else if (use->opcode() == Meta::EffectPhi && isLive(use)) { - placeFixed(use, block, Schedule); - roots.push_back(use); - } - } - } -} - -void NodeScheduler::handleStartNode(Node *startNode, MIBlock *startBlock) -{ - startBlock->instructions().push_front(createMIInstruction(startNode)); - - QVarLengthArray<Node *, 32> args; - for (Node *use : startNode->uses()) { - switch (use->opcode()) { - case Meta::Engine: Q_FALLTHROUGH(); - case Meta::CppFrame: - case Meta::Function: - placeFixed(use, startBlock, Schedule); - break; - case Meta::Parameter: { - auto param = ParameterPayload::get(*use->operation()); - int idx = int(param->parameterIndex()); - if (args.size() <= idx) - args.resize(idx + 1); - args[int(idx)] = use; - placeFixed(use, startBlock, Schedule); - } - break; - default: - break; - } - } - - for (unsigned i = 0, ei = unsigned(args.size()); i != ei; ++i) { - if (Node *arg = args.at(int(i))) - startBlock->addArgument(MIOperand::createJSStackSlot(arg, i)); - } -} - -static Node *firstControlOutput(Node *n) -{ - for (auto it = n->uses().begin(), eit = n->uses().end(); it != eit; ++it) { - if (it.isUsedAsControl()) - return *it; - } - return nullptr; -} - -MIInstr *NodeScheduler::createMIInstruction(Node *node) -{ - const auto opcode = node->operation()->kind(); - - unsigned nArgs = 0; - switch (opcode) { - case Meta::UnwindDispatch: Q_FALLTHROUGH(); - case Meta::Jump: { - Node *target = firstControlOutput(node); - if (target->opcode() == Meta::Region) { - for (Node *n : target->uses()) { - if (n->opcode() == Meta::Phi && isLive(n)) - ++nArgs; - } - } - } - break; - case Meta::Branch: - nArgs = 1; - break; - case Meta::Return: - nArgs = 1; - break; - default: - nArgs = node->operation()->valueInputCount(); - break; - } - - MIInstr *instr = MIInstr::create(m_irFunction->pool(), node, nArgs); - for (unsigned i = 0, ei = node->operation()->valueInputCount(); i != ei; ++i) - instr->setOperand(i, createMIOperand(node->input(i))); - if (node->opcode() != Meta::Start && node->operation()->valueOutputCount() > 0) - instr->setDestination(createMIOperand(node)); - - schedulerData(node)->isScheduledInBlock = true; - return instr; -} - -MIOperand NodeScheduler::createMIOperand(Node *node) -{ - if (node->operation()->isConstant()) - return MIOperand::createConstant(node); - - auto opcode = node->operation()->kind(); - switch (opcode) { - case Meta::Parameter: - return MIOperand::createJSStackSlot( - node, unsigned(ParameterPayload::get(*node->operation())->parameterIndex())); - case Meta::Engine: - return MIOperand::createEngineRegister(node); - case Meta::CppFrame: - return MIOperand::createCppFrameRegister(node); - case Meta::Function: - return MIOperand::createFunction(node); - default: - if ((node->opcode() == Meta::Call - && CallPayload::get(*node->operation())->callee() == Meta::JSThisToObject) - || node->opcode() == Meta::StoreThis) { - return MIOperand::createJSStackSlot(node, CallData::This); - } else { - return MIOperand::createVirtualRegister(node, vregForNode(node)); - } - } -} - -void NodeScheduler::showNodesByBlock(const QString &description) const -{ - if (!lcSched().isDebugEnabled()) - return; - - qCDebug(lcSched) << description; - - for (MIBlock *b : m_miFunction->blocks()) { - QString s; - for (const SchedulerData *sd : m_schedulerData) { - if (!sd) - continue; - if (!isLive(sd->node)) - continue; - if (sd->minimumBlock == b) { - if (!s.isEmpty()) - s += QStringLiteral(", "); - s += QStringLiteral("%1 (%2)").arg(QString::number(sd->node->id()), - sd->node->operation()->debugString()); - } - } - if (s.isEmpty()) - s = QStringLiteral("<<none>>"); - qCDebug(lcSched, "Nodes in block %u: %s", b->index(), s.toUtf8().constData()); - } -} - -void NodeScheduler::dumpDotCFG() const -{ - QString out; - out += QLatin1Char('\n'); - out += QStringLiteral("digraph{root=\"L%1\" label=\"Control Flow Graph\";" - "node[shape=circle];edge[dir=forward fontsize=10]\n") - .arg(MIFunction::StartBlockIndex); - for (MIBlock *src : m_miFunction->blocks()) { - for (MIBlock *dst : src->outEdges()) { - out += QStringLiteral("L%1->L%2\n").arg(QString::number(src->index()), - QString::number(dst->index())); - } - } - out += QStringLiteral("}\n"); - qCDebug(lcDotCFG).nospace().noquote() << out; -} - -} // IR namespace -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jit/qv4schedulers_p.h b/src/qml/jit/qv4schedulers_p.h deleted file mode 100644 index f9179816df..0000000000 --- a/src/qml/jit/qv4schedulers_p.h +++ /dev/null @@ -1,162 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QV4SCHEDULER_P_H -#define QV4SCHEDULER_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qv4global_p.h" -#include "qv4mi_p.h" -#include "qv4node_p.h" -#include "qv4domtree_p.h" -#include "qv4loopinfo_p.h" - -QT_REQUIRE_CONFIG(qml_tracing); - -QT_BEGIN_NAMESPACE - -namespace QV4 { -namespace IR { - -// Node scheduling "flattens" the graph into basic blocks with an ordered list of instructions. -// -// The various steps are mentioned in buildMIFunction, but the general idea is described in -// https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf in chapter 6. -class NodeScheduler final -{ - Q_DISABLE_COPY_MOVE(NodeScheduler) - - class SchedulerData final { - Q_DISABLE_COPY_MOVE(SchedulerData) - public: - static SchedulerData *create(QQmlJS::MemoryPool *pool) - { return pool->New<SchedulerData>(); } - - SchedulerData() = default; - ~SchedulerData() = default; - - Node *node = nullptr; - MIBlock *minimumBlock = nullptr; - bool isFixed = false; - bool isScheduledInBlock = false; - static constexpr unsigned NotYetCalculated = std::numeric_limits<unsigned>::max(); - unsigned unscheduledUses = NotYetCalculated; - }; - -public: - NodeScheduler(Function *irFunction); - ~NodeScheduler() = default; - - MIFunction *buildMIFunction(); - -private: - std::vector<Node *> buildCFG(); - void scheduleEarly(const std::vector<Node *> &roots); - void scheduleEarly(Node *node, NodeWorkList &todo); - void propagateMinimumPosition(MIBlock *newMinimumPosition, Node *toNode, NodeWorkList &todo); - void scheduleLate(const std::vector<Node *> &roots); - void scheduleNodeLate(Node *node, NodeWorkList &todo); - void enqueueInputs(Node *node, NodeWorkList &todo); - Node *firstNotFixedUse(Node *node); - MIBlock *commonDominatorOfUses(Node *node); - void scheduleNodesInBlock(); - void scheduleNodesInBlock(MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo); - void scheduleNodeInBlock(Node *node, MIInstr *&insertionPoint, MIBlock *b, NodeWorkList &todo); - void scheduleNodeNow(Node *node, MIInstr *&insertionPoint); - - void place(Node *node, MIBlock *b); - enum ScheduleOrNot { DontSchedule, Schedule }; - void placeFixed(Node *node, MIBlock *b, ScheduleOrNot markScheduled); - unsigned vregForNode(Node *node); - void addBlockStart(std::vector<Node *> &roots, Node *startNode, MIBlock *block); - void enqueueControlInputs(Node *node); - void handleStartNode(Node *startNode, MIBlock *startBlock); - MIInstr *createMIInstruction(Node *node); - MIOperand createMIOperand(Node *node); - SchedulerData *schedulerData(Node *n) - { - if (Q_UNLIKELY(m_schedulerData.size() <= n->id())) - m_schedulerData.resize(n->id() + 8); - SchedulerData *&sd = m_schedulerData[n->id()]; - if (Q_UNLIKELY(sd == nullptr)) { - sd = SchedulerData::create(m_irFunction->pool()); - sd->node = n; - } - return sd; - } - bool isLive(Node *n) const - { return m_live.isReachable(n->id()); } - bool canStartBlock(Node *node) const; - bool isControlFlowSplit(Node *node) const; - bool isBlockTerminator(Node *node) const; - MIBlock *getCommonDominator(MIBlock *one, MIBlock *other) const; - MIBlock *getHoistBlock(MIBlock *block) const; - - void showNodesByBlock(const QString &description) const; - - void dumpDotCFG() const; - -private: - Function *m_irFunction = nullptr; - MIFunction *m_miFunction = nullptr; - QScopedPointer<LoopInfo> m_loopInfo; - QScopedPointer<DominatorTree> m_domTree; - std::vector<int> m_dominatorDepthForBlock; - std::vector<unsigned> m_vregs; - std::vector<SchedulerData *> m_schedulerData; - NodeCollector m_live; - unsigned m_nextVReg = 0; -}; - -} // namespace IR -} // namespace QV4 - -QT_END_NAMESPACE - -#endif // QV4SCHEDULER_P_H diff --git a/src/qml/jit/qv4tracingjit.cpp b/src/qml/jit/qv4tracingjit.cpp deleted file mode 100644 index c8974b3a1b..0000000000 --- a/src/qml/jit/qv4tracingjit.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include <QtCore/qloggingcategory.h> - -#include "qv4vme_moth_p.h" -#include "qv4graphbuilder_p.h" -#include "qv4lowering_p.h" -#include "qv4mi_p.h" -#include "qv4schedulers_p.h" - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcTracing, "qt.v4.tracing") - -namespace QV4 { - -// This is the entry point for the "tracing JIT". It uses the sea-of-nodes concept as described in -// https://scholarship.rice.edu/bitstream/handle/1911/96451/TR95-252.pdf -// -// The minimal pipeline is as follows: -// - create the graph for the function -// - do generic lowering -// - schedule the nodes -// - run minimal stack slot allocation (no re-use of slots) -// - run the assembler -// -// This pipeline has no optimizations, and generates quite inefficient code. It does have the -// advantage that no trace information is used, so it can be used for testing where it replaces -// the baseline JIT. Any optimizations are additions to this pipeline. -// -// Note: generators (or resuming functions in general) are not supported by this JIT. -void Moth::runTracingJit(QV4::Function *function) -{ - IR::Function irFunction(function); - qCDebug(lcTracing).noquote() << "runTracingJit called for" << irFunction.name() << "..."; - - qCDebug(lcTracing).noquote().nospace() << function->traceInfoToString(); - - IR::GraphBuilder::buildGraph(&irFunction); - irFunction.dump(QStringLiteral("initial IR")); - irFunction.verify(); - - IR::GenericLowering(irFunction).lower(); - irFunction.dump(QStringLiteral("after generic lowering")); - irFunction.verify(); - - IR::NodeScheduler scheduler(&irFunction); - QScopedPointer<IR::MIFunction> miFunction(scheduler.buildMIFunction()); - miFunction->dump(QStringLiteral("initial MI")); - irFunction.verify(); -} - -} // QV4 namespace -QT_END_NAMESPACE diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index aab72f8b2d..45ea79d31a 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -470,6 +470,33 @@ void QJSEngine::installExtensions(QJSEngine::Extensions extensions, const QJSVal QV4::GlobalExtensions::init(obj, extensions); } +/*! + \since 5.14 + Interrupts or re-enables JavaScript execution. + + If \a interrupted is \c true, any JavaScript executed by this engine + immediately aborts and returns an error object until this function is + called again with a value of \c false for \a interrupted. + + This function is thread safe. You may call it from a different thread + in order to interrupt, for example, an infinite loop in JavaScript. +*/ +void QJSEngine::setInterrupted(bool interrupted) +{ + m_v4Engine->isInterrupted = interrupted; +} + +/*! + \since 5.14 + Returns whether JavaScript execution is currently interrupted. + + \sa setInterrupted() +*/ +bool QJSEngine::isInterrupted() const +{ + return m_v4Engine->isInterrupted; +} + static QUrl urlForFileName(const QString &fileName) { if (!fileName.startsWith(QLatin1Char(':'))) @@ -527,6 +554,8 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in result = script.run(); if (scope.engine->hasException) result = v4->catchException(); + if (v4->isInterrupted) + result = v4->newErrorObject(QStringLiteral("Interrupted")); QJSValue retval(v4, result->asReturnedValue()); @@ -565,7 +594,12 @@ QJSValue QJSEngine::importModule(const QString &fileName) if (m_v4Engine->hasException) return QJSValue(m_v4Engine, m_v4Engine->catchException()); moduleUnit->evaluate(); - return QJSValue(m_v4Engine, moduleNamespace->asReturnedValue()); + if (!m_v4Engine->isInterrupted) + return QJSValue(m_v4Engine, moduleNamespace->asReturnedValue()); + + return QJSValue( + m_v4Engine, + m_v4Engine->newErrorObject(QStringLiteral("Interrupted"))->asReturnedValue()); } /*! diff --git a/src/qml/jsapi/qjsengine.h b/src/qml/jsapi/qjsengine.h index 6300842341..31a4d68baa 100644 --- a/src/qml/jsapi/qjsengine.h +++ b/src/qml/jsapi/qjsengine.h @@ -113,6 +113,9 @@ public: void installExtensions(Extensions extensions, const QJSValue &object = QJSValue()); + void setInterrupted(bool interrupted); + bool isInterrupted() const; + QV4::ExecutionEngine *handle() const { return m_v4Engine; } void throwError(const QString &message); diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index e0bd986920..92eaf1d8ee 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -769,6 +769,8 @@ QJSValue QJSValue::call(const QJSValueList &args) ScopedValue result(scope, f->call(jsCallData)); if (engine->hasException) result = engine->catchException(); + if (engine->isInterrupted) + result = engine->newErrorObject(QStringLiteral("Interrupted")); return QJSValue(engine, result->asReturnedValue()); } @@ -825,6 +827,8 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList ScopedValue result(scope, f->call(jsCallData)); if (engine->hasException) result = engine->catchException(); + if (engine->isInterrupted) + result = engine->newErrorObject(QStringLiteral("Interrupted")); return QJSValue(engine, result->asReturnedValue()); } @@ -873,6 +877,8 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) ScopedValue result(scope, f->callAsConstructor(jsCallData)); if (engine->hasException) result = engine->catchException(); + if (engine->isInterrupted) + result = engine->newErrorObject(QStringLiteral("Interrupted")); return QJSValue(engine, result->asReturnedValue()); } diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index 5b9934282c..a87eb92caf 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -96,7 +96,7 @@ struct DateObject: Object { double date() const { return d()->date; } void setDate(double date) { d()->date = date; } - QDateTime toQDateTime() const; + Q_QML_PRIVATE_EXPORT QDateTime toQDateTime() const; }; template<> diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 18927c637c..e10bf3cf79 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -138,8 +138,6 @@ QT_BEGIN_NAMESPACE -Q_LOGGING_CATEGORY(lcTracingAll, "qt.v4.tracing.all") - using namespace QV4; #ifndef V4_BOOTSTRAP @@ -165,7 +163,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) , m_engineId(engineSerial.fetchAndAddOrdered(1)) , regExpCache(nullptr) , m_multiplyWrappedQObjects(nullptr) -#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP) +#if QT_CONFIG(qml_jit) , m_canAllocateExecutableMemory(OSAllocator::canAllocateExecutableMemory()) #endif { @@ -175,7 +173,7 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) bool ok = false; maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok); if (!ok || maxCallDepth <= 0) { -#ifdef QT_NO_DEBUG +#if defined(QT_NO_DEBUG) && !defined(__SANITIZE_ADDRESS__) && !QT_HAS_FEATURE(address_sanitizer) maxCallDepth = 1234; #else // no (tail call) optimization is done, so there'll be a lot mare stack frames active @@ -658,13 +656,6 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) ExecutionEngine::~ExecutionEngine() { - if (Q_UNLIKELY(lcTracingAll().isDebugEnabled())) { - for (auto cu : compilationUnits) { - for (auto f : qAsConst(cu->runtimeFunctions)) - qCDebug(lcTracingAll).noquote().nospace() << f->traceInfoToString(); - } - } - modules.clear(); delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = nullptr; diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 6df4545014..d0c58eee8f 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -464,7 +464,7 @@ public: // but any time a QObject is wrapped a second time in another engine, we have to do // bookkeeping. MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects; -#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP) +#if QT_CONFIG(qml_jit) const bool m_canAllocateExecutableMemory; #endif @@ -595,7 +595,7 @@ public: bool canJIT(Function *f = nullptr) { -#if defined(V4_ENABLE_JIT) && !defined(V4_BOOTSTRAP) +#if QT_CONFIG(qml_jit) if (!m_canAllocateExecutableMemory) return false; if (f) diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index b5cfea8863..82eccd9f3c 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -69,9 +69,23 @@ struct Q_QML_EXPORT EngineBase { CppStackFrame *currentStackFrame = nullptr; Value *jsStackTop = nullptr; + + // The JIT expects hasException and isInterrupted to be in the same 32bit word in memory. quint8 hasException = false; - quint8 writeBarrierActive = false; + // isInterrupted is expected to be set from a different thread +#if defined(Q_ATOMIC_INT8_IS_SUPPORTED) + QAtomicInteger<quint8> isInterrupted = false; quint16 unused = 0; +#elif defined(Q_ATOMIC_INT16_IS_SUPPORTED) + quint8 unused = 0; + QAtomicInteger<quint16> isInterrupted = false; +#elif defined(V4_BOOTSTRAP) + // We don't need the isInterrupted flag when bootstrapping. + quint8 unused[3]; +#else +# error V4 needs either 8bit or 16bit atomics. +#endif + quint8 isExecutingInRegExpJIT = false; quint8 padding[3]; MemoryManager *memoryManager = nullptr; @@ -137,6 +151,10 @@ Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsSta Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + 8); Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE); +#ifndef V4_BOOTSTRAP +Q_STATIC_ASSERT(offsetof(EngineBase, isInterrupted) + sizeof(EngineBase::isInterrupted) <= offsetof(EngineBase, hasException) + 4); +#endif + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 1bd4329fe8..debdf23d27 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -75,19 +75,12 @@ ReturnedValue Function::call(const Value *thisObject, const Value *argv, int arg Function *Function::create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) { - quint16 traceSlotCount = 0; -#if QT_CONFIG(qml_tracing) - traceSlotCount = function->nTraceInfos == CompiledData::Function::NoTracing() - ? 1 - : function->nTraceInfos; -#endif - quint8 *storage = new quint8[sizeof(Function) + traceSlotCount]; - return new(storage) Function(engine, unit, function); + return new Function(engine, unit, function); } void Function::destroy() { - delete[] reinterpret_cast<quint8 *>(this); + delete this; } Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) @@ -111,13 +104,6 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, internalClass = ic->d(); nFormals = compiledFunction->nFormals; - -#if QT_CONFIG(qml_tracing) - if (tracingEnabled()) { - for (uint i = 0; i < function->nTraceInfos; ++i) - *traceInfo(i) = 0; - } -#endif } Function::~Function() @@ -188,22 +174,4 @@ QQmlSourceLocation Function::sourceLocation() const return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); } -QString Function::traceInfoToString() -{ - QString info = QLatin1String("=== Trace information for ") + name()->toQString() + QLatin1Char(':'); - if (!tracingEnabled()) - return info + QStringLiteral(" disabled. Interpreter call count: %1\n").arg(interpreterCallCount); - if (compiledFunction->nTraceInfos == 0) - return info + QLatin1String(" none.\n"); - - info += QLatin1Char('\n'); - for (uint i = 0, ei = compiledFunction->nTraceInfos; i < ei; ++i) { - auto bits = QString::number(*traceInfo(i), 2); - if (bits.size() < 8) - bits.prepend(QString(8 - bits.size(), '0')); - info += QStringLiteral(" %1: %2\n").arg(QString::number(i), bits); - } - return info; -} - QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index f8125a58f8..01b212370d 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -123,31 +123,6 @@ public: return nullptr; return compilationUnit->runtimeFunctions[compiledFunction->nestedFunctionIndex]; } - - Q_NEVER_INLINE QString traceInfoToString(); - - quint8 *traceInfo(uint i) - { -#if QT_CONFIG(qml_tracing) - Q_ASSERT((tracingEnabled() && i < traceInfoCount()) || (i == 0)); - return reinterpret_cast<quint8 *>(this) + sizeof(Function) + i; -#else - Q_UNUSED(i); - return nullptr; -#endif - } - - quint32 traceInfoCount() const - { return compiledFunction->nTraceInfos; } - - bool tracingEnabled() const - { -#if QT_CONFIG(qml_tracing) - return traceInfoCount() != CompiledData::Function::NoTracing(); -#else - return false; -#endif - } }; } diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index e03d49c74d..4fee26f341 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -87,11 +87,11 @@ DECLARE_HEAP_OBJECT(FunctionObject, Object) { } Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name, VTable::Call call); - void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr); - void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); - void init(QV4::ExecutionContext *scope, const QString &name); - void init(); - void destroy(); + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::String *name = nullptr); + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, QV4::Function *function, QV4::String *n = nullptr); + Q_QML_PRIVATE_EXPORT void init(QV4::ExecutionContext *scope, const QString &name); + Q_QML_PRIVATE_EXPORT void init(); + Q_QML_PRIVATE_EXPORT void destroy(); void setFunction(Function *f); @@ -260,7 +260,7 @@ struct FunctionPrototype: FunctionObject static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; -struct IndexedBuiltinFunction : FunctionObject +struct Q_QML_PRIVATE_EXPORT IndexedBuiltinFunction : FunctionObject { V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) }; diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index d47393b3bb..42b6edb6e2 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -82,53 +82,9 @@ inline bool isfinite(double d) { return _finite(d); } inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } #endif -// Decide whether to enable or disable the JIT - -// White list architectures -// -// NOTE: This should match the logic in qv4targetplatform_p.h! - -#if defined(Q_PROCESSOR_X86_32) && (QT_POINTER_SIZE == 4) \ - && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD)) -# define V4_ENABLE_JIT -#elif defined(Q_PROCESSOR_X86_64) && (QT_POINTER_SIZE == 8) \ - && (defined(Q_OS_WIN) || defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_MAC) || defined(Q_OS_FREEBSD)) -# define V4_ENABLE_JIT -#elif defined(Q_PROCESSOR_ARM_32) && (QT_POINTER_SIZE == 4) \ - && (defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_FREEBSD) || defined(Q_OS_INTEGRITY)) -# if defined(thumb2) || defined(__thumb2__) || ((defined(__thumb) || defined(__thumb__)) && __TARGET_ARCH_THUMB-0 == 4) -# define V4_ENABLE_JIT -# elif defined(__ARM_ARCH_ISA_THUMB) && __ARM_ARCH_ISA_THUMB == 2 // clang 3.5 and later will set this if the core supports the Thumb-2 ISA. -# define V4_ENABLE_JIT -# endif -#elif defined(Q_PROCESSOR_ARM_64) && (QT_POINTER_SIZE == 8) -# if defined(Q_OS_LINUX) || defined(Q_OS_QNX) || defined(Q_OS_INTEGRITY) -# define V4_ENABLE_JIT -# endif -//#elif defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) -//# define V4_ENABLE_JIT -#endif - -// check FPU with double precision on ARM platform -#if (defined(Q_PROCESSOR_ARM_64) || defined(Q_PROCESSOR_ARM_32)) && defined(V4_ENABLE_JIT) && defined(__ARM_FP) && (__ARM_FP <= 0x04) -# undef V4_ENABLE_JIT -#endif - -// Black list some platforms -#if defined(V4_ENABLE_JIT) -#if defined(Q_OS_IOS) || defined(Q_OS_TVOS) -# undef V4_ENABLE_JIT -#endif -#endif - -// For debug purposes: add CONFIG+=force-compile-jit to qmake's command-line to always compile the JIT. -#if defined(V4_FORCE_COMPILE_JIT) && !defined(V4_ENABLE_JIT) -# define V4_ENABLE_JIT -#endif - // Do certain things depending on whether the JIT is enabled or disabled -#ifdef V4_ENABLE_JIT +#if QT_CONFIG(qml_jit) #define ENABLE_YARR_JIT 1 #define ENABLE_JIT 1 #define ENABLE_ASSEMBLER 1 @@ -280,20 +236,6 @@ struct IdentifierTable; class RegExpCache; class MultiplyWrappedQObjectMap; -enum class ObservedTraceValues : quint8 { - Integer = 1 << 0, - Boolean = 1 << 1, - Double = 1 << 2, - Other = 1 << 3, - TypeMask = Integer | Boolean | Double | Other, - - TruePathTaken = 1 << 0, - FalsePathTaken = 1 << 1, - - ArrayWasAccessed = 1 << 7, - ArrayAccessNeededFallback = 1 << 6, -}; - enum PropertyFlag { Attr_Data = 0, Attr_Accessor = 0x1, diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp index 5db5bd46ec..f9bc7b68c6 100644 --- a/src/qml/jsruntime/qv4identifier.cpp +++ b/src/qml/jsruntime/qv4identifier.cpp @@ -39,29 +39,19 @@ #include "qv4identifier_p.h" #include "qv4identifiertable_p.h" #include "qv4string_p.h" +#include <private/qprimefornumbits_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - - IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits) : size(0) , numBits(numBits) , identifierTable(table) { refCount.store(1); - alloc = primeForNumBits(numBits); + alloc = qPrimeForNumBits(numBits); entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry)); memset(entries, 0, alloc*sizeof(IdentifierHashEntry)); identifierTable->addIdentifierHash(this); @@ -110,7 +100,7 @@ IdentifierHashEntry *IdentifierHash::addEntry(PropertyKey identifier) if (grow) { ++d->numBits; - int newAlloc = primeForNumBits(d->numBits); + int newAlloc = qPrimeForNumBits(d->numBits); IdentifierHashEntry *newEntries = (IdentifierHashEntry *)malloc(newAlloc * sizeof(IdentifierHashEntry)); memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry)); for (int i = 0; i < d->alloc; ++i) { diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index ae937b2889..21b47c3909 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -38,28 +38,18 @@ ****************************************************************************/ #include "qv4identifiertable_p.h" #include "qv4symbol_p.h" +#include <private/qprimefornumbits_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - - IdentifierTable::IdentifierTable(ExecutionEngine *engine, int numBits) : engine(engine) , size(0) , numBits(numBits) { - alloc = primeForNumBits(numBits); + alloc = qPrimeForNumBits(numBits); entriesByHash = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); entriesById = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *)); memset(entriesByHash, 0, alloc*sizeof(Heap::String *)); @@ -87,7 +77,7 @@ void IdentifierTable::addEntry(Heap::StringOrSymbol *str) if (grow) { ++numBits; - int newAlloc = primeForNumBits(numBits); + int newAlloc = qPrimeForNumBits(numBits); Heap::StringOrSymbol **newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *)); memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *)); for (uint i = 0; i < alloc; ++i) { diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index a10fda79f2..d597335031 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -45,27 +45,18 @@ #include "qv4identifiertable_p.h" #include "qv4value_p.h" #include "qv4mm_p.h" +#include <private/qprimefornumbits_p.h> QT_BEGIN_NAMESPACE namespace QV4 { -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - PropertyHashData::PropertyHashData(int numBits) : refCount(1) , size(0) , numBits(numBits) { - alloc = primeForNumBits(numBits); + alloc = qPrimeForNumBits(numBits); entries = (PropertyHash::Entry *)malloc(alloc*sizeof(PropertyHash::Entry)); memset(entries, 0, alloc*sizeof(PropertyHash::Entry)); } diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index c2c3fa0474..99f425293e 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -242,9 +242,6 @@ ReturnedValue Lookup::getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *eng return o->inlinePropertyDataWithOffset(l->objectLookupTwoClasses.offset)->asReturnedValue(); if (l->objectLookupTwoClasses.ic2 == o->internalClass) return o->inlinePropertyDataWithOffset(l->objectLookupTwoClasses.offset2)->asReturnedValue(); - Value obj = Value::fromHeapObject(o); - Value str = Value::fromHeapObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); - return static_cast<Object &>(obj).get(&static_cast<String &>(str)); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -260,9 +257,6 @@ ReturnedValue Lookup::getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine return o->inlinePropertyDataWithOffset(l->objectLookupTwoClasses.offset)->asReturnedValue(); if (l->objectLookupTwoClasses.ic2 == o->internalClass) return o->memberData->values.data()[l->objectLookupTwoClasses.offset2].asReturnedValue(); - Value obj = Value::fromHeapObject(o); - Value str = Value::fromHeapObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); - return static_cast<Object &>(obj).get(&static_cast<String &>(str)); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -278,9 +272,6 @@ ReturnedValue Lookup::getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEng return o->memberData->values.data()[l->objectLookupTwoClasses.offset].asReturnedValue(); if (l->objectLookupTwoClasses.ic2 == o->internalClass) return o->memberData->values.data()[l->objectLookupTwoClasses.offset2].asReturnedValue(); - Value obj = Value::fromHeapObject(o); - Value str = Value::fromHeapObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); - return static_cast<Object &>(obj).get(&static_cast<String &>(str)); } l->getter = getterFallback; return getterFallback(l, engine, object); @@ -296,9 +287,7 @@ ReturnedValue Lookup::getterProtoTwoClasses(Lookup *l, ExecutionEngine *engine, return l->protoLookupTwoClasses.data->asReturnedValue(); if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId) return l->protoLookupTwoClasses.data2->asReturnedValue(); - Value obj = Value::fromHeapObject(o); - Value str = Value::fromHeapObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); - return static_cast<Object &>(obj).get(&static_cast<String &>(str)); + return getterFallback(l, engine, object); } l->getter = getterFallback; return getterFallback(l, engine, object); diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 7309749a81..f2e0afd797 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -64,7 +64,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { -struct Lookup { +struct Q_QML_PRIVATE_EXPORT Lookup { union { ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h index a60a49a811..90246c4229 100644 --- a/src/qml/jsruntime/qv4math_p.h +++ b/src/qml/jsruntime/qv4math_p.h @@ -66,42 +66,27 @@ QT_BEGIN_NAMESPACE namespace QV4 { -static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b) { int result; - if (Q_UNLIKELY(add_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); + if (Q_UNLIKELY(add_overflow(a, b, &result))) return Value::fromDouble(static_cast<double>(a) + b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); return Value::fromInt32(result).asReturnedValue(); } -static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b) { int result; - if (Q_UNLIKELY(sub_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); + if (Q_UNLIKELY(sub_overflow(a, b, &result))) return Value::fromDouble(static_cast<double>(a) - b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); return Value::fromInt32(result).asReturnedValue(); } -static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b, quint8 *traceInfo = nullptr) +static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b) { int result; - if (Q_UNLIKELY(mul_overflow(a, b, &result))) { - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Double); + if (Q_UNLIKELY(mul_overflow(a, b, &result))) return Value::fromDouble(static_cast<double>(a) * b).asReturnedValue(); - } - if (traceInfo) - *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); return Value::fromInt32(result).asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 567382cbc0..38055ef407 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -410,7 +410,7 @@ private: friend struct ObjectPrototype; }; -struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator +struct Q_QML_PRIVATE_EXPORT ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator { uint arrayIndex = 0; uint memberIndex = 0; diff --git a/src/qml/jsruntime/qv4propertykey_p.h b/src/qml/jsruntime/qv4propertykey_p.h index 523afd4ccf..b2a2ec3dea 100644 --- a/src/qml/jsruntime/qv4propertykey_p.h +++ b/src/qml/jsruntime/qv4propertykey_p.h @@ -124,7 +124,7 @@ public: return m(); } - bool isString() const; + Q_QML_EXPORT bool isString() const; bool isSymbol() const; bool isCanonicalNumericIndexString() const; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index d2aa334805..f3351f6da0 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -231,17 +231,17 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r } else if (r.type.isValid()) { if (lookup) { if (r.type.isSingleton()) { - QQmlEngine *e = v4->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo(); - siinfo->init(e); - if (siinfo->qobjectApi(e)) { + QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine()); + if (r.type.isQObjectSingleton() || r.type.isCompositeSingleton()) { + e->singletonInstance<QObject*>(r.type); lookup->qmlContextSingletonLookup.singleton = static_cast<Heap::Object*>( Value::fromReturnedValue( QQmlTypeWrapper::create(v4, nullptr, r.type) ).heapObject()); } else { - QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); + QJSValue singleton = e->singletonInstance<QJSValue>(r.type); + QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, singleton)); lookup->qmlContextSingletonLookup.singleton = o->d(); } lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton; @@ -457,11 +457,17 @@ ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup * // into the handler expression through the locals of the call context. So for onClicked: { ... } // the parameters of the clicked signal are injected and we must allow for them to be found here // before any other property from the QML context. - ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context); - if (ctx.d()->type == Heap::ExecutionContext::Type_CallContext) { - uint index = ctx.d()->internalClass->indexOfValueOrGetter(name); - if (index < UINT_MAX) - return static_cast<Heap::CallContext*>(ctx.d())->locals[index].asReturnedValue(); + for (Heap::ExecutionContext *ctx = engine->currentContext()->d(); ctx; ctx = ctx->outer) { + if (ctx->type == Heap::ExecutionContext::Type_CallContext) { + const uint index = ctx->internalClass->indexOfValueOrGetter(name); + if (index < std::numeric_limits<uint>::max()) + return static_cast<Heap::CallContext *>(ctx)->locals[index].asReturnedValue(); + } + + // Skip only block contexts within the current call context. + // Other contexts need a regular QML property lookup. See below. + if (ctx->type != Heap::ExecutionContext::Type_BlockContext) + break; } bool hasProperty = false; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index ba9029bd4d..e81c90dd1a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -560,9 +560,9 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP QQmlContextData *callingQmlContext = scope.engine->callingQmlContext(); if (!QQmlPropertyPrivate::write(object, *property, v, callingQmlContext)) { - const char *valueType = nullptr; - if (v.userType() == QVariant::Invalid) valueType = "null"; - else valueType = QMetaType::typeName(v.userType()); + const char *valueType = (v.userType() == QMetaType::UnknownType) + ? "an unknown type" + : QMetaType::typeName(v.userType()); const char *targetTypeName = QMetaType::typeName(property->propType()); if (!targetTypeName) @@ -851,7 +851,7 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E if (!ddata || !ddata->propertyCache) { QQmlPropertyData local; QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local); - return getProperty(engine, qobj, property); + return property ? getProperty(engine, qobj, property) : QV4::Encode::undefined(); } QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 8f2b162106..e95dfa775f 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -717,30 +717,6 @@ ReturnedValue Runtime::LoadElement::call(ExecutionEngine *engine, const Value &o return getElementFallback(engine, object, index); } -ReturnedValue Runtime::LoadElement_Traced::call(ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot) -{ - *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed); - if (index.isPositiveInt()) { - uint idx = static_cast<uint>(index.int_32()); - if (Heap::Base *b = object.heapObject()) { - if (b->internalClass->vtable->isObject) { - Heap::Object *o = static_cast<Heap::Object *>(b); - if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) - if (!s->data(idx).isEmpty()) - return s->data(idx).asReturnedValue(); - } - } - } - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - return getElementIntFallback(engine, object, idx); - } - - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - return getElementFallback(engine, object, index); -} - static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { Scope scope(engine); @@ -796,30 +772,6 @@ void Runtime::StoreElement::call(ExecutionEngine *engine, const Value &object, c engine->throwTypeError(); } -void Runtime::StoreElement_traced::call(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot) -{ - *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed); - if (index.isPositiveInt()) { - uint idx = static_cast<uint>(index.int_32()); - if (Heap::Base *b = object.heapObject()) { - if (b->internalClass->vtable->isObject) { - Heap::Object *o = static_cast<Heap::Object *>(b); - if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) { - s->setData(engine, idx, value); - return; - } - } - } - } - } - - *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); - if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict()) - engine->throwTypeError(); -} - ReturnedValue Runtime::GetIterator::call(ExecutionEngine *engine, const Value &in, int iterator) { Scope scope(engine); diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 86cbccde23..13a73b7046 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -154,10 +154,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime { { static void call(ExecutionEngine *, const Value &, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT StoreElement_traced : Method<Throws::Yes> - { - static void call(ExecutionEngine *, const Value &, const Value &, const Value &, quint8 *); - }; struct Q_QML_PRIVATE_EXPORT LoadProperty : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &, int); @@ -170,10 +166,6 @@ struct Q_QML_PRIVATE_EXPORT Runtime { { static ReturnedValue call(ExecutionEngine *, const Value &, const Value &); }; - struct Q_QML_PRIVATE_EXPORT LoadElement_Traced : Method<Throws::Yes> - { - static ReturnedValue call(ExecutionEngine *, const Value &, const Value &, quint8 *); - }; struct Q_QML_PRIVATE_EXPORT LoadSuperProperty : Method<Throws::Yes> { static ReturnedValue call(ExecutionEngine *, const Value &); diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp index a84521e205..a5e62d3e35 100644 --- a/src/qml/jsruntime/qv4serialize.cpp +++ b/src/qml/jsruntime/qv4serialize.cpp @@ -39,11 +39,6 @@ #include "qv4serialize_p.h" -#if QT_CONFIG(qml_list_model) -#include <private/qqmllistmodel_p.h> -#include <private/qqmllistmodelworkeragent_p.h> -#endif - #include <private/qv4value_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4regexpobject_p.h> @@ -85,9 +80,7 @@ enum Type { WorkerNumber, WorkerDate, WorkerRegexp, -#if QT_CONFIG(qml_list_model) WorkerListModel, -#endif #if QT_CONFIG(qml_sequence_object) WorkerSequence #endif @@ -235,18 +228,15 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine } else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) { // XXX TODO: Generalize passing objects between the main thread and worker scripts so // that others can trivially plug in their elements. -#if QT_CONFIG(qml_list_model) - QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object()); - if (lm && lm->agent()) { - QQmlListModelWorkerAgent *agent = lm->agent(); - agent->addref(); - push(data, valueheader(WorkerListModel)); - push(data, (void *)agent); - return; + if (QObject *lm = qobjectWrapper->object()) { + if (QObject *agent = qvariant_cast<QObject *>(lm->property("agent"))) { + if (QMetaObject::invokeMethod(agent, "addref")) { + push(data, valueheader(WorkerListModel)); + push(data, (void *)agent); + return; + } + } } -#else - Q_UNUSED(qobjectWrapper); -#endif // No other QObject's are allowed to be sent push(data, valueheader(WorkerUndefined)); } else if (const Object *o = v.as<Object>()) { @@ -298,6 +288,41 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine } } +struct VariantRef +{ + VariantRef() : obj(nullptr) {} + VariantRef(const VariantRef &r) : obj(r.obj) { addref(); } + VariantRef(QObject *a) : obj(a) { addref(); } + ~VariantRef() { release(); } + + VariantRef &operator=(const VariantRef &o) { + o.addref(); + release(); + obj = o.obj; + return *this; + } + + void addref() const + { + if (obj) + QMetaObject::invokeMethod(obj, "addref"); + } + + void release() const + { + if (obj) + QMetaObject::invokeMethod(obj, "release"); + + } + + QObject *obj; +}; + +QT_END_NAMESPACE +Q_DECLARE_METATYPE(VariantRef) +Q_DECLARE_METATYPE(QV4::ExecutionEngine *) +QT_BEGIN_NAMESPACE + ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) { quint32 header = popUint32(data); @@ -366,24 +391,21 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine) data += ALIGN(length * sizeof(quint16)); return Encode(engine->newRegExpObject(pattern, flags)); } -#if QT_CONFIG(qml_list_model) case WorkerListModel: { - void *ptr = popPtr(data); - QQmlListModelWorkerAgent *agent = (QQmlListModelWorkerAgent *)ptr; + QObject *agent = reinterpret_cast<QObject *>(popPtr(data)); QV4::ScopedValue rv(scope, QV4::QObjectWrapper::wrap(engine, agent)); // ### Find a better solution then the ugly property - QQmlListModelWorkerAgent::VariantRef ref(agent); + VariantRef ref(agent); QVariant var = QVariant::fromValue(ref); QV4::ScopedValue v(scope, scope.engine->fromVariant(var)); QV4::ScopedString s(scope, engine->newString(QStringLiteral("__qml:hidden:ref"))); rv->as<Object>()->defineReadonlyProperty(s, v); - agent->release(); - agent->setEngine(engine); + QMetaObject::invokeMethod(agent, "release"); + agent->setProperty("engine", QVariant::fromValue(engine)); return rv->asReturnedValue(); } -#endif #if QT_CONFIG(qml_sequence_object) case WorkerSequence: { @@ -423,4 +445,3 @@ ReturnedValue Serialize::deserialize(const QByteArray &data, ExecutionEngine *en } QT_END_NAMESPACE - diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 98e4f4f7b9..ec44f42933 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -61,7 +61,9 @@ #include "qv4alloca_p.h" +#if QT_CONFIG(qml_jit) #include <private/qv4baselinejit_p.h> +#endif #undef COUNT_INSTRUCTIONS @@ -345,85 +347,9 @@ static struct InstrCount { #undef CHECK_EXCEPTION #endif #define CHECK_EXCEPTION \ - if (engine->hasException) \ + if (engine->hasException || engine->isInterrupted) \ goto handleUnwind -static inline void traceJumpTakesTruePath(bool truePathTaken, Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= truePathTaken ? quint8(ObservedTraceValues::TruePathTaken) - : quint8(ObservedTraceValues::FalsePathTaken); -#else - Q_UNUSED(truePathTaken); - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceValue(ReturnedValue acc, Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - switch (Primitive::fromReturnedValue(acc).type()) { - case QV4::Value::Integer_Type: - *traceInfo |= quint8(ObservedTraceValues::Integer); - break; - case QV4::Value::Boolean_Type: - *traceInfo |= quint8(ObservedTraceValues::Boolean); - break; - case QV4::Value::Double_Type: - *traceInfo |= quint8(ObservedTraceValues::Double); - break; - default: - *traceInfo |= quint8(ObservedTraceValues::Other); - break; - } -#else - Q_UNUSED(acc); - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceIntValue(Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= quint8(ObservedTraceValues::Integer); -#else - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceDoubleValue(Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= quint8(ObservedTraceValues::Double); -#else - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - -static inline void traceOtherValue(Function *f, int slot) -{ -#if QT_CONFIG(qml_tracing) - quint8 *traceInfo = f->traceInfo(slot); - Q_ASSERT(traceInfo); - *traceInfo |= quint8(ObservedTraceValues::Other); -#else - Q_UNUSED(f); - Q_UNUSED(slot); -#endif -} - static inline Heap::CallContext *getScope(QV4::Value *stack, int level) { Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d(); @@ -499,22 +425,16 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling QV4::Debugging::Debugger *debugger = engine->debugger(); -#ifdef V4_ENABLE_JIT +#if QT_CONFIG(qml_jit) if (debugger == nullptr) { if (function->jittedCode == nullptr) { - if (engine->canJIT(function)) { -#if QT_CONFIG(qml_tracing) - if (function->tracingEnabled()) - runTracingJit(function); - else -#endif - QV4::JIT::BaselineJIT(function).generate(); - } else { + if (engine->canJIT(function)) + QV4::JIT::BaselineJIT(function).generate(); + else ++function->interpreterCallCount; - } } } -#endif // V4_ENABLE_JIT +#endif // QT_CONFIG(qml_jit) // interpreter if (debugger) @@ -523,22 +443,6 @@ ReturnedValue VME::exec(CppStackFrame *frame, ExecutionEngine *engine) ReturnedValue result; if (function->jittedCode != nullptr && debugger == nullptr) { result = function->jittedCode(frame, engine); - if (QV4::Value::fromReturnedValue(result).isEmpty()) { // de-optimize! - if (ShowWhenDeoptimiationHappens) { - // This is debug code, which is disabled by default, and completely removed by the - // compiler. - fprintf(stderr, "*********************** DEOPT! %s ***********************\n" - "*** deopt IP: %d, line: %d\n", - function->name()->toQString().toUtf8().constData(), - frame->instructionPointer, - frame->lineNumber()); - } - delete function->codeRef; - function->codeRef = nullptr; - function->jittedCode = nullptr; - function->interpreterCallCount = 0; // reset to restart tracing: apparently we didn't have enough info before - result = interpret(frame, engine, function->codeData + frame->instructionPointer); - } } else { // interpreter result = interpret(frame, engine, function->codeData); @@ -557,11 +461,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, QV4::ReturnedValue acc = accumulator.asReturnedValue(); Value *stack = reinterpret_cast<Value *>(frame->jsFrame); - if (function->tracingEnabled()) { - for (int i = 0; i < int(function->nFormals); ++i) - traceValue(frame->jsFrame->argument(i), function, i); - } - MOTH_JUMP_TABLE; for (;;) { @@ -620,7 +519,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); acc = cc->locals[index].asReturnedValue(); - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -633,7 +531,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadScopedLocal) auto cc = getScope(stack, scope); acc = cc->locals[index].asReturnedValue(); - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -658,7 +555,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_IP(); acc = Runtime::LoadName::call(engine, name); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) @@ -666,7 +562,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; acc = l->globalGetter(l, engine); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) @@ -674,7 +569,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; acc = l->qmlContextPropertyGetter(l, engine, nullptr); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameStrict) @@ -694,25 +588,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadElement) STORE_IP(); STORE_ACC(); -#if QT_CONFIG(qml_tracing) - acc = Runtime::LoadElement_Traced::call(engine, STACK_VALUE(base), accumulator, function->traceInfo(traceSlot)); - traceValue(acc, function, traceSlot); -#else - Q_UNUSED(traceSlot); acc = Runtime::LoadElement::call(engine, STACK_VALUE(base), accumulator); -#endif CHECK_EXCEPTION; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) STORE_IP(); STORE_ACC(); -#if QT_CONFIG(qml_tracing) - Runtime::StoreElement_traced::call(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator, function->traceInfo(traceSlot)); -#else - Q_UNUSED(traceSlot); Runtime::StoreElement::call(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator); -#endif CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) @@ -721,7 +604,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_ACC(); acc = Runtime::LoadProperty::call(engine, accumulator, name); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) @@ -740,7 +622,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = l->getter(l, engine, accumulator); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) @@ -814,7 +695,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, Value undef = Value::undefinedValue(); acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) @@ -826,14 +706,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, } acc = static_cast<const FunctionObject &>(func).call(stack + thisObject, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) STORE_IP(); acc = Runtime::CallProperty::call(engine, stack[base], name, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) @@ -861,49 +739,42 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) STORE_IP(); acc = Runtime::CallElement::call(engine, stack[base], STACK_VALUE(index), stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) STORE_IP(); acc = Runtime::CallName::call(engine, name, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) STORE_IP(); acc = Runtime::CallPossiblyDirectEval::call(engine, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) STORE_IP(); acc = Runtime::CallGlobalLookup::call(engine, index, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) STORE_IP(); acc = Runtime::CallQmlContextPropertyLookup::call(engine, index, stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) STORE_IP(); acc = Runtime::CallWithSpread::call(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(TailCall) @@ -1118,7 +989,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, takeJump = ACC.int_32(); else takeJump = ACC.toBoolean(); - traceJumpTakesTruePath(takeJump, function, traceSlot); if (takeJump) code += offset; MOTH_END_INSTR(JumpTrue) @@ -1129,7 +999,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, takeJump = !ACC.int_32(); else takeJump = !ACC.toBoolean(); - traceJumpTakesTruePath(!takeJump, function, traceSlot); if (takeJump) code += offset; MOTH_END_INSTR(JumpFalse) @@ -1144,6 +1013,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, code += offset; MOTH_END_INSTR(JumpNotUndefined) + MOTH_BEGIN_INSTR(CheckException) + CHECK_EXCEPTION; + MOTH_END_INSTR(CheckException) + MOTH_BEGIN_INSTR(CmpEqNull) acc = Encode(ACC.isNullOrUndefined()); MOTH_END_INSTR(CmpEqNull) @@ -1289,12 +1162,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(UNot) MOTH_BEGIN_INSTR(UPlus) - if (Q_LIKELY(ACC.isNumber())) { - if (ACC.isDouble()) - traceDoubleValue(function, traceSlot); - else - traceIntValue(function, traceSlot); - } else { + if (Q_UNLIKELY(!ACC.isNumber())) { acc = Encode(ACC.toNumberImpl()); CHECK_EXCEPTION; } @@ -1305,17 +1173,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, int a = ACC.int_32(); if (a == 0 || a == std::numeric_limits<int>::min()) { acc = Encode(-static_cast<double>(a)); - traceDoubleValue(function, traceSlot); } else { - acc = sub_int32(0, ACC.int_32(), function->traceInfo(traceSlot)); + acc = sub_int32(0, ACC.int_32()); } } else if (ACC.isDouble()) { acc ^= (1ull << 63); // simply flip sign bit - traceDoubleValue(function, traceSlot); } else { acc = Encode(-ACC.toNumberImpl()); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(UMinus) @@ -1326,57 +1191,49 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Increment) if (Q_LIKELY(ACC.integerCompatible())) { - acc = add_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); + acc = add_int32(ACC.int_32(), 1); } else if (ACC.isDouble()) { acc = QV4::Encode(ACC.doubleValue() + 1.); - traceDoubleValue(function, traceSlot); } else { acc = Encode(ACC.toNumberImpl() + 1.); CHECK_EXCEPTION; - traceDoubleValue(function, traceSlot); } MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) if (Q_LIKELY(ACC.integerCompatible())) { - acc = sub_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); + acc = sub_int32(ACC.int_32(), 1); } else if (ACC.isDouble()) { acc = QV4::Encode(ACC.doubleValue() - 1.); - traceDoubleValue(function, traceSlot); } else { acc = Encode(ACC.toNumberImpl() - 1.); CHECK_EXCEPTION; - traceDoubleValue(function, traceSlot); } MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = add_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = add_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() + ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::Add::call(engine, left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(Sub) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = sub_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = sub_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() - ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::Sub::call(left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Sub) @@ -1393,15 +1250,13 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Mul) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = mul_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); + acc = mul_int32(left.int_32(), ACC.int_32()); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() * ACC.asDouble()); - traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::Mul::call(left, accumulator); CHECK_EXCEPTION; - traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Mul) @@ -1415,7 +1270,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_ACC(); acc = Runtime::Mod::call(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; - traceValue(acc, function, traceSlot); MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(BitAnd) @@ -1513,7 +1367,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(Debug) handleUnwind: - Q_ASSERT(engine->hasException || frame->unwindLevel); + // We do start the exception handler in case of isInterrupted. The exception handler will + // immediately abort, due to the same isInterrupted. We don't skip the exception handler + // because the current behavior is easier to implement in the JIT. + Q_ASSERT(engine->hasException || engine->isInterrupted || frame->unwindLevel); if (!frame->unwindHandler) { acc = Encode::undefined(); return acc; diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index 4ac7120d36..8a76e60f20 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -58,8 +58,6 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace Moth { -void runTracingJit(QV4::Function *function); - class VME { public: diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h index a4d91640c5..9dda104cd1 100644 --- a/src/qml/jsruntime/qv4vtable_p.h +++ b/src/qml/jsruntime/qv4vtable_p.h @@ -58,7 +58,7 @@ namespace QV4 { struct Lookup; -struct OwnPropertyKeyIterator { +struct Q_QML_PRIVATE_EXPORT OwnPropertyKeyIterator { virtual ~OwnPropertyKeyIterator() = 0; virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0; }; diff --git a/src/qml/qml.pro b/src/qml/qml.pro index d96a1c285a..ca3282556e 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -70,7 +70,9 @@ include(parser/parser.pri) include(compiler/compiler.pri) include(jsapi/jsapi.pri) include(jsruntime/jsruntime.pri) -include(jit/jit.pri) +qtConfig(qml-jit) { + include(jit/jit.pri) +} include(qml/qml.pri) include(debugger/debugger.pri) include(qmldirparser/qmldirparser.pri) diff --git a/src/qml/qml/ftw/ftw.pri b/src/qml/qml/ftw/ftw.pri index 0bb8cb954e..eadba394b4 100644 --- a/src/qml/qml/ftw/ftw.pri +++ b/src/qml/qml/ftw/ftw.pri @@ -3,6 +3,7 @@ HEADERS += \ $$PWD/qintrusivelist_p.h \ $$PWD/qpodvector_p.h \ $$PWD/qhashedstring_p.h \ + $$PWD/qprimefornumbits_p.h \ $$PWD/qqmlrefcount_p.h \ $$PWD/qfieldlist_p.h \ $$PWD/qqmlthread_p.h \ @@ -18,8 +19,7 @@ HEADERS += \ SOURCES += \ $$PWD/qintrusivelist.cpp \ $$PWD/qhashedstring.cpp \ - $$PWD/qqmlthread.cpp \ - $$PWD/qstringhash.cpp + $$PWD/qqmlthread.cpp # mirrors logic in $$QT_SOURCE_TREE/config.tests/unix/clock-gettime/clock-gettime.pri # clock_gettime() is implemented in librt on these systems diff --git a/src/qml/qml/ftw/qhashedstring.cpp b/src/qml/qml/ftw/qhashedstring.cpp index bb6688599d..7a8fdd0a14 100644 --- a/src/qml/qml/ftw/qhashedstring.cpp +++ b/src/qml/qml/ftw/qhashedstring.cpp @@ -41,6 +41,60 @@ QT_BEGIN_NAMESPACE +// Copy of QString's qMemCompare +bool QHashedString::compare(const QChar *lhs, const QChar *rhs, int length) +{ + Q_ASSERT(lhs && rhs); + const quint16 *a = (const quint16 *)lhs; + const quint16 *b = (const quint16 *)rhs; + + if (a == b || !length) + return true; + + union { + const quint16 *w; + const quint32 *d; + quintptr value; + } sa, sb; + sa.w = a; + sb.w = b; + + // check alignment + if ((sa.value & 2) == (sb.value & 2)) { + // both addresses have the same alignment + if (sa.value & 2) { + // both addresses are not aligned to 4-bytes boundaries + // compare the first character + if (*sa.w != *sb.w) + return false; + --length; + ++sa.w; + ++sb.w; + + // now both addresses are 4-bytes aligned + } + + // both addresses are 4-bytes aligned + // do a fast 32-bit comparison + const quint32 *e = sa.d + (length >> 1); + for ( ; sa.d != e; ++sa.d, ++sb.d) { + if (*sa.d != *sb.d) + return false; + } + + // do we have a tail? + return (length & 1) ? *sa.w == *sb.w : true; + } else { + // one of the addresses isn't 4-byte aligned but the other is + const quint16 *e = sa.w + length; + for ( ; sa.w != e; ++sa.w, ++sb.w) { + if (*sa.w != *sb.w) + return false; + } + } + return true; +} + QHashedStringRef QHashedStringRef::mid(int offset, int length) const { Q_ASSERT(offset < m_length); diff --git a/src/qml/types/qqmlmodelsmodule_p.h b/src/qml/qml/ftw/qprimefornumbits_p.h index 2bb04f1e11..6e9acbf7fd 100644 --- a/src/qml/types/qqmlmodelsmodule_p.h +++ b/src/qml/qml/ftw/qprimefornumbits_p.h @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 Research In Motion. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtQml module of the Qt Toolkit. @@ -37,8 +37,8 @@ ** ****************************************************************************/ -#ifndef QQMLMODELSMODULE_H -#define QQMLMODELSMODULE_H +#ifndef QPRIMEFORNUMBITS_P_H +#define QPRIMEFORNUMBITS_P_H // // W A R N I N G @@ -55,18 +55,26 @@ QT_BEGIN_NAMESPACE -class Q_QML_PRIVATE_EXPORT QQmlModelsModule +/* + The prime_deltas array is a table of selected prime values, even + though it doesn't look like one. The primes we are using are 1, + 2, 5, 11, 17, 37, 67, 131, 257, ..., i.e. primes in the immediate + surrounding of a power of two. + + The qPrimeForNumBits() function returns the prime associated to a + power of two. For example, qPrimeForNumBits(8) returns 257. +*/ + +inline int qPrimeForNumBits(int numBits) { -public: -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - static void registerQmlTypes(); - static void registerQuickTypes(); -#endif + static constexpr const uchar prime_deltas[] = { + 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, + 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 + }; - static void defineModule(); - static void defineLabsModule(); -}; + return (1 << numBits) + prime_deltas[numBits]; +} QT_END_NAMESPACE -#endif +#endif // QPRIMEFORNUMBITS_P_H diff --git a/src/qml/qml/ftw/qstringhash.cpp b/src/qml/qml/ftw/qstringhash.cpp deleted file mode 100644 index a483dcb810..0000000000 --- a/src/qml/qml/ftw/qstringhash.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qstringhash_p.h" - -QT_BEGIN_NAMESPACE - -/* - A QHash has initially around pow(2, MinNumBits) buckets. For - example, if MinNumBits is 4, it has 17 buckets. -*/ -static const int MinNumBits = 4; - -/* - The prime_deltas array is a table of selected prime values, even - though it doesn't look like one. The primes we are using are 1, - 2, 5, 11, 17, 37, 67, 131, 257, ..., i.e. primes in the immediate - surrounding of a power of two. - - The primeForNumBits() function returns the prime associated to a - power of two. For example, primeForNumBits(8) returns 257. -*/ - -static const uchar prime_deltas[] = { - 0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3, - 1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0 -}; - -static inline int primeForNumBits(int numBits) -{ - return (1 << numBits) + prime_deltas[numBits]; -} - -void QStringHashData::rehashToSize(int size) -{ - short bits = qMax(MinNumBits, (int)numBits); - while (primeForNumBits(bits) < size) bits++; - - if (bits > numBits) - rehashToBits(bits); -} - -void QStringHashData::rehashToBits(short bits) -{ - numBits = qMax(MinNumBits, (int)bits); - - int nb = primeForNumBits(numBits); - if (nb == numBuckets && buckets) - return; - - QStringHashNode **newBuckets = new QStringHashNode *[nb]; - ::memset(newBuckets, 0, sizeof(QStringHashNode *) * nb); - - // Preserve the existing order within buckets so that items with the - // same key will retain the same find/findNext order - for (int i = 0; i < numBuckets; ++i) { - QStringHashNode *bucket = buckets[i]; - if (bucket) - rehashNode(newBuckets, nb, bucket); - } - - delete [] buckets; - buckets = newBuckets; - numBuckets = nb; -} - -void QStringHashData::rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node) -{ - QStringHashNode *next = node->next.data(); - if (next) - rehashNode(newBuckets, nb, next); - - int bucket = node->hash % nb; - node->next = newBuckets[bucket]; - newBuckets[bucket] = node; -} - -// Copy of QString's qMemCompare -bool QHashedString::compare(const QChar *lhs, const QChar *rhs, int length) -{ - Q_ASSERT(lhs && rhs); - const quint16 *a = (const quint16 *)lhs; - const quint16 *b = (const quint16 *)rhs; - - if (a == b || !length) - return true; - - union { - const quint16 *w; - const quint32 *d; - quintptr value; - } sa, sb; - sa.w = a; - sb.w = b; - - // check alignment - if ((sa.value & 2) == (sb.value & 2)) { - // both addresses have the same alignment - if (sa.value & 2) { - // both addresses are not aligned to 4-bytes boundaries - // compare the first character - if (*sa.w != *sb.w) - return false; - --length; - ++sa.w; - ++sb.w; - - // now both addresses are 4-bytes aligned - } - - // both addresses are 4-bytes aligned - // do a fast 32-bit comparison - const quint32 *e = sa.d + (length >> 1); - for ( ; sa.d != e; ++sa.d, ++sb.d) { - if (*sa.d != *sb.d) - return false; - } - - // do we have a tail? - return (length & 1) ? *sa.w == *sb.w : true; - } else { - // one of the addresses isn't 4-byte aligned but the other is - const quint16 *e = sa.w + length; - for ( ; sa.w != e; ++sa.w, ++sb.w) { - if (*sa.w != *sb.w) - return false; - } - } - return true; -} - -QT_END_NAMESPACE diff --git a/src/qml/qml/ftw/qstringhash_p.h b/src/qml/qml/ftw/qstringhash_p.h index c7251e8837..f9435b4919 100644 --- a/src/qml/qml/ftw/qstringhash_p.h +++ b/src/qml/qml/ftw/qstringhash_p.h @@ -52,11 +52,14 @@ // #include <private/qhashedstring_p.h> +#include <private/qprimefornumbits_p.h> + +#include <QtCore/qglobal.h> QT_BEGIN_NAMESPACE class QStringHashData; -class Q_AUTOTEST_EXPORT QStringHashNode +class QStringHashNode { public: QStringHashNode() @@ -154,12 +157,20 @@ public: } }; -class Q_AUTOTEST_EXPORT QStringHashData +class QStringHashData { + Q_DISABLE_COPY_MOVE(QStringHashData) public: - QStringHashData() {} + QStringHashData() = default; + ~QStringHashData() = default; + + /* + A QHash has initially around pow(2, MinNumBits) buckets. For + example, if MinNumBits is 4, it has 17 buckets. + */ + enum { MinNumBits = 4 }; - QStringHashNode **buckets = nullptr; + QStringHashNode **buckets = nullptr; // life cycle managed by QStringHash int numBuckets = 0; int size = 0; short numBits = 0; @@ -174,13 +185,51 @@ public: QStringHashNode *n; StringHash *p; }; - void rehashToBits(short); - void rehashToSize(int); - void rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node); -private: - QStringHashData(const QStringHashData &); - QStringHashData &operator=(const QStringHashData &); + void rehashToBits(short bits) + { + numBits = qMax(short(MinNumBits), bits); + + int nb = qPrimeForNumBits(numBits); + if (nb == numBuckets && buckets) + return; + + QStringHashNode **newBuckets = new QStringHashNode *[nb]; + ::memset(newBuckets, 0, sizeof(QStringHashNode *) * nb); + + // Preserve the existing order within buckets so that items with the + // same key will retain the same find/findNext order + for (int i = 0; i < numBuckets; ++i) { + QStringHashNode *bucket = buckets[i]; + if (bucket) + rehashNode(newBuckets, nb, bucket); + } + + delete [] buckets; + buckets = newBuckets; + numBuckets = nb; + } + + void rehashToSize(int size) + { + short bits = qMax(short(MinNumBits), numBits); + while (qPrimeForNumBits(bits) < size) + bits++; + + if (bits > numBits) + rehashToBits(bits); + } + + void rehashNode(QStringHashNode **newBuckets, int nb, QStringHashNode *node) + { + QStringHashNode *next = node->next.data(); + if (next) + rehashNode(newBuckets, nb, next); + + int bucket = node->hash % nb; + node->next = newBuckets[bucket]; + newBuckets[bucket] = node; + } }; // For a supplied key type, in what form do we need to keep a hashed version? diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 3000f56601..7b3f89e943 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -579,9 +579,15 @@ namespace QtQml { Q_QML_EXPORT void qmlExecuteDeferred(QObject *); Q_QML_EXPORT QQmlContext *qmlContext(const QObject *); Q_QML_EXPORT QQmlEngine *qmlEngine(const QObject *); - Q_QML_EXPORT QObject *qmlAttachedPropertiesObjectById(int, const QObject *, bool create = true); - Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(int *, const QObject *, - const QMetaObject *, bool create); +#if QT_DEPRECATED_SINCE(5, 14) + Q_QML_EXPORT QT_DEPRECATED QObject *qmlAttachedPropertiesObjectById(int, const QObject *, bool create = true); + Q_QML_EXPORT QT_DEPRECATED QObject *qmlAttachedPropertiesObject( + int *, const QObject *, const QMetaObject *, bool create); +#endif + Q_QML_EXPORT QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *, + const QMetaObject *); + Q_QML_EXPORT QObject *qmlAttachedPropertiesObject(QObject *, QQmlAttachedPropertiesFunc func, + bool create = true); #ifndef Q_QDOC } @@ -602,8 +608,9 @@ Q_QML_EXPORT void qmlRegisterModule(const char *uri, int versionMajor, int versi template<typename T> QObject *qmlAttachedPropertiesObject(const QObject *obj, bool create = true) { - static int idx = -1; - return qmlAttachedPropertiesObject(&idx, obj, &T::staticMetaObject, create); + QObject *mutableObj = const_cast<QObject *>(obj); + return qmlAttachedPropertiesObject( + mutableObj, qmlAttachedPropertiesFunction(mutableObj, &T::staticMetaObject), create); } inline int qmlRegisterSingletonType(const char *uri, int versionMajor, int versionMinor, const char *typeName, diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index b164517011..656c7dd515 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -517,9 +517,9 @@ QString QQmlBinding::expressionIdentifier() const { if (auto f = function()) { QString url = f->sourceFile(); - quint16 lineNumber = f->compiledFunction->location.line; - quint16 columnNumber = f->compiledFunction->location.column; - return url + QString::asprintf(":%u:%u", uint(lineNumber), uint(columnNumber)); + uint lineNumber = f->compiledFunction->location.line; + uint columnNumber = f->compiledFunction->location.column; + return url + QString::asprintf(":%u:%u", lineNumber, columnNumber); } return QStringLiteral("[native code]"); diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index 2468de6857..f4c03fc17c 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -57,6 +57,7 @@ #include <private/qv4value_p.h> #include <private/qv4persistent_p.h> #include <private/qqmlrefcount_p.h> +#include <qqmlprivate.h> #include <qjsengine.h> #include <qvector.h> @@ -265,7 +266,7 @@ public: } bool hasExtendedData() const { return extendedData != nullptr; } - QHash<int, QObject *> *attachedProperties() const; + QHash<QQmlAttachedPropertiesFunc, QObject *> *attachedProperties() const; static inline bool wasDeleted(const QObject *); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index f070f16afd..bb2b3e462c 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -1057,7 +1057,7 @@ QQmlEngine::~QQmlEngine() // XXX TODO: performance -- store list of singleton types separately? QList<QQmlType> singletonTypes = QQmlMetaType::qmlSingletonTypes(); for (const QQmlType &currType : singletonTypes) - currType.singletonInstanceInfo()->destroy(this); + d->destroySingletonInstance(currType); delete d->rootContext; d->rootContext = nullptr; @@ -1402,23 +1402,13 @@ void QQmlEngine::setOutputWarningsToStandardError(bool enabled) template<> QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId) { + Q_D(QQmlEngine); QQmlType type = QQmlMetaType::qmlType(qmlTypeId, QQmlMetaType::TypeIdCategory::QmlType); if (!type.isValid() || !type.isSingleton()) return QJSValue(); - QQmlType::SingletonInstanceInfo* info = type.singletonInstanceInfo(); - info->init(this); - - if (QObject* o = info->qobjectApi(this)) - return this->newQObject(o); - else { - QJSValue value = info->scriptApi(this); - if (!value.isUndefined()) - return value; - } - - return QJSValue(); + return d->singletonInstance<QJSValue>(type); } /*! @@ -1617,29 +1607,39 @@ QQmlEngine *qmlEngine(const QObject *obj) return data->context->engine; } -QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool create) +static QObject *resolveAttachedProperties(QQmlAttachedPropertiesFunc pf, QQmlData *data, + QObject *object, bool create) { - QQmlData *data = QQmlData::get(object, create); - if (!data) - return nullptr; // Attached properties are only on objects created by QML, unless explicitly requested (create==true) + if (!pf) + return nullptr; - QObject *rv = data->hasExtendedData()?data->attachedProperties()->value(id):0; + QObject *rv = data->hasExtendedData() ? data->attachedProperties()->value(pf) : 0; if (rv || !create) return rv; - QQmlEnginePrivate *engine = QQmlEnginePrivate::get(data->context); - QQmlAttachedPropertiesFunc pf = QQmlMetaType::attachedPropertiesFuncById(engine, id); - if (!pf) - return nullptr; - - rv = pf(const_cast<QObject *>(object)); + rv = pf(object); if (rv) - data->attachedProperties()->insert(id, rv); + data->attachedProperties()->insert(pf, rv); return rv; } +#if QT_DEPRECATED_SINCE(5, 14) +QObject *qmlAttachedPropertiesObjectById(int id, const QObject *object, bool create) +{ + QQmlData *data = QQmlData::get(object, create); + + // Attached properties are only on objects created by QML, + // unless explicitly requested (create==true) + if (!data) + return nullptr; + + QQmlEnginePrivate *engine = QQmlEnginePrivate::get(data->context); + return resolveAttachedProperties(QQmlMetaType::attachedPropertiesFuncById(engine, id), data, + const_cast<QObject *>(object), create); +} + QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object, const QMetaObject *attachedMetaObject, bool create) { @@ -1653,6 +1653,30 @@ QObject *qmlAttachedPropertiesObject(int *idCache, const QObject *object, return qmlAttachedPropertiesObjectById(*idCache, object, create); } +#endif + +QQmlAttachedPropertiesFunc qmlAttachedPropertiesFunction(QObject *object, + const QMetaObject *attachedMetaObject) +{ + QQmlEngine *engine = object ? qmlEngine(object) : nullptr; + return QQmlMetaType::attachedPropertiesFunc(engine ? QQmlEnginePrivate::get(engine) : nullptr, + attachedMetaObject); +} + +QObject *qmlAttachedPropertiesObject(QObject *object, QQmlAttachedPropertiesFunc func, bool create) +{ + if (!object) + return nullptr; + + QQmlData *data = QQmlData::get(object, create); + + // Attached properties are only on objects created by QML, + // unless explicitly requested (create==true) + if (!data) + return nullptr; + + return resolveAttachedProperties(func, data, object, create); +} } // namespace QtQml @@ -1694,7 +1718,7 @@ public: QQmlDataExtended(); ~QQmlDataExtended(); - QHash<int, QObject *> attachedProperties; + QHash<QQmlAttachedPropertiesFunc, QObject *> attachedProperties; }; QQmlDataExtended::QQmlDataExtended() @@ -1840,7 +1864,7 @@ void QQmlData::disconnectNotifiers() } } -QHash<int, QObject *> *QQmlData::attachedProperties() const +QHash<QQmlAttachedPropertiesFunc, QObject *> *QQmlData::attachedProperties() const { if (!extendedData) extendedData = new QQmlDataExtended; return &extendedData->attachedProperties; @@ -2413,6 +2437,67 @@ void QQmlEnginePrivate::unregisterInternalCompositeType(QV4::CompiledData::Compi m_compositeTypes.remove(compilationUnit->metaTypeId); } +template<> +QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type) +{ + Q_Q(QQmlEngine); + + QJSValue value = singletonInstances.value(type); + if (!value.isUndefined()) { + return value; + } + + QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); + Q_ASSERT(siinfo != nullptr); + + if (siinfo->scriptCallback) { + value = siinfo->scriptCallback(q, q); + if (value.isQObject()) { + QObject *o = value.toQObject(); + // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) + // should behave identically to QML singleton types. + q->setContextForObject(o, new QQmlContext(q->rootContext(), q)); + } + singletonInstances.insert(type, value); + + } else if (siinfo->qobjectCallback) { + QObject *o = siinfo->qobjectCallback(q, q); + if (!o) { + qFatal("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.", + qPrintable(QString::fromUtf8(type.typeName()))); + } + // if this object can use a property cache, create it now + QQmlData::ensurePropertyCache(q, o); + // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) + // should behave identically to QML singleton types. + q->setContextForObject(o, new QQmlContext(q->rootContext(), q)); + value = q->newQObject(o); + singletonInstances.insert(type, value); + + } else if (!siinfo->url.isEmpty()) { + QQmlComponent component(q, siinfo->url, QQmlComponent::PreferSynchronous); + QObject *o = component.beginCreate(q->rootContext()); + value = q->newQObject(o); + singletonInstances.insert(type, value); + component.completeCreate(); + } + + return value; +} + +void QQmlEnginePrivate::destroySingletonInstance(const QQmlType &type) +{ + Q_ASSERT(type.isSingleton() || type.isCompositeSingleton()); + + QObject* o = singletonInstances.take(type).toQObject(); + if (o) { + QQmlData *ddata = QQmlData::get(o, false); + if (type.singletonInstanceInfo()->url.isEmpty() && ddata && ddata->indestructible && ddata->explicitIndestructibleSet) + return; + delete o; + } +} + bool QQmlEnginePrivate::isTypeLoaded(const QUrl &url) const { return typeLoader.isTypeLoaded(url); diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 871e6bd9b4..da91c8fa15 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -175,12 +175,7 @@ Q_QML_EXPORT QJSValue QQmlEngine::singletonInstance<QJSValue>(int qmlTypeId); template<typename T> T QQmlEngine::singletonInstance(int qmlTypeId) { - QJSValue instance = singletonInstance<QJSValue>(qmlTypeId); - if (!instance.isQObject()) - return nullptr; - - QObject *object = instance.toQObject(); - return qobject_cast<T>(object); + return qobject_cast<T>(singletonInstance<QJSValue>(qmlTypeId).toQObject()); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index dab4e54cd6..4f7fb79593 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -229,6 +229,10 @@ public: bool isTypeLoaded(const QUrl &url) const; bool isScriptLoaded(const QUrl &url) const; + template <typename T> + T singletonInstance(const QQmlType &type); + void destroySingletonInstance(const QQmlType &type); + void sendQuit(); void sendExit(int retCode = 0); void warning(const QQmlError &); @@ -262,6 +266,8 @@ public: mutable QMutex networkAccessManagerMutex; private: + QHash<QQmlType, QJSValue> singletonInstances; + // These members must be protected by a QQmlEnginePrivate::Locker as they are required by // the threaded loader. Only access them through their respective accessor methods. QHash<int, QV4::CompiledData::CompilationUnit *> m_compositeTypes; @@ -437,6 +443,14 @@ QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e) return get(qmlEngine); } +template<> +Q_QML_PRIVATE_EXPORT QJSValue QQmlEnginePrivate::singletonInstance<QJSValue>(const QQmlType &type); + +template<typename T> +T QQmlEnginePrivate::singletonInstance(const QQmlType &type) { + return qobject_cast<T>(singletonInstance<QJSValue>(type).toQObject()); +} + QT_END_NAMESPACE #endif // QQMLENGINE_P_H diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index 453c8ab8a8..92f2ccbb4a 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -182,7 +182,7 @@ private: QV4::Function *m_v4Function; }; -class QQmlPropertyCapture +class Q_QML_PRIVATE_EXPORT QQmlPropertyCapture { public: QQmlPropertyCapture(QQmlEngine *engine, QQmlJavaScriptExpression *e, QQmlJavaScriptExpression::DeleteWatcher *w) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 32f281b4f2..09df23de51 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -76,6 +76,8 @@ public: const QQmlMetaTypeData *operator->() const { return data; } operator const QQmlMetaTypeData *() const { return data; } + bool isValid() const { return data != nullptr; } + private: QMutexLocker locker; LockedData *data = nullptr; @@ -143,12 +145,6 @@ static QQmlTypePrivate *createQQmlType(QQmlMetaTypeData *data, const QString &el d->baseMetaObject = type.metaObject; d->extraData.cd->attachedPropertiesFunc = type.attachedPropertiesFunction; d->extraData.cd->attachedPropertiesType = type.attachedPropertiesMetaObject; - if (d->extraData.cd->attachedPropertiesType) { - d->extraData.cd->attachedPropertiesId = data->attachedPropertyId(d->baseMetaObject, - d->index); - } else { - d->extraData.cd->attachedPropertiesId = -1; - } d->extraData.cd->parserStatusCast = type.parserStatusCast; d->extraData.cd->propertyValueSourceCast = type.valueSourceCast; d->extraData.cd->propertyValueInterceptorCast = type.valueInterceptorCast; @@ -599,19 +595,6 @@ void QQmlMetaType::registerUndeletableType(const QQmlType &dtype) data->undeletableTypes.insert(dtype); } -int QQmlMetaType::registerAttachedPropertyId(const QMetaObject *metaObject, int index) -{ - QQmlMetaTypeDataPtr data; - return data->attachedPropertyId(metaObject, index); -} - -bool QQmlMetaType::unregisterAttachedPropertyId(const QMetaObject *metaObject, int index) -{ - QQmlMetaTypeDataPtr data; - // This is run from the QQmlType dtor. QQmlTypes in user code can outlive QQmlMetaTypeData. - return data ? data->removeAttachedPropertyId(metaObject, index) : false; -} - static bool namespaceContainsRegistrations(const QQmlMetaTypeData *data, const QString &uri, int majorVersion) { @@ -916,6 +899,7 @@ int QQmlMetaType::listType(int id) return 0; } +#if QT_DEPRECATED_SINCE(5, 14) int QQmlMetaType::attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *mo) { QQmlMetaTypeDataPtr data; @@ -937,6 +921,16 @@ QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(QQmlEnginePr QQmlMetaTypeDataPtr data; return data->types.at(id).attachedPropertiesFunction(engine); } +#endif + +QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFunc(QQmlEnginePrivate *engine, + const QMetaObject *mo) +{ + QQmlMetaTypeDataPtr data; + + QQmlType type(data->metaObjectToType.value(mo)); + return type.attachedPropertiesFunction(engine); +} QMetaProperty QQmlMetaType::defaultProperty(const QMetaObject *metaObject) { @@ -1216,6 +1210,10 @@ void QQmlMetaType::freeUnusedTypesAndCaches() { QQmlMetaTypeDataPtr data; + // in case this is being called during program exit, `data` might be destructed already + if (!data.isValid()) + return; + bool deletedAtLeastOneType; do { deletedAtLeastOneType = false; diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index dde9cf68d7..9af982d1c3 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -88,9 +88,6 @@ public: static void registerUndeletableType(const QQmlType &dtype); - static int registerAttachedPropertyId(const QMetaObject *metaObject, int index); - static bool unregisterAttachedPropertyId(const QMetaObject *metaObject, int index); - static QList<QString> qmlTypeNames(); static QList<QQmlType> qmlTypes(); static QList<QQmlType> qmlSingletonTypes(); @@ -122,8 +119,14 @@ public: static QObject *toQObject(const QVariant &, bool *ok = nullptr); static int listType(int); - static int attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMetaObject *); - static QQmlAttachedPropertiesFunc attachedPropertiesFuncById(QQmlEnginePrivate *, int); +#if QT_DEPRECATED_SINCE(5, 14) + static QT_DEPRECATED int attachedPropertiesFuncId(QQmlEnginePrivate *engine, + const QMetaObject *); + static QT_DEPRECATED QQmlAttachedPropertiesFunc attachedPropertiesFuncById(QQmlEnginePrivate *, + int); +#endif + static QQmlAttachedPropertiesFunc attachedPropertiesFunc(QQmlEnginePrivate *, + const QMetaObject *); enum TypeCategory { Unknown, Object, List }; static TypeCategory typeCategory(int); diff --git a/src/qml/qml/qqmlmetatypedata_p.h b/src/qml/qml/qqmlmetatypedata_p.h index c45bc16280..5239b635ce 100644 --- a/src/qml/qml/qqmlmetatypedata_p.h +++ b/src/qml/qml/qqmlmetatypedata_p.h @@ -134,27 +134,8 @@ struct QQmlMetaTypeData qWarning("%s", message.toUtf8().constData()); } - int attachedPropertyId(const QMetaObject *metaObject, int ownIndex) - { - auto iter = attachedPropertyIds.find(metaObject); - return (iter == attachedPropertyIds.end()) - ? *attachedPropertyIds.insert(metaObject, ownIndex) - : *iter; - } - - bool removeAttachedPropertyId(const QMetaObject *metaObject, int ownIndex) - { - auto iter = attachedPropertyIds.find(metaObject); - if (iter != attachedPropertyIds.end() && *iter == ownIndex) { - attachedPropertyIds.erase(iter); - return true; - } - return false; - } - private: QStringList *m_typeRegistrationFailures = nullptr; - QHash<const QMetaObject *, int> attachedPropertyIds; }; inline uint qHash(const QQmlMetaTypeData::VersionedUri &v) diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index c36b3ed386..26d3b5b6c1 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -824,8 +824,8 @@ bool QQmlObjectCreator::setPropertyBinding(const QQmlPropertyData *bindingProper else return false; } - const int id = attachedType.attachedPropertiesId(QQmlEnginePrivate::get(engine)); - QObject *qmlObject = qmlAttachedPropertiesObjectById(id, _qobject); + QObject *qmlObject = qmlAttachedPropertiesObject( + _qobject, attachedType.attachedPropertiesFunction(QQmlEnginePrivate::get(engine))); if (!populateInstance(binding->value.objectIndex, qmlObject, qmlObject, /*value type property*/nullptr)) return false; return true; diff --git a/src/qml/qml/qqmlprivate.h b/src/qml/qml/qqmlprivate.h index c0232a7691..fa05b3fe19 100644 --- a/src/qml/qml/qqmlprivate.h +++ b/src/qml/qml/qqmlprivate.h @@ -77,6 +77,11 @@ typedef void (*IRLoaderFunction)(Document *, const QQmlPrivate::CachedQmlUnit *) typedef QObject *(*QQmlAttachedPropertiesFunc)(QObject *); +inline uint qHash(QQmlAttachedPropertiesFunc func, uint seed = 0) +{ + return qHash(quintptr(func), seed); +} + template <typename TYPE> class QQmlTypeInfo { diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index 000b88ebaa..c8166695ba 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -277,7 +277,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate); if (!func) return; // Not an attachable type - currentObject = qmlAttachedPropertiesObjectById(r.type.attachedPropertiesId(enginePrivate), currentObject); + currentObject = qmlAttachedPropertiesObject(currentObject, func); if (!currentObject) return; // Something is broken with the attachable type } else if (r.importNamespace) { if ((ii + 1) == path.count()) return; // No type following the namespace @@ -289,7 +289,7 @@ void QQmlPropertyPrivate::initProperty(QObject *obj, const QString &name) QQmlAttachedPropertiesFunc func = r.type.attachedPropertiesFunction(enginePrivate); if (!func) return; // Not an attachable type - currentObject = qmlAttachedPropertiesObjectById(r.type.attachedPropertiesId(enginePrivate), currentObject); + currentObject = qmlAttachedPropertiesObject(currentObject, func); if (!currentObject) return; // Something is broken with the attachable type } else if (r.scriptIndex != -1) { diff --git a/src/qml/qml/qqmltype.cpp b/src/qml/qml/qqmltype.cpp index 88eedec061..926e2810d5 100644 --- a/src/qml/qml/qqmltype.cpp +++ b/src/qml/qml/qqmltype.cpp @@ -51,70 +51,6 @@ QT_BEGIN_NAMESPACE -void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) -{ - if (scriptCallback && scriptApi(e).isUndefined()) { - QJSValue value = scriptCallback(e, e); - if (value.isQObject()) { - QObject *o = value.toQObject(); - // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) - // should behave identically to QML singleton types. - e->setContextForObject(o, new QQmlContext(e->rootContext(), e)); - } - setScriptApi(e, value); - } else if (qobjectCallback && !qobjectApi(e)) { - QObject *o = qobjectCallback(e, e); - setQObjectApi(e, o); - if (!o) { - qFatal("qmlRegisterSingletonType(): \"%s\" is not available because the callback function returns a null pointer.", qPrintable(typeName)); - } - // if this object can use a property cache, create it now - QQmlData::ensurePropertyCache(e, o); - // even though the object is defined in C++, qmlContext(obj) and qmlEngine(obj) - // should behave identically to QML singleton types. - e->setContextForObject(o, new QQmlContext(e->rootContext(), e)); - } else if (!url.isEmpty() && !qobjectApi(e)) { - QQmlComponent component(e, url, QQmlComponent::PreferSynchronous); - QObject *o = component.beginCreate(e->rootContext()); - setQObjectApi(e, o); - if (o) - component.completeCreate(); - } -} - -void QQmlType::SingletonInstanceInfo::destroy(QQmlEngine *e) -{ - // cleans up the engine-specific singleton instances if they exist. - scriptApis.remove(e); - QObject *o = qobjectApis.take(e); - if (o) { - QQmlData *ddata = QQmlData::get(o, false); - if (url.isEmpty() && ddata && ddata->indestructible && ddata->explicitIndestructibleSet) - return; - delete o; - } -} - -void QQmlType::SingletonInstanceInfo::setQObjectApi(QQmlEngine *e, QObject *o) -{ - qobjectApis.insert(e, o); -} - -QObject *QQmlType::SingletonInstanceInfo::qobjectApi(QQmlEngine *e) const -{ - return qobjectApis.value(e); -} - -void QQmlType::SingletonInstanceInfo::setScriptApi(QQmlEngine *e, const QJSValue &v) -{ - scriptApis.insert(e, v); -} - -QJSValue QQmlType::SingletonInstanceInfo::scriptApi(QQmlEngine *e) const -{ - return scriptApis.value(e); -} - QQmlTypePrivate::QQmlTypePrivate(QQmlType::RegistrationType type) : regType(type), iid(nullptr), typeId(0), listId(0), revision(0), containsRevisionedAttributes(false), baseMetaObject(nullptr), @@ -156,10 +92,6 @@ QQmlTypePrivate::~QQmlTypePrivate() qDeleteAll(scopedEnums); switch (regType) { case QQmlType::CppType: - // If attached properties were successfully registered, deregister them. - // (They may not have been registered if some other type used the same baseMetaObject) - if (extraData.cd->attachedPropertiesType) - QQmlMetaType::unregisterAttachedPropertyId(baseMetaObject, index); delete extraData.cd->customParser; delete extraData.cd; break; @@ -640,6 +572,16 @@ bool QQmlType::isCompositeSingleton() const return d && d->regType == CompositeSingletonType; } +bool QQmlType::isQObjectSingleton() const +{ + return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->qobjectCallback; +} + +bool QQmlType::isQJSValueSingleton() const +{ + return d && d->regType == SingletonType && d->extraData.sd->singletonInstanceInfo->scriptCallback; +} + int QQmlType::typeId() const { return d ? d->typeId : -1; @@ -708,6 +650,7 @@ const QMetaObject *QQmlType::attachedPropertiesType(QQmlEnginePrivate *engine) c return base.attachedPropertiesType(engine); } +#if QT_DEPRECATED_SINCE(5, 14) /* This is the id passed to qmlAttachedPropertiesById(). This is different from the index for the case that a single class is registered under two or more names (eg. Item in @@ -718,13 +661,14 @@ int QQmlType::attachedPropertiesId(QQmlEnginePrivate *engine) const if (!d) return -1; if (d->regType == CppType) - return d->extraData.cd->attachedPropertiesId; + return d->extraData.cd->attachedPropertiesType ? d->index : -1; QQmlType base; if (d->regType == CompositeType) base = resolveCompositeBaseType(engine); return base.attachedPropertiesId(engine); } +#endif int QQmlType::parserStatusCast() const { diff --git a/src/qml/qml/qqmltype_p.h b/src/qml/qml/qqmltype_p.h index 0e59b1be06..1d65a08c8f 100644 --- a/src/qml/qml/qqmltype_p.h +++ b/src/qml/qml/qqmltype_p.h @@ -118,6 +118,8 @@ public: bool isInterface() const; bool isComposite() const; bool isCompositeSingleton() const; + bool isQObjectSingleton() const; + bool isQJSValueSingleton() const; int typeId() const; int qListTypeId() const; @@ -129,7 +131,9 @@ public: QQmlAttachedPropertiesFunc attachedPropertiesFunction(QQmlEnginePrivate *engine) const; const QMetaObject *attachedPropertiesType(QQmlEnginePrivate *engine) const; - int attachedPropertiesId(QQmlEnginePrivate *engine) const; +#if QT_DEPRECATED_SINCE(5, 14) + QT_DEPRECATED int attachedPropertiesId(QQmlEnginePrivate *engine) const; +#endif int parserStatusCast() const; const char *interfaceIId() const; @@ -138,28 +142,13 @@ public: int index() const; - class Q_QML_PRIVATE_EXPORT SingletonInstanceInfo + struct Q_QML_PRIVATE_EXPORT SingletonInstanceInfo { - public: - SingletonInstanceInfo() - : scriptCallback(nullptr), qobjectCallback(nullptr), instanceMetaObject(nullptr) {} - - QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *); - QObject *(*qobjectCallback)(QQmlEngine *, QJSEngine *); - const QMetaObject *instanceMetaObject; + QJSValue (*scriptCallback)(QQmlEngine *, QJSEngine *) = nullptr; + QObject *(*qobjectCallback)(QQmlEngine *, QJSEngine *) = nullptr; + const QMetaObject *instanceMetaObject = nullptr; QString typeName; QUrl url; // used by composite singletons - - void setQObjectApi(QQmlEngine *, QObject *); - QObject *qobjectApi(QQmlEngine *) const; - void setScriptApi(QQmlEngine *, const QJSValue &); - QJSValue scriptApi(QQmlEngine *) const; - - void init(QQmlEngine *); - void destroy(QQmlEngine *); - - QHash<QQmlEngine *, QJSValue> scriptApis; - QHash<QQmlEngine *, QObject *> qobjectApis; }; SingletonInstanceInfo *singletonInstanceInfo() const; diff --git a/src/qml/qml/qqmltype_p_p.h b/src/qml/qml/qqmltype_p_p.h index b317aff740..d381e11df4 100644 --- a/src/qml/qml/qqmltype_p_p.h +++ b/src/qml/qml/qqmltype_p_p.h @@ -83,7 +83,6 @@ public: QQmlCustomParser *customParser; QQmlAttachedPropertiesFunc attachedPropertiesFunc; const QMetaObject *attachedPropertiesType; - int attachedPropertiesId; int propertyValueSourceCast; int propertyValueInterceptorCast; bool registerEnumClassesUnscoped; diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 86513b5ff8..236daac75c 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -89,25 +89,21 @@ QObject* QQmlTypeWrapper::singletonObject() const if (!isSingleton()) return nullptr; - QQmlEngine *e = engine()->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo(); - siinfo->init(e); - return siinfo->qobjectApi(e); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine()); + return e->singletonInstance<QObject*>(d()->type()); } QVariant QQmlTypeWrapper::toVariant() const { - // Only Singleton type wrappers can be converted to a variant. if (!isSingleton()) - return QVariant(); + return QVariant::fromValue<QObject *>(d()->object); - QQmlEngine *e = engine()->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = d()->type().singletonInstanceInfo(); - siinfo->init(e); - if (QObject *qobjectSingleton = siinfo->qobjectApi(e)) - return QVariant::fromValue<QObject*>(qobjectSingleton); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine()->qmlEngine()); + const QQmlType type = d()->type(); + if (type.isQJSValueSingleton()) + return QVariant::fromValue<QJSValue>(e->singletonInstance<QJSValue>(type)); - return QVariant::fromValue<QJSValue>(siinfo->scriptApi(e)); + return QVariant::fromValue<QObject*>(e->singletonInstance<QObject*>(type)); } @@ -195,50 +191,51 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons // singleton types are handled differently to other types. if (type.isSingleton()) { - QQmlEngine *e = v4->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) { - - // check for enum value - const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; - if (includeEnums && name->startsWithUpper()) { - bool ok = false; - int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok); - if (ok) - return QV4::Value::fromInt32(value).asReturnedValue(); - - value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); - if (ok) { - Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); - enumWrapper->d()->typePrivate = type.priv(); - QQmlType::refHandle(enumWrapper->d()->typePrivate); - enumWrapper->d()->scopeEnumIndex = value; - return enumWrapper.asReturnedValue(); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(v4->qmlEngine()); + QJSValue scriptSingleton; + if (type.isQObjectSingleton() || type.isCompositeSingleton()) { + if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { + // check for enum value + const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + if (includeEnums && name->startsWithUpper()) { + bool ok = false; + int value = enumForSingleton(v4, name, qobjectSingleton, type, &ok); + if (ok) + return QV4::Value::fromInt32(value).asReturnedValue(); + + value = type.scopedEnumIndex(QQmlEnginePrivate::get(v4->qmlEngine()), name, &ok); + if (ok) { + Scoped<QQmlScopedEnumWrapper> enumWrapper(scope, v4->memoryManager->allocate<QQmlScopedEnumWrapper>()); + enumWrapper->d()->typePrivate = type.priv(); + QQmlType::refHandle(enumWrapper->d()->typePrivate); + enumWrapper->d()->scopeEnumIndex = value; + return enumWrapper.asReturnedValue(); + } } - } - // check for property. - bool ok; - const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok); - if (hasProperty) - *hasProperty = ok; - - // Warn when attempting to access a lowercased enum value, singleton case - if (!ok && includeEnums && !name->startsWithUpper()) { - enumForSingleton(v4, name, qobjectSingleton, type, &ok); - if (ok) - return throwLowercaseEnumError(v4, name, type); - } + // check for property. + bool ok; + const ReturnedValue result = QV4::QObjectWrapper::getQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, &ok); + if (hasProperty) + *hasProperty = ok; + + // Warn when attempting to access a lowercased enum value, singleton case + if (!ok && includeEnums && !name->startsWithUpper()) { + enumForSingleton(v4, name, qobjectSingleton, type, &ok); + if (ok) + return throwLowercaseEnumError(v4, name, type); + } - return result; - } else if (!siinfo->scriptApi(e).isUndefined()) { - // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. - QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); - if (!!o) - return o->get(name); + return result; + } + } else if (type.isQJSValueSingleton()) { + QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); + if (!scriptSingleton.isUndefined()) { + // NOTE: if used in a binding, changes will not trigger re-evaluation since non-NOTIFYable. + QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, scriptSingleton)); + if (!!o) + return o->get(name); + } } // Fall through to base implementation @@ -263,7 +260,9 @@ ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, cons // Fall through to base implementation } else if (w->d()->object) { - QObject *ao = qmlAttachedPropertiesObjectById(type.attachedPropertiesId(QQmlEnginePrivate::get(v4->qmlEngine())), object); + QObject *ao = qmlAttachedPropertiesObject( + object, + type.attachedPropertiesFunction(QQmlEnginePrivate::get(v4->qmlEngine()))); if (ao) return QV4::QObjectWrapper::getQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, hasProperty); @@ -335,26 +334,28 @@ bool QQmlTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, if (type.isValid() && !type.isSingleton() && w->d()->object) { QObject *object = w->d()->object; QQmlEngine *e = scope.engine->qmlEngine(); - QObject *ao = qmlAttachedPropertiesObjectById(type.attachedPropertiesId(QQmlEnginePrivate::get(e)), object); + QObject *ao = qmlAttachedPropertiesObject( + object, type.attachedPropertiesFunction(QQmlEnginePrivate::get(e))); if (ao) return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); return false; } else if (type.isSingleton()) { - QQmlEngine *e = scope.engine->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) { - return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); - } else if (!siinfo->scriptApi(e).isUndefined()) { - QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, siinfo->scriptApi(e))); - if (!apiprivate) { - QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); - scope.engine->throwError(error); - return false; - } else { - return apiprivate->put(name, value); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(scope.engine->qmlEngine()); + if (type.isQObjectSingleton() || type.isCompositeSingleton()) { + if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) + return QV4::QObjectWrapper::setQmlProperty(scope.engine, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); + + } else { + QJSValue scriptSingleton = e->singletonInstance<QJSValue>(type); + if (!scriptSingleton.isUndefined()) { + QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(scope.engine, scriptSingleton)); + if (!apiprivate) { + QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); + scope.engine->throwError(error); + return false; + } else { + return apiprivate->put(name, value); + } } } } @@ -446,27 +447,25 @@ ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, if (type.isValid()) { if (type.isSingleton()) { - QQmlEngine *e = engine->qmlEngine(); - QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); - siinfo->init(e); - - QObject *qobjectSingleton = siinfo->qobjectApi(e); - if (qobjectSingleton) { - - const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; - if (!includeEnums || !name->startsWithUpper()) { - QQmlData *ddata = QQmlData::get(qobjectSingleton, false); - if (ddata && ddata->propertyCache) { - ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); - QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); - if (property) { - lookup->qobjectLookup.ic = This->internalClass(); - lookup->qobjectLookup.staticQObject = static_cast<Heap::QObjectWrapper *>(val->heapObject()); - lookup->qobjectLookup.propertyCache = ddata->propertyCache; - lookup->qobjectLookup.propertyCache->addref(); - lookup->qobjectLookup.propertyData = property; - lookup->getter = QV4::QObjectWrapper::lookupGetter; - return lookup->getter(lookup, engine, *This); + QQmlEnginePrivate *e = QQmlEnginePrivate::get(engine->qmlEngine()); + if (type.isQObjectSingleton() || type.isCompositeSingleton()) { + if (QObject *qobjectSingleton = e->singletonInstance<QObject*>(type)) { + const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + if (!includeEnums || !name->startsWithUpper()) { + QQmlData *ddata = QQmlData::get(qobjectSingleton, false); + if (ddata && ddata->propertyCache) { + ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); + QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); + if (property) { + lookup->qobjectLookup.ic = This->internalClass(); + lookup->qobjectLookup.staticQObject = static_cast<Heap::QObjectWrapper *>(val->heapObject()); + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = property; + lookup->getter = QV4::QObjectWrapper::lookupGetter; + return lookup->getter(lookup, engine, *This); + } + // Fall through to base implementation } // Fall through to base implementation } diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h index 090b830b3c..a111f72e81 100644 --- a/src/qml/qtqmlglobal.h +++ b/src/qml/qtqmlglobal.h @@ -53,7 +53,7 @@ #else # define QT_FEATURE_qml_debug -1 # define QT_FEATURE_qml_sequence_object 1 -# define QT_FEATURE_qml_tracing -1 +# define QT_FEATURE_qml_jit -1 #endif QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qqmldelegatecomponent.cpp b/src/qml/types/qqmldelegatecomponent.cpp deleted file mode 100644 index 470f6cab6a..0000000000 --- a/src/qml/types/qqmldelegatecomponent.cpp +++ /dev/null @@ -1,300 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmldelegatecomponent_p.h" -#include <QtQml/private/qqmladaptormodel_p.h> - -QT_BEGIN_NAMESPACE - -QQmlAbstractDelegateComponent::QQmlAbstractDelegateComponent(QObject *parent) - : QQmlComponent(parent) -{ -} - -QQmlAbstractDelegateComponent::~QQmlAbstractDelegateComponent() -{ -} - -QVariant QQmlAbstractDelegateComponent::value(QQmlAdaptorModel *adaptorModel, int row, int column, const QString &role) const -{ - if (!adaptorModel) - return QVariant(); - return adaptorModel->value(adaptorModel->indexAt(row, column), role); -} - -/*! - \qmlmodule Qt.labs.qmlmodels 1.0 - \title Qt Labs QML Models - QML Types - \ingroup qmlmodules - \brief The Qt Labs QML Models module provides various model-related types for use with views. - - To use this module, import the module with the following line: - - \qml - import Qt.labs.qmlmodels 1.0 - \endqml -*/ - -/*! - \qmltype DelegateChoice - \instantiates QQmlDelegateChoice - \inqmlmodule Qt.labs.qmlmodels - \brief Encapsulates a delegate and when to use it. - - The DelegateChoice type wraps a delegate and defines the circumstances - in which it should be chosen. - - DelegateChoices can be nested inside a DelegateChooser. - - \sa DelegateChooser -*/ - -/*! - \qmlproperty string QtQml.Models::DelegateChoice::roleValue - This property holds the value used to match the role data for the role provided by \l DelegateChooser::role. -*/ -QVariant QQmlDelegateChoice::roleValue() const -{ - return m_value; -} - -void QQmlDelegateChoice::setRoleValue(const QVariant &value) -{ - if (m_value == value) - return; - m_value = value; - emit roleValueChanged(); - emit changed(); -} - -/*! - \qmlproperty index QtQml.Models::DelegateChoice::row - This property holds the value used to match the row value of model elements. - With models that have only the index property (and thus only one column), this property - should be intended as an index, and set to the desired index value. - - \note Setting both row and index has undefined behavior. The two are equivalent and only - one should be used. - - \sa index -*/ - -/*! - \qmlproperty index QtQml.Models::DelegateChoice::index - This property holds the value used to match the index value of model elements. - This is effectively an alias for \l row. - - \sa row -*/ -int QQmlDelegateChoice::row() const -{ - return m_row; -} - -void QQmlDelegateChoice::setRow(int r) -{ - if (m_row == r) - return; - m_row = r; - emit rowChanged(); - emit indexChanged(); - emit changed(); -} - -/*! - \qmlproperty index QtQml.Models::DelegateChoice::column - This property holds the value used to match the column value of model elements. -*/ -int QQmlDelegateChoice::column() const -{ - return m_column; -} - -void QQmlDelegateChoice::setColumn(int c) -{ - if (m_column == c) - return; - m_column = c; - emit columnChanged(); - emit changed(); -} - -QQmlComponent *QQmlDelegateChoice::delegate() const -{ - return m_delegate; -} - -/*! - \qmlproperty Component QtQml.Models::DelegateChoice::delegate - This property holds the delegate to use if this choice matches the model item. -*/ -void QQmlDelegateChoice::setDelegate(QQmlComponent *delegate) -{ - if (m_delegate == delegate) - return; - QQmlAbstractDelegateComponent *adc = static_cast<QQmlAbstractDelegateComponent *>(m_delegate); - if (adc) - disconnect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged); - m_delegate = delegate; - adc = static_cast<QQmlAbstractDelegateComponent *>(delegate); - if (adc) - connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, this, &QQmlDelegateChoice::delegateChanged); - emit delegateChanged(); - emit changed(); -} - -bool QQmlDelegateChoice::match(int row, int column, const QVariant &value) const -{ - if (!m_value.isValid() && m_row < 0 && m_column < 0) - return true; - - const bool roleMatched = (m_value.isValid()) ? value == m_value : true; - const bool rowMatched = (m_row < 0 ) ? true : m_row == row; - const bool columnMatched = (m_column < 0 ) ? true : m_column == column; - return roleMatched && rowMatched && columnMatched; -} - -/*! - \qmltype DelegateChooser - \instantiates QQmlDelegateChooser - \inqmlmodule Qt.labs.qmlmodels - \brief Allows a view to use different delegates for different types of items in the model. - - The DelegateChooser is a special \l Component type intended for those scenarios where a Component is required - by a view and used as a delegate. - DelegateChooser encapsulates a set of \l {DelegateChoice}s. - These choices are used determine the delegate that will be instantiated for each - item in the model. - The selection of the choice is performed based on the value that a model item has for \l role, - and also based on index. - - \note This type is intended to transparently work only with TableView and any DelegateModel-based view. - Views (including user-defined views) that aren't internally based on a DelegateModel need to explicitly support - this type of component to make it function as described. - - \sa DelegateChoice -*/ - -/*! - \qmlproperty string QtQml.Models::DelegateChooser::role - This property holds the role used to determine the delegate for a given model item. - - \sa DelegateChoice -*/ -void QQmlDelegateChooser::setRole(const QString &role) -{ - if (m_role == role) - return; - m_role = role; - emit roleChanged(); -} - -/*! - \qmlproperty list<DelegateChoice> QtQml.Models::DelegateChooser::choices - \default - - The list of DelegateChoices for the chooser. - - The list is treated as an ordered list, where the first DelegateChoice to match - will be used be a view. - - It should not generally be necessary to refer to the \c choices property, - as it is the default property for DelegateChooser and thus all child items are - automatically assigned to this property. -*/ - -QQmlListProperty<QQmlDelegateChoice> QQmlDelegateChooser::choices() -{ - return QQmlListProperty<QQmlDelegateChoice>(this, nullptr, - QQmlDelegateChooser::choices_append, - QQmlDelegateChooser::choices_count, - QQmlDelegateChooser::choices_at, - QQmlDelegateChooser::choices_clear); -} - -void QQmlDelegateChooser::choices_append(QQmlListProperty<QQmlDelegateChoice> *prop, QQmlDelegateChoice *choice) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object); - q->m_choices.append(choice); - connect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged); - q->delegateChanged(); -} - -int QQmlDelegateChooser::choices_count(QQmlListProperty<QQmlDelegateChoice> *prop) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object); - return q->m_choices.count(); -} - -QQmlDelegateChoice *QQmlDelegateChooser::choices_at(QQmlListProperty<QQmlDelegateChoice> *prop, int index) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser*>(prop->object); - return q->m_choices.at(index); -} - -void QQmlDelegateChooser::choices_clear(QQmlListProperty<QQmlDelegateChoice> *prop) -{ - QQmlDelegateChooser *q = static_cast<QQmlDelegateChooser *>(prop->object); - for (QQmlDelegateChoice *choice : q->m_choices) - disconnect(choice, &QQmlDelegateChoice::changed, q, &QQmlAbstractDelegateComponent::delegateChanged); - q->m_choices.clear(); - q->delegateChanged(); -} - -QQmlComponent *QQmlDelegateChooser::delegate(QQmlAdaptorModel *adaptorModel, int row, int column) const -{ - QVariant v; - if (!m_role.isNull()) - v = value(adaptorModel, row, column, m_role); - if (!v.isValid()) { // check if the row only has modelData, for example if the row is a QVariantMap - v = value(adaptorModel, row, column, QStringLiteral("modelData")); - if (v.isValid()) - v = v.toMap().value(m_role); - } - // loop through choices, finding first one that fits - for (int i = 0; i < m_choices.count(); ++i) { - const QQmlDelegateChoice *choice = m_choices.at(i); - if (choice->match(row, column, v)) - return choice->delegate(); - } - - return nullptr; -} - -QT_END_NAMESPACE diff --git a/src/qml/types/qqmldelegatecomponent_p.h b/src/qml/types/qqmldelegatecomponent_p.h deleted file mode 100644 index c925ed9a60..0000000000 --- a/src/qml/types/qqmldelegatecomponent_p.h +++ /dev/null @@ -1,155 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLDELEGATECOMPONENT_P_H -#define QQMLDELEGATECOMPONENT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtqmlglobal_p.h> -#include <qqmlcomponent.h> - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -// TODO: consider making QQmlAbstractDelegateComponent public API -class QQmlAbstractDelegateComponentPrivate; -class QQmlAdaptorModel; -class Q_QML_PRIVATE_EXPORT QQmlAbstractDelegateComponent : public QQmlComponent -{ - Q_OBJECT -public: - QQmlAbstractDelegateComponent(QObject *parent = nullptr); - ~QQmlAbstractDelegateComponent() override; - - virtual QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = 0) const = 0; - -signals: - void delegateChanged(); - -protected: - QVariant value(QQmlAdaptorModel *adaptorModel,int row, int column, const QString &role) const; - -private: - Q_DECLARE_PRIVATE(QQmlAbstractDelegateComponent) - Q_DISABLE_COPY(QQmlAbstractDelegateComponent) -}; - -class Q_QML_PRIVATE_EXPORT QQmlDelegateChoice : public QObject -{ - Q_OBJECT - Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged) - Q_PROPERTY(int row READ row WRITE setRow NOTIFY rowChanged) - Q_PROPERTY(int index READ row WRITE setRow NOTIFY indexChanged) - Q_PROPERTY(int column READ column WRITE setColumn NOTIFY columnChanged) - Q_PROPERTY(QQmlComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_CLASSINFO("DefaultProperty", "delegate") -public: - QVariant roleValue() const; - void setRoleValue(const QVariant &roleValue); - - int row() const; - void setRow(int r); - - int column() const; - void setColumn(int c); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *delegate); - - virtual bool match(int row, int column, const QVariant &value) const; - -signals: - void roleValueChanged(); - void rowChanged(); - void indexChanged(); - void columnChanged(); - void delegateChanged(); - void changed(); - -private: - QVariant m_value; - int m_row = -1; - int m_column = -1; - QQmlComponent *m_delegate = nullptr; -}; - -class Q_QML_PRIVATE_EXPORT QQmlDelegateChooser : public QQmlAbstractDelegateComponent -{ - Q_OBJECT - Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged) - Q_PROPERTY(QQmlListProperty<QQmlDelegateChoice> choices READ choices CONSTANT) - Q_CLASSINFO("DefaultProperty", "choices") - -public: - QString role() const { return m_role; } - void setRole(const QString &role); - - virtual QQmlListProperty<QQmlDelegateChoice> choices(); - static void choices_append(QQmlListProperty<QQmlDelegateChoice> *, QQmlDelegateChoice *); - static int choices_count(QQmlListProperty<QQmlDelegateChoice> *); - static QQmlDelegateChoice *choices_at(QQmlListProperty<QQmlDelegateChoice> *, int); - static void choices_clear(QQmlListProperty<QQmlDelegateChoice> *); - - QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = -1) const override; - -signals: - void roleChanged(); - -private: - QString m_role; - QList<QQmlDelegateChoice *> m_choices; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlDelegateChoice) -QML_DECLARE_TYPE(QQmlDelegateChooser) - -#endif // QQMLDELEGATECOMPONENT_P_H diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp deleted file mode 100644 index 0e57119368..0000000000 --- a/src/qml/types/qqmldelegatemodel.cpp +++ /dev/null @@ -1,3540 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmldelegatemodel_p_p.h" -#include "qqmldelegatecomponent_p.h" - -#include <QtQml/qqmlinfo.h> - -#include <private/qquickpackage_p.h> -#include <private/qmetaobjectbuilder_p.h> -#include <private/qqmladaptormodel_p.h> -#include <private/qqmlchangeset_p.h> -#include <private/qqmlengine_p.h> -#include <private/qqmlcomponent_p.h> -#include <private/qqmlincubator_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4functionobject_p.h> -#include <qv4objectiterator_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlDelegateModelItem; - -namespace QV4 { - -namespace Heap { - -struct DelegateModelGroupFunction : FunctionObject { - void init(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)); - - QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg); - uint flag; -}; - -struct QQmlDelegateModelGroupChange : Object { - void init() { Object::init(); } - - QQmlChangeSet::ChangeData change; -}; - -struct QQmlDelegateModelGroupChangeArray : Object { - void init(const QVector<QQmlChangeSet::Change> &changes); - void destroy() { - delete changes; - Object::destroy(); - } - - QVector<QQmlChangeSet::Change> *changes; -}; - - -} - -struct DelegateModelGroupFunction : QV4::FunctionObject -{ - V4_OBJECT2(DelegateModelGroupFunction, FunctionObject) - - static Heap::DelegateModelGroupFunction *create(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) - { - return scope->engine()->memoryManager->allocate<DelegateModelGroupFunction>(scope, flag, code); - } - - static ReturnedValue virtualCall(const QV4::FunctionObject *that, const Value *thisObject, const Value *argv, int argc) - { - QV4::Scope scope(that->engine()); - QV4::Scoped<DelegateModelGroupFunction> f(scope, static_cast<const DelegateModelGroupFunction *>(that)); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - QV4::ScopedValue v(scope, argc ? argv[0] : Value::undefinedValue()); - return f->d()->code(o->d()->item, f->d()->flag, v); - } -}; - -void Heap::DelegateModelGroupFunction::init(QV4::ExecutionContext *scope, uint flag, QV4::ReturnedValue (*code)(QQmlDelegateModelItem *item, uint flag, const QV4::Value &arg)) -{ - QV4::Heap::FunctionObject::init(scope, QStringLiteral("DelegateModelGroupFunction")); - this->flag = flag; - this->code = code; -} - -} - -DEFINE_OBJECT_VTABLE(QV4::DelegateModelGroupFunction); - - - -class QQmlDelegateModelEngineData : public QV8Engine::Deletable -{ -public: - QQmlDelegateModelEngineData(QV4::ExecutionEngine *v4); - ~QQmlDelegateModelEngineData(); - - QV4::ReturnedValue array(QV4::ExecutionEngine *engine, - const QVector<QQmlChangeSet::Change> &changes); - - QV4::PersistentValue changeProto; -}; - -V4_DEFINE_EXTENSION(QQmlDelegateModelEngineData, engineData) - - -void QQmlDelegateModelPartsMetaObject::propertyCreated(int, QMetaPropertyBuilder &prop) -{ - prop.setWritable(false); -} - -QVariant QQmlDelegateModelPartsMetaObject::initialValue(int id) -{ - QQmlDelegateModelParts *parts = static_cast<QQmlDelegateModelParts *>(object()); - QQmlPartsModel *m = new QQmlPartsModel( - parts->model, QString::fromUtf8(name(id)), parts); - parts->models.append(m); - return QVariant::fromValue(static_cast<QObject *>(m)); -} - -QQmlDelegateModelParts::QQmlDelegateModelParts(QQmlDelegateModel *parent) -: QObject(parent), model(parent) -{ - new QQmlDelegateModelPartsMetaObject(this); -} - -//--------------------------------------------------------------------------- - -/*! - \qmltype DelegateModel - \instantiates QQmlDelegateModel - \inqmlmodule QtQml.Models - \brief Encapsulates a model and delegate. - - The DelegateModel type encapsulates a model and the delegate that will - be instantiated for items in the model. - - It is usually not necessary to create a DelegateModel. - However, it can be useful for manipulating and accessing the \l modelIndex - when a QAbstractItemModel subclass is used as the - model. Also, DelegateModel is used together with \l Package to - provide delegates to multiple views, and with DelegateModelGroup to sort and filter - delegate items. - - The example below illustrates using a DelegateModel with a ListView. - - \snippet delegatemodel/delegatemodel.qml 0 -*/ - -QQmlDelegateModelPrivate::QQmlDelegateModelPrivate(QQmlContext *ctxt) - : m_delegateChooser(nullptr) - , m_cacheMetaType(nullptr) - , m_context(ctxt) - , m_parts(nullptr) - , m_filterGroup(QStringLiteral("items")) - , m_count(0) - , m_groupCount(Compositor::MinimumGroupCount) - , m_compositorGroup(Compositor::Cache) - , m_complete(false) - , m_delegateValidated(false) - , m_reset(false) - , m_transaction(false) - , m_incubatorCleanupScheduled(false) - , m_waitingToFetchMore(false) - , m_cacheItems(nullptr) - , m_items(nullptr) - , m_persistedItems(nullptr) -{ -} - -QQmlDelegateModelPrivate::~QQmlDelegateModelPrivate() -{ - qDeleteAll(m_finishedIncubating); - - if (m_cacheMetaType) - m_cacheMetaType->release(); -} - -int QQmlDelegateModelPrivate::adaptorModelCount() const -{ - // QQmlDelegateModel currently only support list models. - // So even if a model is a table model, only the first - // column will be used. - return m_adaptorModel.rowCount(); -} - -void QQmlDelegateModelPrivate::requestMoreIfNecessary() -{ - Q_Q(QQmlDelegateModel); - if (!m_waitingToFetchMore && m_adaptorModel.canFetchMore()) { - m_waitingToFetchMore = true; - QCoreApplication::postEvent(q, new QEvent(QEvent::UpdateRequest)); - } -} - -void QQmlDelegateModelPrivate::init() -{ - Q_Q(QQmlDelegateModel); - m_compositor.setRemoveGroups(Compositor::GroupMask & ~Compositor::PersistedFlag); - - m_items = new QQmlDelegateModelGroup(QStringLiteral("items"), q, Compositor::Default, q); - m_items->setDefaultInclude(true); - m_persistedItems = new QQmlDelegateModelGroup(QStringLiteral("persistedItems"), q, Compositor::Persisted, q); - QQmlDelegateModelGroupPrivate::get(m_items)->emitters.insert(this); -} - -QQmlDelegateModel::QQmlDelegateModel() - : QQmlDelegateModel(nullptr, nullptr) -{ -} - -QQmlDelegateModel::QQmlDelegateModel(QQmlContext *ctxt, QObject *parent) -: QQmlInstanceModel(*(new QQmlDelegateModelPrivate(ctxt)), parent) -{ - Q_D(QQmlDelegateModel); - d->init(); -} - -QQmlDelegateModel::~QQmlDelegateModel() -{ - Q_D(QQmlDelegateModel); - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.setObject(nullptr, this); - - for (QQmlDelegateModelItem *cacheItem : qAsConst(d->m_cache)) { - if (cacheItem->object) { - delete cacheItem->object; - - cacheItem->object = nullptr; - cacheItem->contextData->invalidate(); - Q_ASSERT(cacheItem->contextData->refCount == 1); - cacheItem->contextData = nullptr; - cacheItem->scriptRef -= 1; - } - cacheItem->groups &= ~Compositor::UnresolvedFlag; - cacheItem->objectRef = 0; - if (!cacheItem->isReferenced()) - delete cacheItem; - else if (cacheItem->incubationTask) - cacheItem->incubationTask->vdm = nullptr; - } -} - - -void QQmlDelegateModel::classBegin() -{ - Q_D(QQmlDelegateModel); - if (!d->m_context) - d->m_context = qmlContext(this); -} - -void QQmlDelegateModel::componentComplete() -{ - Q_D(QQmlDelegateModel); - d->m_complete = true; - - int defaultGroups = 0; - QStringList groupNames; - groupNames.append(QStringLiteral("items")); - groupNames.append(QStringLiteral("persistedItems")); - if (QQmlDelegateModelGroupPrivate::get(d->m_items)->defaultInclude) - defaultGroups |= Compositor::DefaultFlag; - if (QQmlDelegateModelGroupPrivate::get(d->m_persistedItems)->defaultInclude) - defaultGroups |= Compositor::PersistedFlag; - for (int i = Compositor::MinimumGroupCount; i < d->m_groupCount; ++i) { - QString name = d->m_groups[i]->name(); - if (name.isEmpty()) { - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else if (name.at(0).isUpper()) { - qmlWarning(d->m_groups[i]) << QQmlDelegateModelGroup::tr("Group names must start with a lower case letter"); - d->m_groups[i] = d->m_groups[d->m_groupCount - 1]; - --d->m_groupCount; - --i; - } else { - groupNames.append(name); - - QQmlDelegateModelGroupPrivate *group = QQmlDelegateModelGroupPrivate::get(d->m_groups[i]); - group->setModel(this, Compositor::Group(i)); - if (group->defaultInclude) - defaultGroups |= (1 << i); - } - } - - d->m_cacheMetaType = new QQmlDelegateModelItemMetaType( - d->m_context->engine()->handle(), this, groupNames); - - d->m_compositor.setGroupCount(d->m_groupCount); - d->m_compositor.setDefaultGroups(defaultGroups); - d->updateFilterGroup(); - - while (!d->m_pendingParts.isEmpty()) - static_cast<QQmlPartsModel *>(d->m_pendingParts.first())->updateFilterGroup(); - - QVector<Compositor::Insert> inserts; - d->m_count = d->adaptorModelCount(); - d->m_compositor.append( - &d->m_adaptorModel, - 0, - d->m_count, - defaultGroups | Compositor::AppendFlag | Compositor::PrependFlag, - &inserts); - d->itemsInserted(inserts); - d->emitChanges(); - d->requestMoreIfNecessary(); -} - -/*! - \qmlproperty model QtQml.Models::DelegateModel::model - This property holds the model providing data for the DelegateModel. - - The model provides a set of data that is used to create the items - for a view. For large or dynamic datasets the model is usually - provided by a C++ model object. The C++ model object must be a \l - {QAbstractItemModel} subclass or a simple list. - - Models can also be created directly in QML, using a \l{ListModel} or - \l{QtQuick.XmlListModel::XmlListModel}{XmlListModel}. - - \sa {qml-data-models}{Data Models} - \keyword dm-model-property -*/ -QVariant QQmlDelegateModel::model() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.model(); -} - -void QQmlDelegateModelPrivate::connectToAbstractItemModel() -{ - Q_Q(QQmlDelegateModel); - if (!m_adaptorModel.adaptsAim()) - return; - - auto aim = m_adaptorModel.aim(); - - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsInserted(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsInserted(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - q, QQmlDelegateModel, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - q, QQmlDelegateModel, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - q, QQmlDelegateModel, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(modelReset()), - q, QQmlDelegateModel, SLOT(_q_modelReset())); - qmlobject_connect(aim, QAbstractItemModel, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - q, QQmlDelegateModel, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); -} - -void QQmlDelegateModelPrivate::disconnectFromAbstractItemModel() -{ - Q_Q(QQmlDelegateModel); - if (!m_adaptorModel.adaptsAim()) - return; - - auto aim = m_adaptorModel.aim(); - - QObject::disconnect(aim, SIGNAL(rowsInserted(QModelIndex,int,int)), - q, SLOT(_q_rowsInserted(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), - q, SLOT(_q_rowsAboutToBeRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(rowsRemoved(QModelIndex,int,int)), - q, SLOT(_q_rowsRemoved(QModelIndex,int,int))); - QObject::disconnect(aim, SIGNAL(dataChanged(QModelIndex,QModelIndex,QVector<int>)), - q, SLOT(_q_dataChanged(QModelIndex,QModelIndex,QVector<int>))); - QObject::disconnect(aim, SIGNAL(rowsMoved(QModelIndex,int,int,QModelIndex,int)), - q, SLOT(_q_rowsMoved(QModelIndex,int,int,QModelIndex,int))); - QObject::disconnect(aim, SIGNAL(modelReset()), - q, SLOT(_q_modelReset())); - QObject::disconnect(aim, SIGNAL(layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint)), - q, SLOT(_q_layoutChanged(QList<QPersistentModelIndex>,QAbstractItemModel::LayoutChangeHint))); -} - -void QQmlDelegateModel::setModel(const QVariant &model) -{ - Q_D(QQmlDelegateModel); - - if (d->m_complete) - _q_itemsRemoved(0, d->m_count); - - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.setModel(model, this, d->m_context->engine()); - d->connectToAbstractItemModel(); - - d->m_adaptorModel.replaceWatchedRoles(QList<QByteArray>(), d->m_watchedRoles); - for (int i = 0; d->m_parts && i < d->m_parts->models.count(); ++i) { - d->m_adaptorModel.replaceWatchedRoles( - QList<QByteArray>(), d->m_parts->models.at(i)->watchedRoles()); - } - - if (d->m_complete) { - _q_itemsInserted(0, d->adaptorModelCount()); - d->requestMoreIfNecessary(); - } -} - -/*! - \qmlproperty Component QtQml.Models::DelegateModel::delegate - - The delegate provides a template defining each item instantiated by a view. - The index is exposed as an accessible \c index property. Properties of the - model are also available depending upon the type of \l {qml-data-models}{Data Model}. -*/ -QQmlComponent *QQmlDelegateModel::delegate() const -{ - Q_D(const QQmlDelegateModel); - return d->m_delegate; -} - -void QQmlDelegateModel::setDelegate(QQmlComponent *delegate) -{ - Q_D(QQmlDelegateModel); - if (d->m_transaction) { - qmlWarning(this) << tr("The delegate of a DelegateModel cannot be changed within onUpdated."); - return; - } - if (d->m_delegate == delegate) - return; - bool wasValid = d->m_delegate != nullptr; - d->m_delegate.setObject(delegate, this); - d->m_delegateValidated = false; - if (d->m_delegateChooser) - QObject::disconnect(d->m_delegateChooserChanged); - - d->m_delegateChooser = nullptr; - if (delegate) { - QQmlAbstractDelegateComponent *adc = - qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - if (adc) { - d->m_delegateChooser = adc; - d->m_delegateChooserChanged = connect(adc, &QQmlAbstractDelegateComponent::delegateChanged, - [d](){ d->delegateChanged(); }); - } - } - d->delegateChanged(d->m_delegate, wasValid); -} - -/*! - \qmlproperty QModelIndex QtQml.Models::DelegateModel::rootIndex - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. \c rootIndex allows the children of - any node in a QAbstractItemModel to be provided by this model. - - This property only affects models of type QAbstractItemModel that - are hierarchical (e.g, a tree model). - - For example, here is a simple interactive file system browser. - When a directory name is clicked, the view's \c rootIndex is set to the - QModelIndex node of the clicked directory, thus updating the view to show - the new directory's contents. - - \c main.cpp: - \snippet delegatemodel/delegatemodel_rootindex/main.cpp 0 - - \c view.qml: - \snippet delegatemodel/delegatemodel_rootindex/view.qml 0 - - If the \l {dm-model-property}{model} is a QAbstractItemModel subclass, - the delegate can also reference a \c hasModelChildren property (optionally - qualified by a \e model. prefix) that indicates whether the delegate's - model item has any child nodes. - - \sa modelIndex(), parentModelIndex() -*/ -QVariant QQmlDelegateModel::rootIndex() const -{ - Q_D(const QQmlDelegateModel); - return QVariant::fromValue(QModelIndex(d->m_adaptorModel.rootIndex)); -} - -void QQmlDelegateModel::setRootIndex(const QVariant &root) -{ - Q_D(QQmlDelegateModel); - - QModelIndex modelIndex = qvariant_cast<QModelIndex>(root); - const bool changed = d->m_adaptorModel.rootIndex != modelIndex; - if (changed || !d->m_adaptorModel.isValid()) { - const int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = modelIndex; - if (!d->m_adaptorModel.isValid() && d->m_adaptorModel.aim()) { - // The previous root index was invalidated, so we need to reconnect the model. - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.setModel(d->m_adaptorModel.list.list(), this, d->m_context->engine()); - d->connectToAbstractItemModel(); - } - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - if (d->m_complete) { - const int newCount = d->adaptorModelCount(); - if (oldCount) - _q_itemsRemoved(0, oldCount); - if (newCount) - _q_itemsInserted(0, newCount); - } - if (changed) - emit rootIndexChanged(); - } -} - -/*! - \qmlmethod QModelIndex QtQml.Models::DelegateModel::modelIndex(int index) - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the specified index. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQmlDelegateModel::modelIndex(int idx) const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.modelIndex(idx); -} - -/*! - \qmlmethod QModelIndex QtQml.Models::DelegateModel::parentModelIndex() - - QAbstractItemModel provides a hierarchical tree of data, whereas - QML only operates on list data. This function assists in using - tree models in QML. - - Returns a QModelIndex for the parent of the current rootIndex. - This value can be assigned to rootIndex. - - \sa rootIndex -*/ -QVariant QQmlDelegateModel::parentModelIndex() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.parentModelIndex(); -} - -/*! - \qmlproperty int QtQml.Models::DelegateModel::count -*/ - -int QQmlDelegateModel::count() const -{ - Q_D(const QQmlDelegateModel); - if (!d->m_delegate) - return 0; - return d->m_compositor.count(d->m_compositorGroup); -} - -QQmlDelegateModel::ReleaseFlags QQmlDelegateModelPrivate::release(QObject *object) -{ - if (!object) - return QQmlDelegateModel::ReleaseFlags(0); - - QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(object); - if (!cacheItem) - return QQmlDelegateModel::ReleaseFlags(0); - - if (!cacheItem->releaseObject()) - return QQmlDelegateModel::Referenced; - - cacheItem->destroyObject(); - emitDestroyingItem(object); - if (cacheItem->incubationTask) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - } - cacheItem->Dispose(); - return QQmlInstanceModel::Destroyed; -} - -/* - Returns ReleaseStatus flags. -*/ - -QQmlDelegateModel::ReleaseFlags QQmlDelegateModel::release(QObject *item) -{ - Q_D(QQmlDelegateModel); - QQmlInstanceModel::ReleaseFlags stat = d->release(item); - return stat; -} - -// Cancel a requested async item -void QQmlDelegateModel::cancel(int index) -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "DelegateModel::cancel: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return; - } - - Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); - QQmlDelegateModelItem *cacheItem = it->inCache() ? d->m_cache.at(it.cacheIndex) : 0; - if (cacheItem) { - if (cacheItem->incubationTask && !cacheItem->isObjectReferenced()) { - d->releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - - if (cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - d->emitDestroyingPackage(package); - else - d->emitDestroyingItem(object); - } - - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - d->m_compositor.clearFlags(Compositor::Cache, it.cacheIndex, 1, Compositor::CacheFlag); - d->m_cache.removeAt(it.cacheIndex); - delete cacheItem; - Q_ASSERT(d->m_cache.count() == d->m_compositor.count(Compositor::Cache)); - } - } -} - -void QQmlDelegateModelPrivate::group_append( - QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group) -{ - QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data); - if (d->m_complete) - return; - if (d->m_groupCount == Compositor::MaximumGroupCount) { - qmlWarning(d->q_func()) << QQmlDelegateModel::tr("The maximum number of supported DelegateModelGroups is 8"); - return; - } - d->m_groups[d->m_groupCount] = group; - d->m_groupCount += 1; -} - -int QQmlDelegateModelPrivate::group_count( - QQmlListProperty<QQmlDelegateModelGroup> *property) -{ - QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data); - return d->m_groupCount - 1; -} - -QQmlDelegateModelGroup *QQmlDelegateModelPrivate::group_at( - QQmlListProperty<QQmlDelegateModelGroup> *property, int index) -{ - QQmlDelegateModelPrivate *d = static_cast<QQmlDelegateModelPrivate *>(property->data); - return index >= 0 && index < d->m_groupCount - 1 - ? d->m_groups[index + 1] - : nullptr; -} - -/*! - \qmlproperty list<DelegateModelGroup> QtQml.Models::DelegateModel::groups - - This property holds a delegate model's group definitions. - - Groups define a sub-set of the items in a delegate model and can be used to filter - a model. - - For every group defined in a DelegateModel two attached properties are added to each - delegate item. The first of the form DelegateModel.in\e{GroupName} holds whether the - item belongs to the group and the second DelegateModel.\e{groupName}Index holds the - index of the item in that group. - - The following example illustrates using groups to select items in a model. - - \snippet delegatemodel/delegatemodelgroup.qml 0 - \keyword dm-groups-property -*/ - -QQmlListProperty<QQmlDelegateModelGroup> QQmlDelegateModel::groups() -{ - Q_D(QQmlDelegateModel); - return QQmlListProperty<QQmlDelegateModelGroup>( - this, - d, - QQmlDelegateModelPrivate::group_append, - QQmlDelegateModelPrivate::group_count, - QQmlDelegateModelPrivate::group_at, - nullptr); -} - -/*! - \qmlproperty DelegateModelGroup QtQml.Models::DelegateModel::items - - This property holds default group to which all new items are added. -*/ - -QQmlDelegateModelGroup *QQmlDelegateModel::items() -{ - Q_D(QQmlDelegateModel); - return d->m_items; -} - -/*! - \qmlproperty DelegateModelGroup QtQml.Models::DelegateModel::persistedItems - - This property holds delegate model's persisted items group. - - Items in this group are not destroyed when released by a view, instead they are persisted - until removed from the group. - - An item can be removed from the persistedItems group by setting the - DelegateModel.inPersistedItems property to false. If the item is not referenced by a view - at that time it will be destroyed. Adding an item to this group will not create a new - instance. - - Items returned by the \l QtQml.Models::DelegateModelGroup::create() function are automatically added - to this group. -*/ - -QQmlDelegateModelGroup *QQmlDelegateModel::persistedItems() -{ - Q_D(QQmlDelegateModel); - return d->m_persistedItems; -} - -/*! - \qmlproperty string QtQml.Models::DelegateModel::filterOnGroup - - This property holds name of the group that is used to filter the delegate model. - - Only items that belong to this group are visible to a view. - - By default this is the \l items group. -*/ - -QString QQmlDelegateModel::filterGroup() const -{ - Q_D(const QQmlDelegateModel); - return d->m_filterGroup; -} - -void QQmlDelegateModel::setFilterGroup(const QString &group) -{ - Q_D(QQmlDelegateModel); - - if (d->m_transaction) { - qmlWarning(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); - return; - } - - if (d->m_filterGroup != group) { - d->m_filterGroup = group; - d->updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQmlDelegateModel::resetFilterGroup() -{ - setFilterGroup(QStringLiteral("items")); -} - -void QQmlDelegateModelPrivate::updateFilterGroup() -{ - Q_Q(QQmlDelegateModel); - if (!m_cacheMetaType) - return; - - QQmlListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - for (int i = 1; i < m_groupCount; ++i) { - if (m_filterGroup == m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQmlDelegateModelGroupPrivate::get(m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector<QQmlChangeSet::Change> removes; - QVector<QQmlChangeSet::Change> inserts; - m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQmlChangeSet changeSet; - changeSet.move(removes, inserts); - emit q->modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit q->countChanged(); - - if (m_parts) { - auto partsCopy = m_parts->models; // deliberate; this may alter m_parts - for (QQmlPartsModel *model : qAsConst(partsCopy)) - model->updateFilterGroup(m_compositorGroup, changeSet); - } - } -} - -/*! - \qmlproperty object QtQml.Models::DelegateModel::parts - - The \a parts property selects a DelegateModel which creates - delegates from the part named. This is used in conjunction with - the \l Package type. - - For example, the code below selects a model which creates - delegates named \e list from a \l Package: - - \code - DelegateModel { - id: visualModel - delegate: Package { - Item { Package.name: "list" } - } - model: myModel - } - - ListView { - width: 200; height:200 - model: visualModel.parts.list - } - \endcode - - \sa Package -*/ - -QObject *QQmlDelegateModel::parts() -{ - Q_D(QQmlDelegateModel); - if (!d->m_parts) - d->m_parts = new QQmlDelegateModelParts(this); - return d->m_parts; -} - -const QAbstractItemModel *QQmlDelegateModel::abstractItemModel() const -{ - Q_D(const QQmlDelegateModel); - return d->m_adaptorModel.adaptsAim() ? d->m_adaptorModel.aim() : nullptr; -} - -void QQmlDelegateModelPrivate::emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->createdPackage(incubationTask->index[i], package); -} - -void QQmlDelegateModelPrivate::emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->initPackage(incubationTask->index[i], package); -} - -void QQmlDelegateModelPrivate::emitDestroyingPackage(QQuickPackage *package) -{ - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->destroyingPackage(package); -} - -static bool isDoneIncubating(QQmlIncubator::Status status) -{ - return status == QQmlIncubator::Ready || status == QQmlIncubator::Error; -} - -void QQDMIncubationTask::statusChanged(Status status) -{ - if (vdm) { - vdm->incubatorStatusChanged(this, status); - } else if (isDoneIncubating(status)) { - Q_ASSERT(incubating); - // The model was deleted from under our feet, cleanup ourselves - delete incubating->object; - incubating->object = nullptr; - if (incubating->contextData) { - incubating->contextData->invalidate(); - Q_ASSERT(incubating->contextData->refCount == 1); - incubating->contextData = nullptr; - } - incubating->scriptRef = 0; - incubating->deleteLater(); - } -} - -void QQmlDelegateModelPrivate::releaseIncubator(QQDMIncubationTask *incubationTask) -{ - Q_Q(QQmlDelegateModel); - if (!incubationTask->isError()) - incubationTask->clear(); - m_finishedIncubating.append(incubationTask); - if (!m_incubatorCleanupScheduled) { - m_incubatorCleanupScheduled = true; - QCoreApplication::postEvent(q, new QEvent(QEvent::User)); - } -} - -void QQmlDelegateModelPrivate::addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it) -{ - m_cache.insert(it.cacheIndex, item); - m_compositor.setFlags(it, 1, Compositor::CacheFlag); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); -} - -void QQmlDelegateModelPrivate::removeCacheItem(QQmlDelegateModelItem *cacheItem) -{ - int cidx = m_cache.lastIndexOf(cacheItem); - if (cidx >= 0) { - m_compositor.clearFlags(Compositor::Cache, cidx, 1, Compositor::CacheFlag); - m_cache.removeAt(cidx); - } - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); -} - -void QQmlDelegateModelPrivate::incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status) -{ - if (!isDoneIncubating(status)) - return; - - const QList<QQmlError> incubationTaskErrors = incubationTask->errors(); - - QQmlDelegateModelItem *cacheItem = incubationTask->incubating; - cacheItem->incubationTask = nullptr; - incubationTask->incubating = nullptr; - releaseIncubator(incubationTask); - - if (status == QQmlIncubator::Ready) { - cacheItem->referenceObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object)) - emitCreatedPackage(incubationTask, package); - else - emitCreatedItem(incubationTask, cacheItem->object); - cacheItem->releaseObject(); - } else if (status == QQmlIncubator::Error) { - qmlInfo(m_delegate, incubationTaskErrors + m_delegate->errors()) << "Cannot create delegate"; - } - - if (!cacheItem->isObjectReferenced()) { - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(cacheItem->object); - delete cacheItem->object; - cacheItem->object = nullptr; - cacheItem->scriptRef -= 1; - if (cacheItem->contextData) { - cacheItem->contextData->invalidate(); - Q_ASSERT(cacheItem->contextData->refCount == 1); - } - cacheItem->contextData = nullptr; - - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - } -} - -void QQDMIncubationTask::setInitialState(QObject *o) -{ - vdm->setInitialState(this, o); -} - -void QQmlDelegateModelPrivate::setInitialState(QQDMIncubationTask *incubationTask, QObject *o) -{ - QQmlDelegateModelItem *cacheItem = incubationTask->incubating; - cacheItem->object = o; - - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(cacheItem->object)) - emitInitPackage(incubationTask, package); - else - emitInitItem(incubationTask, cacheItem->object); -} - -QObject *QQmlDelegateModelPrivate::object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode) -{ - if (!m_delegate || index < 0 || index >= m_compositor.count(group)) { - qWarning() << "DelegateModel::item: index out range" << index << m_compositor.count(group); - return nullptr; - } else if (!m_context || !m_context->isValid()) { - return nullptr; - } - - Compositor::iterator it = m_compositor.find(group, index); - - QQmlDelegateModelItem *cacheItem = it->inCache() ? m_cache.at(it.cacheIndex) : 0; - - if (!cacheItem) { - cacheItem = m_adaptorModel.createItem(m_cacheMetaType, it.modelIndex()); - if (!cacheItem) - return nullptr; - - cacheItem->groups = it->flags; - addCacheItem(cacheItem, it); - } - - // Bump the reference counts temporarily so neither the content data or the delegate object - // are deleted if incubatorStatusChanged() is called synchronously. - cacheItem->scriptRef += 1; - cacheItem->referenceObject(); - - if (cacheItem->incubationTask) { - bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested); - if (sync && cacheItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) { - // previously requested async - now needed immediately - cacheItem->incubationTask->forceCompletion(); - } - } else if (!cacheItem->object) { - QQmlComponent *delegate = m_delegate; - if (m_delegateChooser) { - QQmlAbstractDelegateComponent *chooser = m_delegateChooser; - do { - delegate = chooser->delegate(&m_adaptorModel, index); - chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - } while (chooser); - if (!delegate) - return nullptr; - } - - QQmlContext *creationContext = delegate->creationContext(); - - cacheItem->scriptRef += 1; - - cacheItem->incubationTask = new QQDMIncubationTask(this, incubationMode); - cacheItem->incubationTask->incubating = cacheItem; - cacheItem->incubationTask->clear(); - - for (int i = 1; i < m_groupCount; ++i) - cacheItem->incubationTask->index[i] = it.index[i]; - - QQmlContextData *ctxt = new QQmlContextData; - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_context.data())); - ctxt->contextObject = cacheItem; - cacheItem->contextData = ctxt; - - if (m_adaptorModel.hasProxyObject()) { - if (QQmlAdaptorModelProxyInterface *proxy - = qobject_cast<QQmlAdaptorModelProxyInterface *>(cacheItem)) { - ctxt = new QQmlContextData; - ctxt->setParent(cacheItem->contextData, /*stronglyReferencedByParent*/true); - QObject *proxied = proxy->proxiedObject(); - ctxt->contextObject = proxied; - // We don't own the proxied object. We need to clear it if it goes away. - QObject::connect(proxied, &QObject::destroyed, - cacheItem, &QQmlDelegateModelItem::childContextObjectDestroyed); - } - } - - QQmlComponentPrivate *cp = QQmlComponentPrivate::get(delegate); - cp->incubateObject( - cacheItem->incubationTask, - delegate, - m_context->engine(), - ctxt, - QQmlContextData::get(m_context)); - } - - if (index == m_compositor.count(group) - 1) - requestMoreIfNecessary(); - - // Remove the temporary reference count. - cacheItem->scriptRef -= 1; - if (cacheItem->object && (!cacheItem->incubationTask || isDoneIncubating(cacheItem->incubationTask->status()))) - return cacheItem->object; - - cacheItem->releaseObject(); - if (!cacheItem->isReferenced()) { - removeCacheItem(cacheItem); - delete cacheItem; - } - - return nullptr; -} - -/* - If asynchronous is true or the component is being loaded asynchronously due - to an ancestor being loaded asynchronously, object() may return 0. In this - case createdItem() will be emitted when the object is available. The object - at this stage does not have any references, so object() must be called again - to ensure a reference is held. Any call to object() which returns a valid object - must be matched by a call to release() in order to destroy the object. -*/ -QObject *QQmlDelegateModel::object(int index, QQmlIncubator::IncubationMode incubationMode) -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate || index < 0 || index >= d->m_compositor.count(d->m_compositorGroup)) { - qWarning() << "DelegateModel::item: index out range" << index << d->m_compositor.count(d->m_compositorGroup); - return nullptr; - } - - return d->object(d->m_compositorGroup, index, incubationMode); -} - -QQmlIncubator::Status QQmlDelegateModel::incubationStatus(int index) -{ - Q_D(QQmlDelegateModel); - Compositor::iterator it = d->m_compositor.find(d->m_compositorGroup, index); - if (!it->inCache()) - return QQmlIncubator::Null; - - if (auto incubationTask = d->m_cache.at(it.cacheIndex)->incubationTask) - return incubationTask->status(); - - return QQmlIncubator::Ready; -} - -QVariant QQmlDelegateModelPrivate::variantValue(QQmlListCompositor::Group group, int index, const QString &name) -{ - Compositor::iterator it = m_compositor.find(group, index); - if (QQmlAdaptorModel *model = it.list<QQmlAdaptorModel>()) { - QString role = name; - int dot = name.indexOf(QLatin1Char('.')); - if (dot > 0) - role = name.left(dot); - QVariant value = model->value(it.modelIndex(), role); - while (dot > 0) { - QObject *obj = qvariant_cast<QObject*>(value); - if (!obj) - return QVariant(); - const int from = dot + 1; - dot = name.indexOf(QLatin1Char('.'), from); - value = obj->property(name.midRef(from, dot - from).toUtf8()); - } - return value; - } - return QVariant(); -} - -QVariant QQmlDelegateModel::variantValue(int index, const QString &role) -{ - Q_D(QQmlDelegateModel); - return d->variantValue(d->m_compositorGroup, index, role); -} - -int QQmlDelegateModel::indexOf(QObject *item, QObject *) const -{ - Q_D(const QQmlDelegateModel); - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(item)) - return cacheItem->groupIndex(d->m_compositorGroup); - return -1; -} - -void QQmlDelegateModel::setWatchedRoles(const QList<QByteArray> &roles) -{ - Q_D(QQmlDelegateModel); - d->m_adaptorModel.replaceWatchedRoles(d->m_watchedRoles, roles); - d->m_watchedRoles = roles; -} - -void QQmlDelegateModelPrivate::addGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector<Compositor::Insert> inserts; - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - emitChanges(); -} - -void QQmlDelegateModelPrivate::removeGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector<Compositor::Remove> removes; - m_compositor.clearFlags(from, count, group, groupFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -void QQmlDelegateModelPrivate::setGroups( - Compositor::iterator from, int count, Compositor::Group group, int groupFlags) -{ - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - - m_compositor.setFlags(from, count, group, groupFlags, &inserts); - itemsInserted(inserts); - const int removeFlags = ~groupFlags & Compositor::GroupMask; - - from = m_compositor.find(from.group, from.index[from.group]); - m_compositor.clearFlags(from, count, group, removeFlags, &removes); - itemsRemoved(removes); - emitChanges(); -} - -bool QQmlDelegateModel::event(QEvent *e) -{ - Q_D(QQmlDelegateModel); - if (e->type() == QEvent::UpdateRequest) { - d->m_waitingToFetchMore = false; - d->m_adaptorModel.fetchMore(); - } else if (e->type() == QEvent::User) { - d->m_incubatorCleanupScheduled = false; - qDeleteAll(d->m_finishedIncubating); - d->m_finishedIncubating.clear(); - } - return QQmlInstanceModel::event(e); -} - -void QQmlDelegateModelPrivate::itemsChanged(const QVector<Compositor::Change> &changes) -{ - if (!m_delegate) - return; - - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedChanges(m_groupCount); - - for (const Compositor::Change &change : changes) { - for (int i = 1; i < m_groupCount; ++i) { - if (change.inGroup(i)) { - translatedChanges[i].append(QQmlChangeSet::Change(change.index[i], change.count)); - } - } - } - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.change(translatedChanges.at(i)); -} - -void QQmlDelegateModel::_q_itemsChanged(int index, int count, const QVector<int> &roles) -{ - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - if (d->m_adaptorModel.notify(d->m_cache, index, count, roles)) { - QVector<Compositor::Change> changes; - d->m_compositor.listItemsChanged(&d->m_adaptorModel, index, count, &changes); - d->itemsChanged(changes); - d->emitChanges(); - } -} - -static void incrementIndexes(QQmlDelegateModelItem *cacheItem, int count, const int *deltas) -{ - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < count; ++i) - incubationTask->index[i] += deltas[i]; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < qMin<int>(count, Compositor::MaximumGroupCount); ++i) - attached->m_currentIndex[i] += deltas[i]; - } -} - -void QQmlDelegateModelPrivate::itemsInserted( - const QVector<Compositor::Insert> &inserts, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedInserts, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems) -{ - int cacheIndex = 0; - - int inserted[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - inserted[i] = 0; - - for (const Compositor::Insert &insert : inserts) { - for (; cacheIndex < insert.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, inserted); - - for (int i = 1; i < m_groupCount; ++i) { - if (insert.inGroup(i)) { - (*translatedInserts)[i].append( - QQmlChangeSet::Change(insert.index[i], insert.count, insert.moveId)); - inserted[i] += insert.count; - } - } - - if (!insert.inCache()) - continue; - - if (movedItems && insert.isMove()) { - QList<QQmlDelegateModelItem *> items = movedItems->take(insert.moveId); - Q_ASSERT(items.count() == insert.count); - m_cache = m_cache.mid(0, insert.cacheIndex) + items + m_cache.mid(insert.cacheIndex); - } - if (insert.inGroup()) { - for (int offset = 0; cacheIndex < insert.cacheIndex + insert.count; ++cacheIndex, ++offset) { - QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); - cacheItem->groups |= insert.flags & Compositor::GroupMask; - - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = cacheItem->groups & (1 << i) - ? insert.index[i] + offset - : insert.index[i]; - } - } - } else { - cacheIndex = insert.cacheIndex + insert.count; - } - } - for (const QList<QQmlDelegateModelItem *> cache = m_cache; cacheIndex < cache.count(); ++cacheIndex) - incrementIndexes(cache.at(cacheIndex), m_groupCount, inserted); -} - -void QQmlDelegateModelPrivate::itemsInserted(const QVector<Compositor::Insert> &inserts) -{ - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert(translatedInserts.at(i)); -} - -void QQmlDelegateModel::_q_itemsInserted(int index, int count) -{ - - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - d->m_count += count; - - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - if (item->modelIndex() >= index) { - const int newIndex = item->modelIndex() + count; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } - } - - QVector<Compositor::Insert> inserts; - d->m_compositor.listItemsInserted(&d->m_adaptorModel, index, count, &inserts); - d->itemsInserted(inserts); - d->emitChanges(); -} - -//### This method should be split in two. It will remove delegates, and it will re-render the list. -// When e.g. QQmlListModel::remove is called, the removal of the delegates should be done on -// QAbstractItemModel::rowsAboutToBeRemoved, and the re-rendering on -// QAbstractItemModel::rowsRemoved. Currently both are done on the latter signal. The problem is -// that the destruction of an item will emit a changed signal that ends up at the delegate, which -// in turn will try to load the data from the model (which should have already freed it), resulting -// in a use-after-free. See QTBUG-59256. -void QQmlDelegateModelPrivate::itemsRemoved( - const QVector<Compositor::Remove> &removes, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedRemoves, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems) -{ - int cacheIndex = 0; - int removedCache = 0; - - int removed[Compositor::MaximumGroupCount]; - for (int i = 1; i < m_groupCount; ++i) - removed[i] = 0; - - for (const Compositor::Remove &remove : removes) { - for (; cacheIndex < remove.cacheIndex; ++cacheIndex) - incrementIndexes(m_cache.at(cacheIndex), m_groupCount, removed); - - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) { - (*translatedRemoves)[i].append( - QQmlChangeSet::Change(remove.index[i], remove.count, remove.moveId)); - removed[i] -= remove.count; - } - } - - if (!remove.inCache()) - continue; - - if (movedItems && remove.isMove()) { - movedItems->insert(remove.moveId, m_cache.mid(remove.cacheIndex, remove.count)); - QList<QQmlDelegateModelItem *>::iterator begin = m_cache.begin() + remove.cacheIndex; - QList<QQmlDelegateModelItem *>::iterator end = begin + remove.count; - m_cache.erase(begin, end); - } else { - for (; cacheIndex < remove.cacheIndex + remove.count - removedCache; ++cacheIndex) { - QQmlDelegateModelItem *cacheItem = m_cache.at(cacheIndex); - if (remove.inGroup(Compositor::Persisted) && cacheItem->objectRef == 0 && cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(object); - cacheItem->scriptRef -= 1; - } - if (!cacheItem->isReferenced()) { - m_compositor.clearFlags(Compositor::Cache, cacheIndex, 1, Compositor::CacheFlag); - m_cache.removeAt(cacheIndex); - delete cacheItem; - --cacheIndex; - ++removedCache; - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - } else if (remove.groups() == cacheItem->groups) { - cacheItem->groups = 0; - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - for (int i = 1; i < m_groupCount; ++i) - incubationTask->index[i] = -1; - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) - attached->m_currentIndex[i] = -1; - } - } else { - if (QQDMIncubationTask *incubationTask = cacheItem->incubationTask) { - if (!cacheItem->isObjectReferenced()) { - releaseIncubator(cacheItem->incubationTask); - cacheItem->incubationTask = nullptr; - if (cacheItem->object) { - QObject *object = cacheItem->object; - cacheItem->destroyObject(); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - emitDestroyingPackage(package); - else - emitDestroyingItem(object); - } - cacheItem->scriptRef -= 1; - } else { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - incubationTask->index[i] = remove.index[i]; - } - } - } - if (QQmlDelegateModelAttached *attached = cacheItem->attached) { - for (int i = 1; i < m_groupCount; ++i) { - if (remove.inGroup(i)) - attached->m_currentIndex[i] = remove.index[i]; - } - } - cacheItem->groups &= ~remove.flags; - } - } - } - } - - for (const QList<QQmlDelegateModelItem *> cache = m_cache; cacheIndex < cache.count(); ++cacheIndex) - incrementIndexes(cache.at(cacheIndex), m_groupCount, removed); -} - -void QQmlDelegateModelPrivate::itemsRemoved(const QVector<Compositor::Remove> &removes) -{ - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove(translatedRemoves.at(i)); -} - -void QQmlDelegateModel::_q_itemsRemoved(int index, int count) -{ - Q_D(QQmlDelegateModel); - if (count <= 0|| !d->m_complete) - return; - - d->m_count -= count; - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - // layout change triggered by removal of a previous item might have - // already invalidated this item in d->m_cache and deleted it - if (!d->m_cache.contains(item)) - continue; - - if (item->modelIndex() >= index + count) { - const int newIndex = item->modelIndex() - count; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } else if (item->modelIndex() >= index) { - item->setModelIndex(-1, -1, -1); - } - } - - QVector<Compositor::Remove> removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, index, count, &removes); - d->itemsRemoved(removes); - - d->emitChanges(); -} - -void QQmlDelegateModelPrivate::itemsMoved( - const QVector<Compositor::Remove> &removes, const QVector<Compositor::Insert> &inserts) -{ - QHash<int, QList<QQmlDelegateModelItem *> > movedItems; - - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedRemoves(m_groupCount); - itemsRemoved(removes, &translatedRemoves, &movedItems); - - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> translatedInserts(m_groupCount); - itemsInserted(inserts, &translatedInserts, &movedItems); - Q_ASSERT(m_cache.count() == m_compositor.count(Compositor::Cache)); - Q_ASSERT(movedItems.isEmpty()); - if (!m_delegate) - return; - - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.move( - translatedRemoves.at(i), - translatedInserts.at(i)); - } -} - -void QQmlDelegateModel::_q_itemsMoved(int from, int to, int count) -{ - Q_D(QQmlDelegateModel); - if (count <= 0 || !d->m_complete) - return; - - const int minimum = qMin(from, to); - const int maximum = qMax(from, to) + count; - const int difference = from > to ? count : -count; - - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - if (item->modelIndex() >= from && item->modelIndex() < from + count) { - const int newIndex = item->modelIndex() - from + to; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } else if (item->modelIndex() >= minimum && item->modelIndex() < maximum) { - const int newIndex = item->modelIndex() + difference; - const int row = newIndex; - const int column = 0; - item->setModelIndex(newIndex, row, column); - } - } - - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - d->m_compositor.listItemsMoved(&d->m_adaptorModel, from, to, count, &removes, &inserts); - d->itemsMoved(removes, inserts); - d->emitChanges(); -} - -void QQmlDelegateModelPrivate::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - Q_Q(QQmlDelegateModel); - emit q->modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQmlDelegateModelPrivate::delegateChanged(bool add, bool remove) -{ - Q_Q(QQmlDelegateModel); - if (!m_complete) - return; - - if (m_transaction) { - qmlWarning(q) << QQmlDelegateModel::tr("The delegates of a DelegateModel cannot be changed within onUpdated."); - return; - } - - if (remove) { - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.remove( - 0, m_compositor.count(Compositor::Group(i))); - } - } - if (add) { - for (int i = 1; i < m_groupCount; ++i) { - QQmlDelegateModelGroupPrivate::get(m_groups[i])->changeSet.insert( - 0, m_compositor.count(Compositor::Group(i))); - } - } - emitChanges(); -} - -void QQmlDelegateModelPrivate::emitChanges() -{ - if (m_transaction || !m_complete || !m_context || !m_context->isValid()) - return; - - m_transaction = true; - QV4::ExecutionEngine *engine = m_context->engine()->handle(); - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitChanges(engine); - m_transaction = false; - - const bool reset = m_reset; - m_reset = false; - for (int i = 1; i < m_groupCount; ++i) - QQmlDelegateModelGroupPrivate::get(m_groups[i])->emitModelUpdated(reset); - - auto cacheCopy = m_cache; // deliberate; emitChanges may alter m_cache - for (QQmlDelegateModelItem *cacheItem : qAsConst(cacheCopy)) { - if (cacheItem->attached) - cacheItem->attached->emitChanges(); - } -} - -void QQmlDelegateModel::_q_modelReset() -{ - Q_D(QQmlDelegateModel); - if (!d->m_delegate) - return; - - int oldCount = d->m_count; - d->m_adaptorModel.rootIndex = QModelIndex(); - - if (d->m_complete) { - d->m_count = d->adaptorModelCount(); - - const QList<QQmlDelegateModelItem *> cache = d->m_cache; - for (int i = 0, c = cache.count(); i < c; ++i) { - QQmlDelegateModelItem *item = cache.at(i); - if (item->modelIndex() != -1) - item->setModelIndex(-1, -1, -1); - } - - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - if (oldCount) - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - if (d->m_count) - d->m_compositor.listItemsInserted(&d->m_adaptorModel, 0, d->m_count, &inserts); - d->itemsMoved(removes, inserts); - d->m_reset = true; - - if (d->m_adaptorModel.canFetchMore()) - d->m_adaptorModel.fetchMore(); - - d->emitChanges(); - } - emit rootIndexChanged(); -} - -void QQmlDelegateModel::_q_rowsInserted(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsInserted(begin, end - begin + 1); -} - -void QQmlDelegateModel::_q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (!d->m_adaptorModel.rootIndex.isValid()) - return; - const QModelIndex index = d->m_adaptorModel.rootIndex; - if (index.parent() == parent && index.row() >= begin && index.row() <= end) { - const int oldCount = d->m_count; - d->m_count = 0; - d->disconnectFromAbstractItemModel(); - d->m_adaptorModel.invalidateModel(); - - if (d->m_complete && oldCount > 0) { - QVector<Compositor::Remove> removes; - d->m_compositor.listItemsRemoved(&d->m_adaptorModel, 0, oldCount, &removes); - d->itemsRemoved(removes); - d->emitChanges(); - } - } -} - -void QQmlDelegateModel::_q_rowsRemoved(const QModelIndex &parent, int begin, int end) -{ - Q_D(QQmlDelegateModel); - if (parent == d->m_adaptorModel.rootIndex) - _q_itemsRemoved(begin, end - begin + 1); -} - -void QQmlDelegateModel::_q_rowsMoved( - const QModelIndex &sourceParent, int sourceStart, int sourceEnd, - const QModelIndex &destinationParent, int destinationRow) -{ - Q_D(QQmlDelegateModel); - const int count = sourceEnd - sourceStart + 1; - if (destinationParent == d->m_adaptorModel.rootIndex && sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsMoved(sourceStart, sourceStart > destinationRow ? destinationRow : destinationRow - count, count); - } else if (sourceParent == d->m_adaptorModel.rootIndex) { - _q_itemsRemoved(sourceStart, count); - } else if (destinationParent == d->m_adaptorModel.rootIndex) { - _q_itemsInserted(destinationRow, count); - } -} - -void QQmlDelegateModel::_q_dataChanged(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles) -{ - Q_D(QQmlDelegateModel); - if (begin.parent() == d->m_adaptorModel.rootIndex) - _q_itemsChanged(begin.row(), end.row() - begin.row() + 1, roles); -} - -bool QQmlDelegateModel::isDescendantOf(const QPersistentModelIndex& desc, const QList< QPersistentModelIndex >& parents) const -{ - for (int i = 0, c = parents.count(); i < c; ++i) { - for (QPersistentModelIndex parent = desc; parent.isValid(); parent = parent.parent()) { - if (parent == parents[i]) - return true; - } - } - - return false; -} - -void QQmlDelegateModel::_q_layoutChanged(const QList<QPersistentModelIndex> &parents, QAbstractItemModel::LayoutChangeHint hint) -{ - Q_D(QQmlDelegateModel); - if (!d->m_complete) - return; - - if (hint == QAbstractItemModel::VerticalSortHint) { - if (!parents.isEmpty() && d->m_adaptorModel.rootIndex.isValid() && !isDescendantOf(d->m_adaptorModel.rootIndex, parents)) { - return; - } - - // mark all items as changed - _q_itemsChanged(0, d->m_count, QVector<int>()); - - } else if (hint == QAbstractItemModel::HorizontalSortHint) { - // Ignored - } else { - // We don't know what's going on, so reset the model - _q_modelReset(); - } -} - -QQmlDelegateModelAttached *QQmlDelegateModel::qmlAttachedProperties(QObject *obj) -{ - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(obj)) { - if (cacheItem->object == obj) { // Don't create attached item for child objects. - cacheItem->attached = new QQmlDelegateModelAttached(cacheItem, obj); - return cacheItem->attached; - } - } - return new QQmlDelegateModelAttached(obj); -} - -bool QQmlDelegateModelPrivate::insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups) -{ - if (!m_context || !m_context->isValid()) - return false; - - QQmlDelegateModelItem *cacheItem = m_adaptorModel.createItem(m_cacheMetaType, -1); - if (!cacheItem) - return false; - if (!object.isObject()) - return false; - - QV4::ExecutionEngine *v4 = object.as<QV4::Object>()->engine(); - QV4::Scope scope(v4); - QV4::ScopedObject o(scope, object); - if (!o) - return false; - - QV4::ObjectIterator it(scope, o, QV4::ObjectIterator::EnumerableOnly); - QV4::ScopedValue propertyName(scope); - QV4::ScopedValue v(scope); - while (1) { - propertyName = it.nextPropertyNameAsString(v); - if (propertyName->isNull()) - break; - cacheItem->setValue(propertyName->toQStringNoThrow(), scope.engine->toVariant(v, QVariant::Invalid)); - } - - cacheItem->groups = groups | Compositor::UnresolvedFlag | Compositor::CacheFlag; - - // Must be before the new object is inserted into the cache or its indexes will be adjusted too. - itemsInserted(QVector<Compositor::Insert>(1, Compositor::Insert(before, 1, cacheItem->groups & ~Compositor::CacheFlag))); - - before = m_compositor.insert(before, nullptr, 0, 1, cacheItem->groups); - m_cache.insert(before.cacheIndex, cacheItem); - - return true; -} - -//============================================================================ - -QQmlDelegateModelItemMetaType::QQmlDelegateModelItemMetaType( - QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames) - : model(model) - , groupCount(groupNames.count() + 1) - , v4Engine(engine) - , metaObject(nullptr) - , groupNames(groupNames) -{ -} - -QQmlDelegateModelItemMetaType::~QQmlDelegateModelItemMetaType() -{ - if (metaObject) - metaObject->release(); -} - -void QQmlDelegateModelItemMetaType::initializeMetaObject() -{ - QMetaObjectBuilder builder; - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder.setClassName(QQmlDelegateModelAttached::staticMetaObject.className()); - builder.setSuperClass(&QQmlDelegateModelAttached::staticMetaObject); - - int notifierId = 0; - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - QString propertyName = QLatin1String("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "bool", notifierId); - propertyBuilder.setWritable(true); - } - for (int i = 0; i < groupNames.count(); ++i, ++notifierId) { - const QString propertyName = groupNames.at(i) + QLatin1String("Index"); - builder.addSignal("__" + propertyName.toUtf8() + "Changed()"); - QMetaPropertyBuilder propertyBuilder = builder.addProperty( - propertyName.toUtf8(), "int", notifierId); - propertyBuilder.setWritable(true); - } - - metaObject = new QQmlDelegateModelAttachedMetaObject(this, builder.toMetaObject()); -} - -void QQmlDelegateModelItemMetaType::initializePrototype() -{ - QV4::Scope scope(v4Engine); - - QV4::ScopedObject proto(scope, v4Engine->newObject()); - proto->defineAccessorProperty(QStringLiteral("model"), QQmlDelegateModelItem::get_model, nullptr); - proto->defineAccessorProperty(QStringLiteral("groups"), QQmlDelegateModelItem::get_groups, QQmlDelegateModelItem::set_groups); - QV4::ScopedString s(scope); - QV4::ScopedProperty p(scope); - - s = v4Engine->newString(QStringLiteral("isUnresolved")); - QV4::ScopedFunctionObject f(scope); - QV4::ExecutionContext *global = scope.engine->rootContext(); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, 30, QQmlDelegateModelItem::get_member))); - p->setSetter(nullptr); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("inItems")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::set_member))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("inPersistedItems")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::set_member))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("itemsIndex")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Default, QQmlDelegateModelItem::get_index))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - s = v4Engine->newString(QStringLiteral("persistedItemsIndex")); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, QQmlListCompositor::Persisted, QQmlDelegateModelItem::get_index))); - p->setSetter(nullptr); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - - for (int i = 2; i < groupNames.count(); ++i) { - QString propertyName = QLatin1String("in") + groupNames.at(i); - propertyName.replace(2, 1, propertyName.at(2).toUpper()); - s = v4Engine->newString(propertyName); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_member))); - p->setSetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::set_member))); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - } - for (int i = 2; i < groupNames.count(); ++i) { - const QString propertyName = groupNames.at(i) + QLatin1String("Index"); - s = v4Engine->newString(propertyName); - p->setGetter((f = QV4::DelegateModelGroupFunction::create(global, i + 1, QQmlDelegateModelItem::get_index))); - p->setSetter(nullptr); - proto->insertMember(s, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable); - } - modelItemProto.set(v4Engine, proto); -} - -int QQmlDelegateModelItemMetaType::parseGroups(const QStringList &groups) const -{ - int groupFlags = 0; - for (const QString &groupName : groups) { - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - return groupFlags; -} - -int QQmlDelegateModelItemMetaType::parseGroups(const QV4::Value &groups) const -{ - int groupFlags = 0; - QV4::Scope scope(v4Engine); - - QV4::ScopedString s(scope, groups); - if (s) { - const QString groupName = s->toQString(); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - return groupFlags; - } - - QV4::ScopedArrayObject array(scope, groups); - if (array) { - QV4::ScopedValue v(scope); - uint arrayLength = array->getLength(); - for (uint i = 0; i < arrayLength; ++i) { - v = array->get(i); - const QString groupName = v->toQString(); - int index = groupNames.indexOf(groupName); - if (index != -1) - groupFlags |= 2 << index; - } - } - return groupFlags; -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_model(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return b->engine()->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - if (!o->d()->item->metaType->model) - RETURN_UNDEFINED(); - - return o->d()->item->get(); -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_groups(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - QStringList groups; - for (int i = 1; i < o->d()->item->metaType->groupCount; ++i) { - if (o->d()->item->groups & (1 << i)) - groups.append(o->d()->item->metaType->groupNames.at(i - 1)); - } - - return scope.engine->fromVariant(groups); -} - -QV4::ReturnedValue QQmlDelegateModelItem::set_groups(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - if (!argc) - THROW_TYPE_ERROR(); - - if (!o->d()->item->metaType->model) - RETURN_UNDEFINED(); - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(o->d()->item->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(argv[0]); - const int cacheIndex = model->m_cache.indexOf(o->d()->item); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); - return QV4::Encode::undefined(); -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &) -{ - return QV4::Encode(bool(thisItem->groups & (1 << flag))); -} - -QV4::ReturnedValue QQmlDelegateModelItem::set_member(QQmlDelegateModelItem *cacheItem, uint flag, const QV4::Value &arg) -{ - if (!cacheItem->metaType->model) - return QV4::Encode::undefined(); - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(cacheItem->metaType->model); - - bool member = arg.toBoolean(); - uint groupFlag = (1 << flag); - if (member == ((cacheItem->groups & groupFlag) != 0)) - return QV4::Encode::undefined(); - - const int cacheIndex = model->m_cache.indexOf(cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - if (member) - model->addGroups(it, 1, Compositor::Cache, groupFlag); - else - model->removeGroups(it, 1, Compositor::Cache, groupFlag); - return QV4::Encode::undefined(); -} - -QV4::ReturnedValue QQmlDelegateModelItem::get_index(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &) -{ - return QV4::Encode((int)thisItem->groupIndex(Compositor::Group(flag))); -} - -void QQmlDelegateModelItem::childContextObjectDestroyed(QObject *childContextObject) -{ - if (!contextData) - return; - - for (QQmlContextData *ctxt = contextData->childContexts; ctxt; ctxt = ctxt->nextChild) { - if (ctxt->contextObject == childContextObject) - ctxt->contextObject = nullptr; - } -} - - -//--------------------------------------------------------------------------- - -DEFINE_OBJECT_VTABLE(QQmlDelegateModelItemObject); - -void QV4::Heap::QQmlDelegateModelItemObject::destroy() -{ - item->Dispose(); - Object::destroy(); -} - - -QQmlDelegateModelItem::QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, - QQmlAdaptorModel::Accessors *accessor, - int modelIndex, int row, int column) - : v4(metaType->v4Engine) - , metaType(metaType) - , contextData(nullptr) - , object(nullptr) - , attached(nullptr) - , incubationTask(nullptr) - , delegate(nullptr) - , poolTime(0) - , objectRef(0) - , scriptRef(0) - , groups(0) - , index(modelIndex) - , row(row) - , column(column) -{ - metaType->addref(); - - if (accessor->propertyCache) { - // The property cache in the accessor is common for all the model - // items in the model it wraps. It describes available model roles, - // together with revisioned properties like row, column and index, all - // which should be available in the delegate. We assign this cache to the - // model item so that the QML engine can use the revision information - // when resolving the properties (rather than falling back to just - // inspecting the QObject in the model item directly). - QQmlData *qmldata = QQmlData::get(this, true); - if (qmldata->propertyCache) - qmldata->propertyCache->release(); - qmldata->propertyCache = accessor->propertyCache.data(); - qmldata->propertyCache->addref(); - } -} - -QQmlDelegateModelItem::~QQmlDelegateModelItem() -{ - Q_ASSERT(scriptRef == 0); - Q_ASSERT(objectRef == 0); - Q_ASSERT(!object); - - if (incubationTask) { - if (metaType->model) - QQmlDelegateModelPrivate::get(metaType->model)->releaseIncubator(incubationTask); - else - delete incubationTask; - } - - metaType->release(); - -} - -void QQmlDelegateModelItem::Dispose() -{ - --scriptRef; - if (isReferenced()) - return; - - if (metaType->model) { - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - model->removeCacheItem(this); - } - delete this; -} - -void QQmlDelegateModelItem::setModelIndex(int idx, int newRow, int newColumn) -{ - const int prevIndex = index; - const int prevRow = row; - const int prevColumn = column; - - index = idx; - row = newRow; - column = newColumn; - - if (idx != prevIndex) - emit modelIndexChanged(); - if (row != prevRow) - emit rowChanged(); - if (column != prevColumn) - emit columnChanged(); -} - -void QQmlDelegateModelItem::destroyObject() -{ - Q_ASSERT(object); - Q_ASSERT(contextData); - - QQmlData *data = QQmlData::get(object); - Q_ASSERT(data); - if (data->ownContext) { - data->ownContext->clearContext(); - if (data->ownContext->contextObject == object) - data->ownContext->contextObject = nullptr; - data->ownContext = nullptr; - data->context = nullptr; - } - object->deleteLater(); - - if (attached) { - attached->m_cacheItem = nullptr; - attached = nullptr; - } - - contextData->invalidate(); - contextData = nullptr; - object = nullptr; -} - -QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) -{ - QQmlData *d = QQmlData::get(object); - QQmlContextData *context = d ? d->context : nullptr; - for (context = context ? context->parent : nullptr; context; context = context->parent) { - if (QQmlDelegateModelItem *cacheItem = qobject_cast<QQmlDelegateModelItem *>( - context->contextObject)) { - return cacheItem; - } - } - return nullptr; -} - -int QQmlDelegateModelItem::groupIndex(Compositor::Group group) -{ - if (QQmlDelegateModelPrivate * const model = metaType->model - ? QQmlDelegateModelPrivate::get(metaType->model) - : nullptr) { - return model->m_compositor.find(Compositor::Cache, model->m_cache.indexOf(this)).index[group]; - } - return -1; -} - -//--------------------------------------------------------------------------- - -QQmlDelegateModelAttachedMetaObject::QQmlDelegateModelAttachedMetaObject( - QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject) - : metaType(metaType) - , metaObject(metaObject) - , memberPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount()) - , indexPropertyOffset(QQmlDelegateModelAttached::staticMetaObject.propertyCount() + metaType->groupNames.count()) -{ - // Don't reference count the meta-type here as that would create a circular reference. - // Instead we rely the fact that the meta-type's reference count can't reach 0 without first - // destroying all delegates with attached objects. - *static_cast<QMetaObject *>(this) = *metaObject; -} - -QQmlDelegateModelAttachedMetaObject::~QQmlDelegateModelAttachedMetaObject() -{ - ::free(metaObject); -} - -void QQmlDelegateModelAttachedMetaObject::objectDestroyed(QObject *) -{ - release(); -} - -int QQmlDelegateModelAttachedMetaObject::metaCall(QObject *object, QMetaObject::Call call, int _id, void **arguments) -{ - QQmlDelegateModelAttached *attached = static_cast<QQmlDelegateModelAttached *>(object); - if (call == QMetaObject::ReadProperty) { - if (_id >= indexPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - indexPropertyOffset + 1); - *static_cast<int *>(arguments[0]) = attached->m_currentIndex[group]; - return -1; - } else if (_id >= memberPropertyOffset) { - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - *static_cast<bool *>(arguments[0]) = attached->m_cacheItem->groups & (1 << group); - return -1; - } - } else if (call == QMetaObject::WriteProperty) { - if (_id >= memberPropertyOffset) { - if (!metaType->model) - return -1; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(metaType->model); - Compositor::Group group = Compositor::Group(_id - memberPropertyOffset + 1); - const int groupFlag = 1 << group; - const bool member = attached->m_cacheItem->groups & groupFlag; - if (member && !*static_cast<bool *>(arguments[0])) { - Compositor::iterator it = model->m_compositor.find( - group, attached->m_currentIndex[group]); - model->removeGroups(it, 1, group, groupFlag); - } else if (!member && *static_cast<bool *>(arguments[0])) { - for (int i = 1; i < metaType->groupCount; ++i) { - if (attached->m_cacheItem->groups & (1 << i)) { - Compositor::iterator it = model->m_compositor.find( - Compositor::Group(i), attached->m_currentIndex[i]); - model->addGroups(it, 1, Compositor::Group(i), groupFlag); - break; - } - } - } - return -1; - } - } - return attached->qt_metacall(call, _id, arguments); -} - -QQmlDelegateModelAttached::QQmlDelegateModelAttached(QObject *parent) - : m_cacheItem(nullptr) - , m_previousGroups(0) -{ - QQml_setParent_noEvent(this, parent); -} - -QQmlDelegateModelAttached::QQmlDelegateModelAttached( - QQmlDelegateModelItem *cacheItem, QObject *parent) - : m_cacheItem(cacheItem) - , m_previousGroups(cacheItem->groups) -{ - QQml_setParent_noEvent(this, parent); - resetCurrentIndex(); - // Let m_previousIndex be equal to m_currentIndex - std::copy(std::begin(m_currentIndex), std::end(m_currentIndex), std::begin(m_previousIndex)); - - if (!cacheItem->metaType->metaObject) - cacheItem->metaType->initializeMetaObject(); - - QObjectPrivate::get(this)->metaObject = cacheItem->metaType->metaObject; - cacheItem->metaType->metaObject->addref(); -} - -void QQmlDelegateModelAttached::resetCurrentIndex() -{ - if (QQDMIncubationTask *incubationTask = m_cacheItem->incubationTask) { - for (int i = 1; i < qMin<int>(m_cacheItem->metaType->groupCount, Compositor::MaximumGroupCount); ++i) - m_currentIndex[i] = incubationTask->index[i]; - } else { - QQmlDelegateModelPrivate * const model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); - Compositor::iterator it = model->m_compositor.find( - Compositor::Cache, model->m_cache.indexOf(m_cacheItem)); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) - m_currentIndex[i] = it.index[i]; - } -} - -/*! - \qmlattachedproperty model QtQml.Models::DelegateModel::model - - This attached property holds the data model this delegate instance belongs to. - - It is attached to each instance of the delegate. -*/ - -QQmlDelegateModel *QQmlDelegateModelAttached::model() const -{ - return m_cacheItem ? m_cacheItem->metaType->model : nullptr; -} - -/*! - \qmlattachedproperty stringlist QtQml.Models::DelegateModel::groups - - This attached property holds the name of DelegateModelGroups the item belongs to. - - It is attached to each instance of the delegate. -*/ - -QStringList QQmlDelegateModelAttached::groups() const -{ - QStringList groups; - - if (!m_cacheItem) - return groups; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_cacheItem->groups & (1 << i)) - groups.append(m_cacheItem->metaType->groupNames.at(i - 1)); - } - return groups; -} - -void QQmlDelegateModelAttached::setGroups(const QStringList &groups) -{ - if (!m_cacheItem) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_cacheItem->metaType->model); - - const int groupFlags = model->m_cacheMetaType->parseGroups(groups); - const int cacheIndex = model->m_cache.indexOf(m_cacheItem); - Compositor::iterator it = model->m_compositor.find(Compositor::Cache, cacheIndex); - model->setGroups(it, 1, Compositor::Cache, groupFlags); -} - -/*! - \qmlattachedproperty bool QtQml.Models::DelegateModel::isUnresolved - - This attached property indicates whether the visual item is bound to a data model index. - Returns true if the item is not bound to the model, and false if it is. - - An unresolved item can be bound to the data model using the DelegateModelGroup::resolve() - function. - - It is attached to each instance of the delegate. -*/ - -bool QQmlDelegateModelAttached::isUnresolved() const -{ - if (!m_cacheItem) - return false; - - return m_cacheItem->groups & Compositor::UnresolvedFlag; -} - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::inItems - - This attached property holds whether the item belongs to the default \l items DelegateModelGroup. - - Changing this property will add or remove the item from the items group. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::itemsIndex - - This attached property holds the index of the item in the default \l items DelegateModelGroup. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::inPersistedItems - - This attached property holds whether the item belongs to the \l persistedItems DelegateModelGroup. - - Changing this property will add or remove the item from the items group. Change with caution - as removing an item from the persistedItems group will destroy the current instance if it is - not referenced by a model. - - It is attached to each instance of the delegate. -*/ - -/*! - \qmlattachedproperty int QtQml.Models::DelegateModel::persistedItemsIndex - - This attached property holds the index of the item in the \l persistedItems DelegateModelGroup. - - It is attached to each instance of the delegate. -*/ - -void QQmlDelegateModelAttached::emitChanges() -{ - const int groupChanges = m_previousGroups ^ m_cacheItem->groups; - m_previousGroups = m_cacheItem->groups; - - int indexChanges = 0; - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i) { - if (m_previousIndex[i] != m_currentIndex[i]) { - m_previousIndex[i] = m_currentIndex[i]; - indexChanges |= (1 << i); - } - } - - int notifierId = 0; - const QMetaObject *meta = metaObject(); - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (groupChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, nullptr); - } - for (int i = 1; i < m_cacheItem->metaType->groupCount; ++i, ++notifierId) { - if (indexChanges & (1 << i)) - QMetaObject::activate(this, meta, notifierId, nullptr); - } - - if (groupChanges) - emit groupsChanged(); -} - -//============================================================================ - -void QQmlDelegateModelGroupPrivate::setModel(QQmlDelegateModel *m, Compositor::Group g) -{ - Q_ASSERT(!model); - model = m; - group = g; -} - -bool QQmlDelegateModelGroupPrivate::isChangedConnected() -{ - Q_Q(QQmlDelegateModelGroup); - IS_SIGNAL_CONNECTED(q, QQmlDelegateModelGroup, changed, (const QJSValue &,const QJSValue &)); -} - -void QQmlDelegateModelGroupPrivate::emitChanges(QV4::ExecutionEngine *v4) -{ - Q_Q(QQmlDelegateModelGroup); - if (isChangedConnected() && !changeSet.isEmpty()) { - emit q->changed(QJSValue(v4, engineData(v4)->array(v4, changeSet.removes())), - QJSValue(v4, engineData(v4)->array(v4, changeSet.inserts()))); - } - if (changeSet.difference() != 0) - emit q->countChanged(); -} - -void QQmlDelegateModelGroupPrivate::emitModelUpdated(bool reset) -{ - for (QQmlDelegateModelGroupEmitterList::iterator it = emitters.begin(); it != emitters.end(); ++it) - it->emitModelUpdated(changeSet, reset); - changeSet.clear(); -} - -typedef QQmlDelegateModelGroupEmitterList::iterator GroupEmitterListIt; - -void QQmlDelegateModelGroupPrivate::createdPackage(int index, QQuickPackage *package) -{ - for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it) - it->createdPackage(index, package); -} - -void QQmlDelegateModelGroupPrivate::initPackage(int index, QQuickPackage *package) -{ - for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it) - it->initPackage(index, package); -} - -void QQmlDelegateModelGroupPrivate::destroyingPackage(QQuickPackage *package) -{ - for (GroupEmitterListIt it = emitters.begin(), end = emitters.end(); it != end; ++it) - it->destroyingPackage(package); -} - -/*! - \qmltype DelegateModelGroup - \instantiates QQmlDelegateModelGroup - \inqmlmodule QtQml.Models - \ingroup qtquick-models - \brief Encapsulates a filtered set of visual data items. - - The DelegateModelGroup type provides a means to address the model data of a - DelegateModel's delegate items, as well as sort and filter these delegate - items. - - The initial set of instantiable delegate items in a DelegateModel is represented - by its \l {QtQml.Models::DelegateModel::items}{items} group, which normally directly reflects - the contents of the model assigned to DelegateModel::model. This set can be changed to - the contents of any other member of DelegateModel::groups by assigning the \l name of that - DelegateModelGroup to the DelegateModel::filterOnGroup property. - - The data of an item in a DelegateModelGroup can be accessed using the get() function, which returns - information about group membership and indexes as well as model data. In combination - with the move() function this can be used to implement view sorting, with remove() to filter - items out of a view, or with setGroups() and \l Package delegates to categorize items into - different views. Different groups can only be sorted independently if they are disjunct. Moving - an item in one group will also move it in all other groups it is a part of. - - Data from models can be supplemented by inserting data directly into a DelegateModelGroup - with the insert() function. This can be used to introduce mock items into a view, or - placeholder items that are later \l {resolve()}{resolved} to real model data when it becomes - available. - - Delegate items can also be instantiated directly from a DelegateModelGroup using the - create() function, making it possible to use DelegateModel without an accompanying view - type or to cherry-pick specific items that should be instantiated irregardless of whether - they're currently within a view's visible area. - - \sa {QML Dynamic View Ordering Tutorial} -*/ -QQmlDelegateModelGroup::QQmlDelegateModelGroup(QObject *parent) - : QObject(*new QQmlDelegateModelGroupPrivate, parent) -{ -} - -QQmlDelegateModelGroup::QQmlDelegateModelGroup( - const QString &name, QQmlDelegateModel *model, int index, QObject *parent) - : QQmlDelegateModelGroup(parent) -{ - Q_D(QQmlDelegateModelGroup); - d->name = name; - d->setModel(model, Compositor::Group(index)); -} - -QQmlDelegateModelGroup::~QQmlDelegateModelGroup() -{ -} - -/*! - \qmlproperty string QtQml.Models::DelegateModelGroup::name - - This property holds the name of the group. - - Each group in a model must have a unique name starting with a lower case letter. -*/ - -QString QQmlDelegateModelGroup::name() const -{ - Q_D(const QQmlDelegateModelGroup); - return d->name; -} - -void QQmlDelegateModelGroup::setName(const QString &name) -{ - Q_D(QQmlDelegateModelGroup); - if (d->model) - return; - if (d->name != name) { - d->name = name; - emit nameChanged(); - } -} - -/*! - \qmlproperty int QtQml.Models::DelegateModelGroup::count - - This property holds the number of items in the group. -*/ - -int QQmlDelegateModelGroup::count() const -{ - Q_D(const QQmlDelegateModelGroup); - if (!d->model) - return 0; - return QQmlDelegateModelPrivate::get(d->model)->m_compositor.count(d->group); -} - -/*! - \qmlproperty bool QtQml.Models::DelegateModelGroup::includeByDefault - - This property holds whether new items are assigned to this group by default. -*/ - -bool QQmlDelegateModelGroup::defaultInclude() const -{ - Q_D(const QQmlDelegateModelGroup); - return d->defaultInclude; -} - -void QQmlDelegateModelGroup::setDefaultInclude(bool include) -{ - Q_D(QQmlDelegateModelGroup); - if (d->defaultInclude != include) { - d->defaultInclude = include; - - if (d->model) { - if (include) - QQmlDelegateModelPrivate::get(d->model)->m_compositor.setDefaultGroup(d->group); - else - QQmlDelegateModelPrivate::get(d->model)->m_compositor.clearDefaultGroup(d->group); - } - emit defaultIncludeChanged(); - } -} - -/*! - \qmlmethod object QtQml.Models::DelegateModelGroup::get(int index) - - Returns a javascript object describing the item at \a index in the group. - - The returned object contains the same information that is available to a delegate from the - DelegateModel attached as well as the model for that item. It has the properties: - - \list - \li \b model The model data of the item. This is the same as the model context property in - a delegate - \li \b groups A list the of names of groups the item is a member of. This property can be - written to change the item's membership. - \li \b inItems Whether the item belongs to the \l {QtQml.Models::DelegateModel::items}{items} group. - Writing to this property will add or remove the item from the group. - \li \b itemsIndex The index of the item within the \l {QtQml.Models::DelegateModel::items}{items} group. - \li \b {in<GroupName>} Whether the item belongs to the dynamic group \e groupName. Writing to - this property will add or remove the item from the group. - \li \b {<groupName>Index} The index of the item within the dynamic group \e groupName. - \li \b isUnresolved Whether the item is bound to an index in the model assigned to - DelegateModel::model. Returns true if the item is not bound to the model, and false if it is. - \endlist -*/ - -QJSValue QQmlDelegateModelGroup::get(int index) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return QJSValue(); - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (!model->m_context || !model->m_context->isValid()) { - return QJSValue(); - } else if (index < 0 || index >= model->m_compositor.count(d->group)) { - qmlWarning(this) << tr("get: index out of range"); - return QJSValue(); - } - - Compositor::iterator it = model->m_compositor.find(d->group, index); - QQmlDelegateModelItem *cacheItem = it->inCache() - ? model->m_cache.at(it.cacheIndex) - : 0; - - if (!cacheItem) { - cacheItem = model->m_adaptorModel.createItem( - model->m_cacheMetaType, it.modelIndex()); - if (!cacheItem) - return QJSValue(); - cacheItem->groups = it->flags; - - model->m_cache.insert(it.cacheIndex, cacheItem); - model->m_compositor.setFlags(it, 1, Compositor::CacheFlag); - } - - if (model->m_cacheMetaType->modelItemProto.isUndefined()) - model->m_cacheMetaType->initializePrototype(); - QV4::ExecutionEngine *v4 = model->m_cacheMetaType->v4Engine; - QV4::Scope scope(v4); - QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(cacheItem)); - QV4::ScopedObject p(scope, model->m_cacheMetaType->modelItemProto.value()); - o->setPrototypeOf(p); - ++cacheItem->scriptRef; - - return QJSValue(v4, o->asReturnedValue()); -} - -bool QQmlDelegateModelGroupPrivate::parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const -{ - if (value.isNumber()) { - *index = value.toInt32(); - return true; - } - - if (!value.isObject()) - return false; - - QV4::ExecutionEngine *v4 = value.as<QV4::Object>()->engine(); - QV4::Scope scope(v4); - QV4::Scoped<QQmlDelegateModelItemObject> object(scope, value); - - if (object) { - QQmlDelegateModelItem * const cacheItem = object->d()->item; - if (QQmlDelegateModelPrivate *model = cacheItem->metaType->model - ? QQmlDelegateModelPrivate::get(cacheItem->metaType->model) - : nullptr) { - *index = model->m_cache.indexOf(cacheItem); - *group = Compositor::Cache; - return true; - } - } - return false; -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::insert(int index, jsdict data, array groups = undefined) - \qmlmethod QtQml.Models::DelegateModelGroup::insert(jsdict data, var groups = undefined) - - Creates a new entry at \a index in a DelegateModel with the values from \a data that - correspond to roles in the model assigned to DelegateModel::model. - - If no index is supplied the data is appended to the model. - - The optional \a groups parameter identifies the groups the new entry should belong to, - if unspecified this is equal to the group insert was called on. - - Data inserted into a DelegateModel can later be merged with an existing entry in - DelegateModel::model using the \l resolve() function. This can be used to create placeholder - items that are later replaced by actual data. -*/ - -void QQmlDelegateModelGroup::insert(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - if (args->length() == 0) - return; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[i]); - if (d->parseIndex(v, &index, &group)) { - if (index < 0 || index > model->m_compositor.count(group)) { - qmlWarning(this) << tr("insert: index out of range"); - return; - } - if (++i == args->length()) - return; - v = (*args)[i]; - } - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - int groups = 1 << d->group; - if (++i < args->length()) { - QV4::ScopedValue val(scope, (*args)[i]); - groups |= model->m_cacheMetaType->parseGroups(val); - } - - if (v->as<QV4::ArrayObject>()) { - return; - } else if (v->as<QV4::Object>()) { - model->insert(before, v, groups); - model->emitChanges(); - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::create(int index) - \qmlmethod QtQml.Models::DelegateModelGroup::create(int index, jsdict data, array groups = undefined) - \qmlmethod QtQml.Models::DelegateModelGroup::create(jsdict data, array groups = undefined) - - Returns a reference to the instantiated item at \a index in the group. - - If a \a data object is provided it will be \l {insert}{inserted} at \a index and an item - referencing this new entry will be returned. The optional \a groups parameter identifies - the groups the new entry should belong to, if unspecified this is equal to the group create() - was called on. - - All items returned by create are added to the - \l {QtQml.Models::DelegateModel::persistedItems}{persistedItems} group. Items in this - group remain instantiated when not referenced by any view. -*/ - -void QQmlDelegateModelGroup::create(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - - if (args->length() == 0) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - int index = model->m_compositor.count(d->group); - Compositor::Group group = d->group; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[i]); - if (d->parseIndex(v, &index, &group)) - ++i; - - if (i < args->length() && index >= 0 && index <= model->m_compositor.count(group)) { - v = (*args)[i]; - if (v->as<QV4::Object>()) { - int groups = 1 << d->group; - if (++i < args->length()) { - QV4::ScopedValue val(scope, (*args)[i]); - groups |= model->m_cacheMetaType->parseGroups(val); - } - - Compositor::insert_iterator before = index < model->m_compositor.count(group) - ? model->m_compositor.findInsertPosition(group, index) - : model->m_compositor.end(); - - index = before.index[d->group]; - group = d->group; - - if (!model->insert(before, v, groups)) { - return; - } - } - } - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("create: index out of range"); - return; - } - - QObject *object = model->object(group, index, QQmlIncubator::AsynchronousIfNested); - if (object) { - QVector<Compositor::Insert> inserts; - Compositor::iterator it = model->m_compositor.find(group, index); - model->m_compositor.setFlags(it, 1, d->group, Compositor::PersistedFlag, &inserts); - model->itemsInserted(inserts); - model->m_cache.at(it.cacheIndex)->releaseObject(); - } - - args->setReturnValue(QV4::QObjectWrapper::wrap(args->v4engine(), object)); - model->emitChanges(); -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::resolve(int from, int to) - - Binds an unresolved item at \a from to an item in DelegateModel::model at index \a to. - - Unresolved items are entries whose data has been \l {insert()}{inserted} into a DelegateModelGroup - instead of being derived from a DelegateModel::model index. Resolving an item will replace - the item at the target index with the unresolved item. A resolved an item will reflect the data - of the source model at its bound index and will move when that index moves like any other item. - - If a new item is replaced in the DelegateModelGroup onChanged() handler its insertion and - replacement will be communicated to views as an atomic operation, creating the appearance - that the model contents have not changed, or if the unresolved and model item are not adjacent - that the previously unresolved item has simply moved. - -*/ -void QQmlDelegateModelGroup::resolve(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - if (args->length() < 2) - return; - - int from = -1; - int to = -1; - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[0]); - if (d->parseIndex(v, &from, &fromGroup)) { - if (from < 0 || from >= model->m_compositor.count(fromGroup)) { - qmlWarning(this) << tr("resolve: from index out of range"); - return; - } - } else { - qmlWarning(this) << tr("resolve: from index invalid"); - return; - } - - v = (*args)[1]; - if (d->parseIndex(v, &to, &toGroup)) { - if (to < 0 || to >= model->m_compositor.count(toGroup)) { - qmlWarning(this) << tr("resolve: to index out of range"); - return; - } - } else { - qmlWarning(this) << tr("resolve: to index invalid"); - return; - } - - Compositor::iterator fromIt = model->m_compositor.find(fromGroup, from); - Compositor::iterator toIt = model->m_compositor.find(toGroup, to); - - if (!fromIt->isUnresolved()) { - qmlWarning(this) << tr("resolve: from is not an unresolved item"); - return; - } - if (!toIt->list) { - qmlWarning(this) << tr("resolve: to is not a model item"); - return; - } - - const int unresolvedFlags = fromIt->flags; - const int resolvedFlags = toIt->flags; - const int resolvedIndex = toIt.modelIndex(); - void * const resolvedList = toIt->list; - - QQmlDelegateModelItem *cacheItem = model->m_cache.at(fromIt.cacheIndex); - cacheItem->groups &= ~Compositor::UnresolvedFlag; - - if (toIt.cacheIndex > fromIt.cacheIndex) - toIt.decrementIndexes(1, unresolvedFlags); - if (!toIt->inGroup(fromGroup) || toIt.index[fromGroup] > from) - from += 1; - - model->itemsMoved( - QVector<Compositor::Remove>(1, Compositor::Remove(fromIt, 1, unresolvedFlags, 0)), - QVector<Compositor::Insert>(1, Compositor::Insert(toIt, 1, unresolvedFlags, 0))); - model->itemsInserted( - QVector<Compositor::Insert>(1, Compositor::Insert(toIt, 1, (resolvedFlags & ~unresolvedFlags) | Compositor::CacheFlag))); - toIt.incrementIndexes(1, resolvedFlags | unresolvedFlags); - model->itemsRemoved(QVector<Compositor::Remove>(1, Compositor::Remove(toIt, 1, resolvedFlags))); - - model->m_compositor.setFlags(toGroup, to, 1, unresolvedFlags & ~Compositor::UnresolvedFlag); - model->m_compositor.clearFlags(fromGroup, from, 1, unresolvedFlags); - - if (resolvedFlags & Compositor::CacheFlag) - model->m_compositor.insert(Compositor::Cache, toIt.cacheIndex, resolvedList, resolvedIndex, 1, Compositor::CacheFlag); - - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - - if (!cacheItem->isReferenced()) { - Q_ASSERT(toIt.cacheIndex == model->m_cache.indexOf(cacheItem)); - model->m_cache.removeAt(toIt.cacheIndex); - model->m_compositor.clearFlags(Compositor::Cache, toIt.cacheIndex, 1, Compositor::CacheFlag); - delete cacheItem; - Q_ASSERT(model->m_cache.count() == model->m_compositor.count(Compositor::Cache)); - } else { - cacheItem->resolveIndex(model->m_adaptorModel, resolvedIndex); - if (cacheItem->attached) - cacheItem->attached->emitUnresolvedChanged(); - } - - model->emitChanges(); -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::remove(int index, int count) - - Removes \a count items starting at \a index from the group. -*/ - -void QQmlDelegateModelGroup::remove(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - if (!d->model) - return; - Compositor::Group group = d->group; - int index = -1; - int count = 1; - - if (args->length() == 0) - return; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[0]); - if (!d->parseIndex(v, &index, &group)) { - qmlWarning(this) << tr("remove: invalid index"); - return; - } - - if (++i < args->length()) { - v = (*args)[i]; - if (v->isNumber()) - count = v->toInt32(); - } - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("remove: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("remove: invalid count"); - } else { - model->removeGroups(it, count, d->group, 1 << d->group); - } - } -} - -bool QQmlDelegateModelGroupPrivate::parseGroupArgs( - QQmlV4Function *args, Compositor::Group *group, int *index, int *count, int *groups) const -{ - if (!model || !QQmlDelegateModelPrivate::get(model)->m_cacheMetaType) - return false; - - if (args->length() < 2) - return false; - - int i = 0; - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[i]); - if (!parseIndex(v, index, group)) - return false; - - v = (*args)[++i]; - if (v->isNumber()) { - *count = v->toInt32(); - - if (++i == args->length()) - return false; - v = (*args)[i]; - } - - *groups = QQmlDelegateModelPrivate::get(model)->m_cacheMetaType->parseGroups(v); - - return true; -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::addGroups(int index, int count, stringlist groups) - - Adds \a count items starting at \a index to \a groups. -*/ - -void QQmlDelegateModelGroup::addGroups(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("addGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("addGroups: invalid count"); - } else { - model->addGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::removeGroups(int index, int count, stringlist groups) - - Removes \a count items starting at \a index from \a groups. -*/ - -void QQmlDelegateModelGroup::removeGroups(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("removeGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("removeGroups: invalid count"); - } else { - model->removeGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -void QQmlDelegateModelGroup::setGroups(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - Compositor::Group group = d->group; - int index = -1; - int count = 1; - int groups = 0; - - if (!d->parseGroupArgs(args, &group, &index, &count, &groups)) - return; - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - if (index < 0 || index >= model->m_compositor.count(group)) { - qmlWarning(this) << tr("setGroups: index out of range"); - } else if (count != 0) { - Compositor::iterator it = model->m_compositor.find(group, index); - if (count < 0 || count > model->m_compositor.count(d->group) - it.index[d->group]) { - qmlWarning(this) << tr("setGroups: invalid count"); - } else { - model->setGroups(it, count, d->group, groups); - } - } -} - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::setGroups(int index, int count, stringlist groups) - - Sets the \a groups \a count items starting at \a index belong to. -*/ - -/*! - \qmlmethod QtQml.Models::DelegateModelGroup::move(var from, var to, int count) - - Moves \a count at \a from in a group \a to a new position. - - \note The DelegateModel acts as a proxy model: it holds the delegates in a - different order than the \l{dm-model-property}{underlying model} has them. - Any subsequent changes to the underlying model will not undo whatever - reordering you have done via this function. -*/ - -void QQmlDelegateModelGroup::move(QQmlV4Function *args) -{ - Q_D(QQmlDelegateModelGroup); - - if (args->length() < 2) - return; - - Compositor::Group fromGroup = d->group; - Compositor::Group toGroup = d->group; - int from = -1; - int to = -1; - int count = 1; - - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue v(scope, (*args)[0]); - if (!d->parseIndex(v, &from, &fromGroup)) { - qmlWarning(this) << tr("move: invalid from index"); - return; - } - - v = (*args)[1]; - if (!d->parseIndex(v, &to, &toGroup)) { - qmlWarning(this) << tr("move: invalid to index"); - return; - } - - if (args->length() > 2) { - v = (*args)[2]; - if (v->isNumber()) - count = v->toInt32(); - } - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(d->model); - - if (count < 0) { - qmlWarning(this) << tr("move: invalid count"); - } else if (from < 0 || from + count > model->m_compositor.count(fromGroup)) { - qmlWarning(this) << tr("move: from index out of range"); - } else if (!model->m_compositor.verifyMoveTo(fromGroup, from, toGroup, to, count, d->group)) { - qmlWarning(this) << tr("move: to index out of range"); - } else if (count > 0) { - QVector<Compositor::Remove> removes; - QVector<Compositor::Insert> inserts; - - model->m_compositor.move(fromGroup, from, toGroup, to, count, d->group, &removes, &inserts); - model->itemsMoved(removes, inserts); - model->emitChanges(); - } - -} - -/*! - \qmlsignal QtQml.Models::DelegateModelGroup::changed(array removed, array inserted) - - This signal is emitted when items have been removed from or inserted into the group. - - Each object in the \a removed and \a inserted arrays has two values; the \e index of the first - item inserted or removed and a \e count of the number of consecutive items inserted or removed. - - Each index is adjusted for previous changes with all removed items preceding any inserted - items. - - The corresponding handler is \c onChanged. -*/ - -//============================================================================ - -QQmlPartsModel::QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent) - : QQmlInstanceModel(*new QObjectPrivate, parent) - , m_model(model) - , m_part(part) - , m_compositorGroup(Compositor::Cache) - , m_inheritGroup(true) -{ - QQmlDelegateModelPrivate *d = QQmlDelegateModelPrivate::get(m_model); - if (d->m_cacheMetaType) { - QQmlDelegateModelGroupPrivate::get(d->m_groups[1])->emitters.insert(this); - m_compositorGroup = Compositor::Default; - } else { - d->m_pendingParts.insert(this); - } -} - -QQmlPartsModel::~QQmlPartsModel() -{ -} - -QString QQmlPartsModel::filterGroup() const -{ - if (m_inheritGroup) - return m_model->filterGroup(); - return m_filterGroup; -} - -void QQmlPartsModel::setFilterGroup(const QString &group) -{ - if (QQmlDelegateModelPrivate::get(m_model)->m_transaction) { - qmlWarning(this) << tr("The group of a DelegateModel cannot be changed within onChanged"); - return; - } - - if (m_filterGroup != group || m_inheritGroup) { - m_filterGroup = group; - m_inheritGroup = false; - updateFilterGroup(); - - emit filterGroupChanged(); - } -} - -void QQmlPartsModel::resetFilterGroup() -{ - if (!m_inheritGroup) { - m_inheritGroup = true; - updateFilterGroup(); - emit filterGroupChanged(); - } -} - -void QQmlPartsModel::updateFilterGroup() -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - if (!model->m_cacheMetaType) - return; - - if (m_inheritGroup) { - if (m_filterGroup == model->m_filterGroup) - return; - m_filterGroup = model->m_filterGroup; - } - - QQmlListCompositor::Group previousGroup = m_compositorGroup; - m_compositorGroup = Compositor::Default; - QQmlDelegateModelGroupPrivate::get(model->m_groups[Compositor::Default])->emitters.insert(this); - for (int i = 1; i < model->m_groupCount; ++i) { - if (m_filterGroup == model->m_cacheMetaType->groupNames.at(i - 1)) { - m_compositorGroup = Compositor::Group(i); - break; - } - } - - QQmlDelegateModelGroupPrivate::get(model->m_groups[m_compositorGroup])->emitters.insert(this); - if (m_compositorGroup != previousGroup) { - QVector<QQmlChangeSet::Change> removes; - QVector<QQmlChangeSet::Change> inserts; - model->m_compositor.transition(previousGroup, m_compositorGroup, &removes, &inserts); - - QQmlChangeSet changeSet; - changeSet.move(removes, inserts); - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - } -} - -void QQmlPartsModel::updateFilterGroup( - Compositor::Group group, const QQmlChangeSet &changeSet) -{ - if (!m_inheritGroup) - return; - - m_compositorGroup = group; - QQmlDelegateModelGroupPrivate::get(QQmlDelegateModelPrivate::get(m_model)->m_groups[m_compositorGroup])->emitters.insert(this); - - if (!changeSet.isEmpty()) - emit modelUpdated(changeSet, false); - - if (changeSet.difference() != 0) - emit countChanged(); - - emit filterGroupChanged(); -} - -int QQmlPartsModel::count() const -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - return model->m_delegate - ? model->m_compositor.count(m_compositorGroup) - : 0; -} - -bool QQmlPartsModel::isValid() const -{ - return m_model->isValid(); -} - -QObject *QQmlPartsModel::object(int index, QQmlIncubator::IncubationMode incubationMode) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - - if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) { - qWarning() << "DelegateModel::item: index out range" << index << model->m_compositor.count(m_compositorGroup); - return nullptr; - } - - QObject *object = model->object(m_compositorGroup, index, incubationMode); - - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) { - QObject *part = package->part(m_part); - if (!part) - return nullptr; - m_packaged.insertMulti(part, package); - return part; - } - - model->release(object); - if (!model->m_delegateValidated) { - if (object) - qmlWarning(model->m_delegate) << tr("Delegate component must be Package type."); - model->m_delegateValidated = true; - } - - return nullptr; -} - -QQmlInstanceModel::ReleaseFlags QQmlPartsModel::release(QObject *item) -{ - QQmlInstanceModel::ReleaseFlags flags = nullptr; - - QHash<QObject *, QQuickPackage *>::iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - QQuickPackage *package = *it; - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - flags = model->release(package); - m_packaged.erase(it); - if (!m_packaged.contains(item)) - flags &= ~Referenced; - if (flags & Destroyed) - QQmlDelegateModelPrivate::get(m_model)->emitDestroyingPackage(package); - } - return flags; -} - -QVariant QQmlPartsModel::variantValue(int index, const QString &role) -{ - return QQmlDelegateModelPrivate::get(m_model)->variantValue(m_compositorGroup, index, role); -} - -void QQmlPartsModel::setWatchedRoles(const QList<QByteArray> &roles) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - model->m_adaptorModel.replaceWatchedRoles(m_watchedRoles, roles); - m_watchedRoles = roles; -} - -QQmlIncubator::Status QQmlPartsModel::incubationStatus(int index) -{ - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - Compositor::iterator it = model->m_compositor.find(model->m_compositorGroup, index); - if (!it->inCache()) - return QQmlIncubator::Null; - - if (auto incubationTask = model->m_cache.at(it.cacheIndex)->incubationTask) - return incubationTask->status(); - - return QQmlIncubator::Ready; -} - -int QQmlPartsModel::indexOf(QObject *item, QObject *) const -{ - QHash<QObject *, QQuickPackage *>::const_iterator it = m_packaged.find(item); - if (it != m_packaged.end()) { - if (QQmlDelegateModelItem *cacheItem = QQmlDelegateModelItem::dataForObject(*it)) - return cacheItem->groupIndex(m_compositorGroup); - } - return -1; -} - -void QQmlPartsModel::createdPackage(int index, QQuickPackage *package) -{ - emit createdItem(index, package->part(m_part)); -} - -void QQmlPartsModel::initPackage(int index, QQuickPackage *package) -{ - if (m_modelUpdatePending) - m_pendingPackageInitializations << index; - else - emit initItem(index, package->part(m_part)); -} - -void QQmlPartsModel::destroyingPackage(QQuickPackage *package) -{ - QObject *item = package->part(m_part); - Q_ASSERT(!m_packaged.contains(item)); - emit destroyingItem(item); -} - -void QQmlPartsModel::emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - m_modelUpdatePending = false; - emit modelUpdated(changeSet, reset); - if (changeSet.difference() != 0) - emit countChanged(); - - QQmlDelegateModelPrivate *model = QQmlDelegateModelPrivate::get(m_model); - QVector<int> pendingPackageInitializations; - qSwap(pendingPackageInitializations, m_pendingPackageInitializations); - for (int index : pendingPackageInitializations) { - if (!model->m_delegate || index < 0 || index >= model->m_compositor.count(m_compositorGroup)) - continue; - QObject *object = model->object(m_compositorGroup, index, QQmlIncubator::Asynchronous); - if (QQuickPackage *package = qmlobject_cast<QQuickPackage *>(object)) - emit initItem(index, package->part(m_part)); - model->release(object); - } -} - -//============================================================================ - -struct QQmlDelegateModelGroupChange : QV4::Object -{ - V4_OBJECT2(QQmlDelegateModelGroupChange, QV4::Object) - - static QV4::Heap::QQmlDelegateModelGroupChange *create(QV4::ExecutionEngine *e) { - return e->memoryManager->allocate<QQmlDelegateModelGroupChange>(); - } - - static QV4::ReturnedValue method_get_index(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, thisObject->as<QQmlDelegateModelGroupChange>()); - if (!that) - THROW_TYPE_ERROR(); - return QV4::Encode(that->d()->change.index); - } - static QV4::ReturnedValue method_get_count(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, thisObject->as<QQmlDelegateModelGroupChange>()); - if (!that) - THROW_TYPE_ERROR(); - return QV4::Encode(that->d()->change.count); - } - static QV4::ReturnedValue method_get_moveId(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelGroupChange> that(scope, thisObject->as<QQmlDelegateModelGroupChange>()); - if (!that) - THROW_TYPE_ERROR(); - if (that->d()->change.moveId < 0) - RETURN_UNDEFINED(); - return QV4::Encode(that->d()->change.moveId); - } -}; - -DEFINE_OBJECT_VTABLE(QQmlDelegateModelGroupChange); - -struct QQmlDelegateModelGroupChangeArray : public QV4::Object -{ - V4_OBJECT2(QQmlDelegateModelGroupChangeArray, QV4::Object) - V4_NEEDS_DESTROY -public: - static QV4::Heap::QQmlDelegateModelGroupChangeArray *create(QV4::ExecutionEngine *engine, const QVector<QQmlChangeSet::Change> &changes) - { - return engine->memoryManager->allocate<QQmlDelegateModelGroupChangeArray>(changes); - } - - quint32 count() const { return d()->changes->count(); } - const QQmlChangeSet::Change &at(int index) const { return d()->changes->at(index); } - - static QV4::ReturnedValue virtualGet(const QV4::Managed *m, QV4::PropertyKey id, const QV4::Value *receiver, bool *hasProperty) - { - if (id.isArrayIndex()) { - uint index = id.asArrayIndex(); - Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - QV4::ExecutionEngine *v4 = static_cast<const QQmlDelegateModelGroupChangeArray *>(m)->engine(); - QV4::Scope scope(v4); - QV4::Scoped<QQmlDelegateModelGroupChangeArray> array(scope, static_cast<const QQmlDelegateModelGroupChangeArray *>(m)); - - if (index >= array->count()) { - if (hasProperty) - *hasProperty = false; - return QV4::Value::undefinedValue().asReturnedValue(); - } - - const QQmlChangeSet::Change &change = array->at(index); - - QV4::ScopedObject changeProto(scope, engineData(v4)->changeProto.value()); - QV4::Scoped<QQmlDelegateModelGroupChange> object(scope, QQmlDelegateModelGroupChange::create(v4)); - object->setPrototypeOf(changeProto); - object->d()->change = change; - - if (hasProperty) - *hasProperty = true; - return object.asReturnedValue(); - } - - Q_ASSERT(m->as<QQmlDelegateModelGroupChangeArray>()); - const QQmlDelegateModelGroupChangeArray *array = static_cast<const QQmlDelegateModelGroupChangeArray *>(m); - - if (id == array->engine()->id_length()->propertyKey()) { - if (hasProperty) - *hasProperty = true; - return QV4::Encode(array->count()); - } - - return Object::virtualGet(m, id, receiver, hasProperty); - } -}; - -void QV4::Heap::QQmlDelegateModelGroupChangeArray::init(const QVector<QQmlChangeSet::Change> &changes) -{ - Object::init(); - this->changes = new QVector<QQmlChangeSet::Change>(changes); - QV4::Scope scope(internalClass->engine); - QV4::ScopedObject o(scope, this); - o->setArrayType(QV4::Heap::ArrayData::Custom); -} - -DEFINE_OBJECT_VTABLE(QQmlDelegateModelGroupChangeArray); - -QQmlDelegateModelEngineData::QQmlDelegateModelEngineData(QV4::ExecutionEngine *v4) -{ - QV4::Scope scope(v4); - - QV4::ScopedObject proto(scope, v4->newObject()); - proto->defineAccessorProperty(QStringLiteral("index"), QQmlDelegateModelGroupChange::method_get_index, nullptr); - proto->defineAccessorProperty(QStringLiteral("count"), QQmlDelegateModelGroupChange::method_get_count, nullptr); - proto->defineAccessorProperty(QStringLiteral("moveId"), QQmlDelegateModelGroupChange::method_get_moveId, nullptr); - changeProto.set(v4, proto); -} - -QQmlDelegateModelEngineData::~QQmlDelegateModelEngineData() -{ -} - -QV4::ReturnedValue QQmlDelegateModelEngineData::array(QV4::ExecutionEngine *v4, - const QVector<QQmlChangeSet::Change> &changes) -{ - QV4::Scope scope(v4); - QV4::ScopedObject o(scope, QQmlDelegateModelGroupChangeArray::create(v4, changes)); - return o.asReturnedValue(); -} - -QT_END_NAMESPACE - -#include "moc_qqmldelegatemodel_p.cpp" diff --git a/src/qml/types/qqmldelegatemodel_p.h b/src/qml/types/qqmldelegatemodel_p.h deleted file mode 100644 index 2684162514..0000000000 --- a/src/qml/types/qqmldelegatemodel_p.h +++ /dev/null @@ -1,248 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLDATAMODEL_P_H -#define QQMLDATAMODEL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtqmlglobal_p.h> -#include <private/qqmllistcompositor_p.h> -#include <private/qqmlobjectmodel_p.h> -#include <private/qqmlincubator_p.h> - -#include <QtCore/qabstractitemmodel.h> -#include <QtCore/qstringlist.h> - -#include <private/qqmlglobal_p.h> - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -class QQmlChangeSet; -class QQuickPackage; -class QQmlV4Function; -class QQmlDelegateModelGroup; -class QQmlDelegateModelAttached; -class QQmlDelegateModelPrivate; - - -class Q_QML_PRIVATE_EXPORT QQmlDelegateModel : public QQmlInstanceModel, public QQmlParserStatus -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlDelegateModel) - - Q_PROPERTY(QVariant model READ model WRITE setModel) - Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate) - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) - Q_PROPERTY(QQmlDelegateModelGroup *items READ items CONSTANT) //TODO : worth renaming? - Q_PROPERTY(QQmlDelegateModelGroup *persistedItems READ persistedItems CONSTANT) - Q_PROPERTY(QQmlListProperty<QQmlDelegateModelGroup> groups READ groups CONSTANT) - Q_PROPERTY(QObject *parts READ parts CONSTANT) - Q_PROPERTY(QVariant rootIndex READ rootIndex WRITE setRootIndex NOTIFY rootIndexChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - Q_INTERFACES(QQmlParserStatus) -public: - QQmlDelegateModel(); - QQmlDelegateModel(QQmlContext *, QObject *parent=nullptr); - ~QQmlDelegateModel(); - - void classBegin() override; - void componentComplete() override; - - QVariant model() const; - void setModel(const QVariant &); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *); - - QVariant rootIndex() const; - void setRootIndex(const QVariant &root); - - Q_INVOKABLE QVariant modelIndex(int idx) const; - Q_INVOKABLE QVariant parentModelIndex() const; - - int count() const override; - bool isValid() const override { return delegate() != nullptr; } - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *object) override; - void cancel(int index) override; - QVariant variantValue(int index, const QString &role) override; - void setWatchedRoles(const QList<QByteArray> &roles) override; - QQmlIncubator::Status incubationStatus(int index) override; - - int indexOf(QObject *object, QObject *objectContext) const override; - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - - QQmlDelegateModelGroup *items(); - QQmlDelegateModelGroup *persistedItems(); - QQmlListProperty<QQmlDelegateModelGroup> groups(); - QObject *parts(); - - const QAbstractItemModel *abstractItemModel() const override; - - bool event(QEvent *) override; - - static QQmlDelegateModelAttached *qmlAttachedProperties(QObject *obj); - -Q_SIGNALS: - void filterGroupChanged(); - void defaultGroupsChanged(); - void rootIndexChanged(); - -private Q_SLOTS: - void _q_itemsChanged(int index, int count, const QVector<int> &roles); - void _q_itemsInserted(int index, int count); - void _q_itemsRemoved(int index, int count); - void _q_itemsMoved(int from, int to, int count); - void _q_modelReset(); - void _q_rowsInserted(const QModelIndex &,int,int); - void _q_rowsAboutToBeRemoved(const QModelIndex &parent, int begin, int end); - void _q_rowsRemoved(const QModelIndex &,int,int); - void _q_rowsMoved(const QModelIndex &, int, int, const QModelIndex &, int); - void _q_dataChanged(const QModelIndex&,const QModelIndex&,const QVector<int> &); - void _q_layoutChanged(const QList<QPersistentModelIndex>&, QAbstractItemModel::LayoutChangeHint); - -private: - bool isDescendantOf(const QPersistentModelIndex &desc, const QList<QPersistentModelIndex> &parents) const; - - Q_DISABLE_COPY(QQmlDelegateModel) -}; - -class QQmlDelegateModelGroupPrivate; -class Q_QML_PRIVATE_EXPORT QQmlDelegateModelGroup : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) - Q_PROPERTY(bool includeByDefault READ defaultInclude WRITE setDefaultInclude NOTIFY defaultIncludeChanged) -public: - QQmlDelegateModelGroup(QObject *parent = nullptr); - QQmlDelegateModelGroup(const QString &name, QQmlDelegateModel *model, int compositorType, QObject *parent = nullptr); - ~QQmlDelegateModelGroup(); - - QString name() const; - void setName(const QString &name); - - int count() const; - - bool defaultInclude() const; - void setDefaultInclude(bool include); - - Q_INVOKABLE QJSValue get(int index); - -public Q_SLOTS: - void insert(QQmlV4Function *); - void create(QQmlV4Function *); - void resolve(QQmlV4Function *); - void remove(QQmlV4Function *); - void addGroups(QQmlV4Function *); - void removeGroups(QQmlV4Function *); - void setGroups(QQmlV4Function *); - void move(QQmlV4Function *); - -Q_SIGNALS: - void countChanged(); - void nameChanged(); - void defaultIncludeChanged(); - void changed(const QJSValue &removed, const QJSValue &inserted); -private: - Q_DECLARE_PRIVATE(QQmlDelegateModelGroup) -}; - -class QQmlDelegateModelItem; -class QQmlDelegateModelAttachedMetaObject; -class QQmlDelegateModelAttached : public QObject -{ - Q_OBJECT - Q_PROPERTY(QQmlDelegateModel *model READ model CONSTANT) - Q_PROPERTY(QStringList groups READ groups WRITE setGroups NOTIFY groupsChanged) - Q_PROPERTY(bool isUnresolved READ isUnresolved NOTIFY unresolvedChanged) -public: - QQmlDelegateModelAttached(QObject *parent); - QQmlDelegateModelAttached(QQmlDelegateModelItem *cacheItem, QObject *parent); - ~QQmlDelegateModelAttached() {} - - void resetCurrentIndex(); - void setCacheItem(QQmlDelegateModelItem *item); - - QQmlDelegateModel *model() const; - - QStringList groups() const; - void setGroups(const QStringList &groups); - - bool isUnresolved() const; - - void emitChanges(); - - void emitUnresolvedChanged() { Q_EMIT unresolvedChanged(); } - -Q_SIGNALS: - void groupsChanged(); - void unresolvedChanged(); - -public: - QQmlDelegateModelItem *m_cacheItem; - int m_previousGroups; - int m_currentIndex[QQmlListCompositor::MaximumGroupCount]; - int m_previousIndex[QQmlListCompositor::MaximumGroupCount]; - - friend class QQmlDelegateModelAttachedMetaObject; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlDelegateModel) -QML_DECLARE_TYPEINFO(QQmlDelegateModel, QML_HAS_ATTACHED_PROPERTIES) -QML_DECLARE_TYPE(QQmlDelegateModelGroup) - -#endif // QQMLDATAMODEL_P_H diff --git a/src/qml/types/qqmldelegatemodel_p_p.h b/src/qml/types/qqmldelegatemodel_p_p.h deleted file mode 100644 index 7f10bbf370..0000000000 --- a/src/qml/types/qqmldelegatemodel_p_p.h +++ /dev/null @@ -1,450 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLDATAMODEL_P_P_H -#define QQMLDATAMODEL_P_P_H - -#include "qqmldelegatemodel_p.h" -#include <private/qv4qobjectwrapper_p.h> - -#include <QtQml/qqmlcontext.h> -#include <QtQml/qqmlincubator.h> - -#include <private/qqmladaptormodel_p.h> -#include <private/qqmlopenmetaobject_p.h> - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -typedef QQmlListCompositor Compositor; - -class QQmlDelegateModelAttachedMetaObject; -class QQmlAbstractDelegateComponent; - -class Q_QML_PRIVATE_EXPORT QQmlDelegateModelItemMetaType : public QQmlRefCount -{ -public: - QQmlDelegateModelItemMetaType(QV4::ExecutionEngine *engine, QQmlDelegateModel *model, const QStringList &groupNames); - ~QQmlDelegateModelItemMetaType(); - - void initializeMetaObject(); - void initializePrototype(); - - int parseGroups(const QStringList &groupNames) const; - int parseGroups(const QV4::Value &groupNames) const; - - QPointer<QQmlDelegateModel> model; - const int groupCount; - QV4::ExecutionEngine * const v4Engine; - QQmlDelegateModelAttachedMetaObject *metaObject; - const QStringList groupNames; - QV4::PersistentValue modelItemProto; -}; - -class QQmlAdaptorModel; -class QQDMIncubationTask; - -class QQmlDelegateModelItem : public QObject -{ - Q_OBJECT - Q_PROPERTY(int index READ modelIndex NOTIFY modelIndexChanged) - Q_PROPERTY(int row READ modelRow NOTIFY rowChanged REVISION 12) - Q_PROPERTY(int column READ modelColumn NOTIFY columnChanged REVISION 12) - Q_PROPERTY(QObject *model READ modelObject CONSTANT) -public: - QQmlDelegateModelItem(QQmlDelegateModelItemMetaType *metaType, - QQmlAdaptorModel::Accessors *accessor, int modelIndex, - int row, int column); - ~QQmlDelegateModelItem(); - - void referenceObject() { ++objectRef; } - bool releaseObject() { return --objectRef == 0 && !(groups & Compositor::PersistedFlag); } - bool isObjectReferenced() const { return objectRef != 0 || (groups & Compositor::PersistedFlag); } - void childContextObjectDestroyed(QObject *childContextObject); - - bool isReferenced() const { - return scriptRef - || incubationTask - || ((groups & Compositor::UnresolvedFlag) && (groups & Compositor::GroupMask)); - } - - void Dispose(); - - QObject *modelObject() { return this; } - - void destroyObject(); - - static QQmlDelegateModelItem *dataForObject(QObject *object); - - int groupIndex(Compositor::Group group); - - int modelRow() const { return row; } - int modelColumn() const { return column; } - int modelIndex() const { return index; } - virtual void setModelIndex(int idx, int newRow, int newColumn); - - virtual QV4::ReturnedValue get() { return QV4::QObjectWrapper::wrap(v4, this); } - - virtual void setValue(const QString &role, const QVariant &value) { Q_UNUSED(role); Q_UNUSED(value); } - virtual bool resolveIndex(const QQmlAdaptorModel &, int) { return false; } - - static QV4::ReturnedValue get_model(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue get_groups(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue set_groups(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue get_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &); - static QV4::ReturnedValue set_member(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg); - static QV4::ReturnedValue get_index(QQmlDelegateModelItem *thisItem, uint flag, const QV4::Value &arg); - - QV4::ExecutionEngine *v4; - QQmlDelegateModelItemMetaType * const metaType; - QQmlContextDataRef contextData; - QPointer<QObject> object; - QPointer<QQmlDelegateModelAttached> attached; - QQDMIncubationTask *incubationTask; - QQmlComponent *delegate; - int poolTime; - int objectRef; - int scriptRef; - int groups; - int index; - -Q_SIGNALS: - void modelIndexChanged(); - Q_REVISION(12) void rowChanged(); - Q_REVISION(12) void columnChanged(); - -protected: - void objectDestroyed(QObject *); - int row; - int column; -}; - -namespace QV4 { -namespace Heap { -struct QQmlDelegateModelItemObject : Object { - inline void init(QQmlDelegateModelItem *item); - void destroy(); - QQmlDelegateModelItem *item; -}; - -} -} - -struct QQmlDelegateModelItemObject : QV4::Object -{ - V4_OBJECT2(QQmlDelegateModelItemObject, QV4::Object) - V4_NEEDS_DESTROY -}; - -void QV4::Heap::QQmlDelegateModelItemObject::init(QQmlDelegateModelItem *item) -{ - Object::init(); - this->item = item; -} - - - -class QQmlDelegateModelPrivate; -class QQDMIncubationTask : public QQmlIncubator -{ -public: - QQDMIncubationTask(QQmlDelegateModelPrivate *l, IncubationMode mode) - : QQmlIncubator(mode) - , incubating(nullptr) - , vdm(l) {} - - void statusChanged(Status) override; - void setInitialState(QObject *) override; - - QQmlDelegateModelItem *incubating = nullptr; - QQmlDelegateModelPrivate *vdm = nullptr; - int index[QQmlListCompositor::MaximumGroupCount]; -}; - - -class QQmlDelegateModelGroupEmitter -{ -public: - virtual ~QQmlDelegateModelGroupEmitter() {} - virtual void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) = 0; - virtual void createdPackage(int, QQuickPackage *) {} - virtual void initPackage(int, QQuickPackage *) {} - virtual void destroyingPackage(QQuickPackage *) {} - - QIntrusiveListNode emitterNode; -}; - -typedef QIntrusiveList<QQmlDelegateModelGroupEmitter, &QQmlDelegateModelGroupEmitter::emitterNode> QQmlDelegateModelGroupEmitterList; - -class QQmlDelegateModelGroupPrivate : public QObjectPrivate -{ -public: - Q_DECLARE_PUBLIC(QQmlDelegateModelGroup) - - QQmlDelegateModelGroupPrivate() : group(Compositor::Cache), defaultInclude(false) {} - - static QQmlDelegateModelGroupPrivate *get(QQmlDelegateModelGroup *group) { - return static_cast<QQmlDelegateModelGroupPrivate *>(QObjectPrivate::get(group)); } - - void setModel(QQmlDelegateModel *model, Compositor::Group group); - bool isChangedConnected(); - void emitChanges(QV4::ExecutionEngine *engine); - void emitModelUpdated(bool reset); - - void createdPackage(int index, QQuickPackage *package); - void initPackage(int index, QQuickPackage *package); - void destroyingPackage(QQuickPackage *package); - - bool parseIndex(const QV4::Value &value, int *index, Compositor::Group *group) const; - bool parseGroupArgs( - QQmlV4Function *args, Compositor::Group *group, int *index, int *count, int *groups) const; - - Compositor::Group group; - QPointer<QQmlDelegateModel> model; - QQmlDelegateModelGroupEmitterList emitters; - QQmlChangeSet changeSet; - QString name; - bool defaultInclude; -}; - -class QQmlDelegateModelParts; - -class QQmlDelegateModelPrivate : public QObjectPrivate, public QQmlDelegateModelGroupEmitter -{ - Q_DECLARE_PUBLIC(QQmlDelegateModel) -public: - QQmlDelegateModelPrivate(QQmlContext *); - ~QQmlDelegateModelPrivate(); - - static QQmlDelegateModelPrivate *get(QQmlDelegateModel *m) { - return static_cast<QQmlDelegateModelPrivate *>(QObjectPrivate::get(m)); - } - - void init(); - void connectModel(QQmlAdaptorModel *model); - void connectToAbstractItemModel(); - void disconnectFromAbstractItemModel(); - - void requestMoreIfNecessary(); - QObject *object(Compositor::Group group, int index, QQmlIncubator::IncubationMode incubationMode); - QQmlDelegateModel::ReleaseFlags release(QObject *object); - QVariant variantValue(Compositor::Group group, int index, const QString &name); - void emitCreatedPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); - void emitInitPackage(QQDMIncubationTask *incubationTask, QQuickPackage *package); - void emitCreatedItem(QQDMIncubationTask *incubationTask, QObject *item) { - Q_EMIT q_func()->createdItem(incubationTask->index[m_compositorGroup], item); } - void emitInitItem(QQDMIncubationTask *incubationTask, QObject *item) { - Q_EMIT q_func()->initItem(incubationTask->index[m_compositorGroup], item); } - void emitDestroyingPackage(QQuickPackage *package); - void emitDestroyingItem(QObject *item) { Q_EMIT q_func()->destroyingItem(item); } - void addCacheItem(QQmlDelegateModelItem *item, Compositor::iterator it); - void removeCacheItem(QQmlDelegateModelItem *cacheItem); - - void updateFilterGroup(); - - void addGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void removeGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - void setGroups(Compositor::iterator from, int count, Compositor::Group group, int groupFlags); - - void itemsInserted( - const QVector<Compositor::Insert> &inserts, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedInserts, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems = nullptr); - void itemsInserted(const QVector<Compositor::Insert> &inserts); - void itemsRemoved( - const QVector<Compositor::Remove> &removes, - QVarLengthArray<QVector<QQmlChangeSet::Change>, Compositor::MaximumGroupCount> *translatedRemoves, - QHash<int, QList<QQmlDelegateModelItem *> > *movedItems = nullptr); - void itemsRemoved(const QVector<Compositor::Remove> &removes); - void itemsMoved( - const QVector<Compositor::Remove> &removes, const QVector<Compositor::Insert> &inserts); - void itemsChanged(const QVector<Compositor::Change> &changes); - void emitChanges(); - void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override; - void delegateChanged(bool add = true, bool remove = true); - - bool insert(Compositor::insert_iterator &before, const QV4::Value &object, int groups); - - int adaptorModelCount() const; - - static void group_append(QQmlListProperty<QQmlDelegateModelGroup> *property, QQmlDelegateModelGroup *group); - static int group_count(QQmlListProperty<QQmlDelegateModelGroup> *property); - static QQmlDelegateModelGroup *group_at(QQmlListProperty<QQmlDelegateModelGroup> *property, int index); - - void releaseIncubator(QQDMIncubationTask *incubationTask); - void incubatorStatusChanged(QQDMIncubationTask *incubationTask, QQmlIncubator::Status status); - void setInitialState(QQDMIncubationTask *incubationTask, QObject *o); - - QQmlAdaptorModel m_adaptorModel; - QQmlListCompositor m_compositor; - QQmlStrongJSQObjectReference<QQmlComponent> m_delegate; - QQmlAbstractDelegateComponent *m_delegateChooser; - QMetaObject::Connection m_delegateChooserChanged; - QQmlDelegateModelItemMetaType *m_cacheMetaType; - QPointer<QQmlContext> m_context; - QQmlDelegateModelParts *m_parts; - QQmlDelegateModelGroupEmitterList m_pendingParts; - - QList<QQmlDelegateModelItem *> m_cache; - QList<QQDMIncubationTask *> m_finishedIncubating; - QList<QByteArray> m_watchedRoles; - - QString m_filterGroup; - - int m_count; - int m_groupCount; - - QQmlListCompositor::Group m_compositorGroup; - bool m_complete : 1; - bool m_delegateValidated : 1; - bool m_reset : 1; - bool m_transaction : 1; - bool m_incubatorCleanupScheduled : 1; - bool m_waitingToFetchMore : 1; - - union { - struct { - QQmlDelegateModelGroup *m_cacheItems; - QQmlDelegateModelGroup *m_items; - QQmlDelegateModelGroup *m_persistedItems; - }; - QQmlDelegateModelGroup *m_groups[Compositor::MaximumGroupCount]; - }; -}; - -class QQmlPartsModel : public QQmlInstanceModel, public QQmlDelegateModelGroupEmitter -{ - Q_OBJECT - Q_PROPERTY(QString filterOnGroup READ filterGroup WRITE setFilterGroup NOTIFY filterGroupChanged RESET resetFilterGroup) -public: - QQmlPartsModel(QQmlDelegateModel *model, const QString &part, QObject *parent = nullptr); - ~QQmlPartsModel(); - - QString filterGroup() const; - void setFilterGroup(const QString &group); - void resetFilterGroup(); - void updateFilterGroup(); - void updateFilterGroup(Compositor::Group group, const QQmlChangeSet &changeSet); - - int count() const override; - bool isValid() const override; - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *item) override; - QVariant variantValue(int index, const QString &role) override; - QList<QByteArray> watchedRoles() const { return m_watchedRoles; } - void setWatchedRoles(const QList<QByteArray> &roles) override; - QQmlIncubator::Status incubationStatus(int index) override; - - int indexOf(QObject *item, QObject *objectContext) const override; - - void emitModelUpdated(const QQmlChangeSet &changeSet, bool reset) override; - - void createdPackage(int index, QQuickPackage *package) override; - void initPackage(int index, QQuickPackage *package) override; - void destroyingPackage(QQuickPackage *package) override; - -Q_SIGNALS: - void filterGroupChanged(); - -private: - QQmlDelegateModel *m_model; - QHash<QObject *, QQuickPackage *> m_packaged; - QString m_part; - QString m_filterGroup; - QList<QByteArray> m_watchedRoles; - QVector<int> m_pendingPackageInitializations; // vector holds model indices - Compositor::Group m_compositorGroup; - bool m_inheritGroup; - bool m_modelUpdatePending = true; -}; - -class QMetaPropertyBuilder; - -class QQmlDelegateModelPartsMetaObject : public QQmlOpenMetaObject -{ -public: - QQmlDelegateModelPartsMetaObject(QObject *parent) - : QQmlOpenMetaObject(parent) {} - - void propertyCreated(int, QMetaPropertyBuilder &) override; - QVariant initialValue(int) override; -}; - -class QQmlDelegateModelParts : public QObject -{ -Q_OBJECT -public: - QQmlDelegateModelParts(QQmlDelegateModel *parent); - - QQmlDelegateModel *model; - QList<QQmlPartsModel *> models; -}; - -class QQmlDelegateModelAttachedMetaObject : public QAbstractDynamicMetaObject, public QQmlRefCount -{ -public: - QQmlDelegateModelAttachedMetaObject( - QQmlDelegateModelItemMetaType *metaType, QMetaObject *metaObject); - ~QQmlDelegateModelAttachedMetaObject(); - - void objectDestroyed(QObject *) override; - int metaCall(QObject *, QMetaObject::Call, int _id, void **) override; - -private: - QQmlDelegateModelItemMetaType * const metaType; - QMetaObject * const metaObject; - const int memberPropertyOffset; - const int indexPropertyOffset; -}; - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/types/qqmlinstantiator.cpp b/src/qml/types/qqmlinstantiator.cpp deleted file mode 100644 index a23ec0f2b4..0000000000 --- a/src/qml/types/qqmlinstantiator.cpp +++ /dev/null @@ -1,509 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlinstantiator_p.h" -#include "qqmlinstantiator_p_p.h" -#include <QtQml/QQmlContext> -#include <QtQml/QQmlComponent> -#include <QtQml/QQmlInfo> -#include <QtQml/QQmlError> -#include <QtQml/private/qqmlobjectmodel_p.h> -#if QT_CONFIG(qml_delegate_model) -#include <QtQml/private/qqmldelegatemodel_p.h> -#endif - -QT_BEGIN_NAMESPACE - -QQmlInstantiatorPrivate::QQmlInstantiatorPrivate() - : componentComplete(true) - , effectiveReset(false) - , active(true) - , async(false) -#if QT_CONFIG(qml_delegate_model) - , ownModel(false) -#endif - , requestedIndex(-1) - , model(QVariant(1)) - , instanceModel(nullptr) - , delegate(nullptr) -{ -} - -QQmlInstantiatorPrivate::~QQmlInstantiatorPrivate() -{ - qDeleteAll(objects); -} - -void QQmlInstantiatorPrivate::clear() -{ - Q_Q(QQmlInstantiator); - if (!instanceModel) - return; - if (!objects.count()) - return; - - for (int i=0; i < objects.count(); i++) { - q->objectRemoved(i, objects[i]); - instanceModel->release(objects[i]); - } - objects.clear(); - q->objectChanged(); -} - -QObject *QQmlInstantiatorPrivate::modelObject(int index, bool async) -{ - requestedIndex = index; - QObject *o = instanceModel->object(index, async ? QQmlIncubator::Asynchronous : QQmlIncubator::AsynchronousIfNested); - requestedIndex = -1; - return o; -} - - -void QQmlInstantiatorPrivate::regenerate() -{ - Q_Q(QQmlInstantiator); - if (!componentComplete) - return; - - int prevCount = q->count(); - - clear(); - - if (!active || !instanceModel || !instanceModel->count() || !instanceModel->isValid()) { - if (prevCount) - q->countChanged(); - return; - } - - for (int i = 0; i < instanceModel->count(); i++) { - QObject *object = modelObject(i, async); - // If the item was already created we won't get a createdItem - if (object) - _q_createdItem(i, object); - } - if (q->count() != prevCount) - q->countChanged(); -} - -void QQmlInstantiatorPrivate::_q_createdItem(int idx, QObject* item) -{ - Q_Q(QQmlInstantiator); - if (objects.contains(item)) //Case when it was created synchronously in regenerate - return; - if (requestedIndex != idx) // Asynchronous creation, reference the object - (void)instanceModel->object(idx); - item->setParent(q); - if (objects.size() < idx + 1) { - int modelCount = instanceModel->count(); - if (objects.capacity() < modelCount) - objects.reserve(modelCount); - objects.resize(idx + 1); - } - if (QObject *o = objects.at(idx)) - instanceModel->release(o); - objects.replace(idx, item); - if (objects.count() == 1) - q->objectChanged(); - q->objectAdded(idx, item); -} - -void QQmlInstantiatorPrivate::_q_modelUpdated(const QQmlChangeSet &changeSet, bool reset) -{ - Q_Q(QQmlInstantiator); - - if (!componentComplete || effectiveReset) - return; - - if (reset) { - regenerate(); - if (changeSet.difference() != 0) - q->countChanged(); - return; - } - - int difference = 0; - QHash<int, QVector<QPointer<QObject> > > moved; - const QVector<QQmlChangeSet::Change> &removes = changeSet.removes(); - for (const QQmlChangeSet::Change &remove : removes) { - int index = qMin(remove.index, objects.count()); - int count = qMin(remove.index + remove.count, objects.count()) - index; - if (remove.isMove()) { - moved.insert(remove.moveId, objects.mid(index, count)); - objects.erase( - objects.begin() + index, - objects.begin() + index + count); - } else while (count--) { - QObject *obj = objects.at(index); - objects.remove(index); - q->objectRemoved(index, obj); - if (obj) - instanceModel->release(obj); - } - - difference -= remove.count; - } - - const QVector<QQmlChangeSet::Change> &inserts = changeSet.inserts(); - for (const QQmlChangeSet::Change &insert : inserts) { - int index = qMin(insert.index, objects.count()); - if (insert.isMove()) { - QVector<QPointer<QObject> > movedObjects = moved.value(insert.moveId); - objects = objects.mid(0, index) + movedObjects + objects.mid(index); - } else { - if (insert.index <= objects.size()) - objects.insert(insert.index, insert.count, nullptr); - for (int i = 0; i < insert.count; ++i) { - int modelIndex = index + i; - QObject* obj = modelObject(modelIndex, async); - if (obj) - _q_createdItem(modelIndex, obj); - } - } - difference += insert.count; - } - - if (difference != 0) - q->countChanged(); -} - -#if QT_CONFIG(qml_delegate_model) -void QQmlInstantiatorPrivate::makeModel() -{ - Q_Q(QQmlInstantiator); - QQmlDelegateModel* delegateModel = new QQmlDelegateModel(qmlContext(q), q); - instanceModel = delegateModel; - ownModel = true; - delegateModel->setDelegate(delegate); - delegateModel->classBegin(); //Pretend it was made in QML - if (componentComplete) - delegateModel->componentComplete(); -} -#endif - - -/*! - \qmltype Instantiator - \instantiates QQmlInstantiator - \inqmlmodule QtQml - \brief Dynamically creates objects. - - A Instantiator can be used to control the dynamic creation of objects, or to dynamically - create multiple objects from a template. - - The Instantiator element will manage the objects it creates. Those objects are parented to the - Instantiator and can also be deleted by the Instantiator if the Instantiator's properties change. Objects - can also be destroyed dynamically through other means, and the Instantiator will not recreate - them unless the properties of the Instantiator change. - -*/ -QQmlInstantiator::QQmlInstantiator(QObject *parent) - : QObject(*(new QQmlInstantiatorPrivate), parent) -{ -} - -QQmlInstantiator::~QQmlInstantiator() -{ -} - -/*! - \qmlsignal QtQml::Instantiator::objectAdded(int index, QtObject object) - - This signal is emitted when an object is added to the Instantiator. The \a index - parameter holds the index which the object has been given, and the \a object - parameter holds the \l QtObject that has been added. - - The corresponding handler is \c onObjectAdded. -*/ - -/*! - \qmlsignal QtQml::Instantiator::objectRemoved(int index, QtObject object) - - This signal is emitted when an object is removed from the Instantiator. The \a index - parameter holds the index which the object had been given, and the \a object - parameter holds the \l QtObject that has been removed. - - Do not keep a reference to \a object if it was created by this Instantiator, as - in these cases it will be deleted shortly after the signal is handled. - - The corresponding handler is \c onObjectRemoved. -*/ -/*! - \qmlproperty bool QtQml::Instantiator::active - - When active is true, and the delegate component is ready, the Instantiator will - create objects according to the model. When active is false, no objects - will be created and any previously created objects will be destroyed. - - Default is true. -*/ -bool QQmlInstantiator::isActive() const -{ - Q_D(const QQmlInstantiator); - return d->active; -} - -void QQmlInstantiator::setActive(bool newVal) -{ - Q_D(QQmlInstantiator); - if (newVal == d->active) - return; - d->active = newVal; - emit activeChanged(); - d->regenerate(); -} - -/*! - \qmlproperty bool QtQml::Instantiator::asynchronous - - When asynchronous is true the Instantiator will attempt to create objects - asynchronously. This means that objects may not be available immediately, - even if active is set to true. - - You can use the objectAdded signal to respond to items being created. - - Default is false. -*/ -bool QQmlInstantiator::isAsync() const -{ - Q_D(const QQmlInstantiator); - return d->async; -} - -void QQmlInstantiator::setAsync(bool newVal) -{ - Q_D(QQmlInstantiator); - if (newVal == d->async) - return; - d->async = newVal; - emit asynchronousChanged(); -} - - -/*! - \qmlproperty int QtQml::Instantiator::count - - The number of objects the Instantiator is currently managing. -*/ - -int QQmlInstantiator::count() const -{ - Q_D(const QQmlInstantiator); - return d->objects.count(); -} - -/*! - \qmlproperty QtQml::Component QtQml::Instantiator::delegate - \default - - The component used to create all objects. - - Note that an extra variable, index, will be available inside instances of the - delegate. This variable refers to the index of the instance inside the Instantiator, - and can be used to obtain the object through the objectAt method of the Instantiator. - - If this property is changed, all instances using the old delegate will be destroyed - and new instances will be created using the new delegate. -*/ -QQmlComponent* QQmlInstantiator::delegate() -{ - Q_D(QQmlInstantiator); - return d->delegate; -} - -void QQmlInstantiator::setDelegate(QQmlComponent* c) -{ - Q_D(QQmlInstantiator); - if (c == d->delegate) - return; - - d->delegate = c; - emit delegateChanged(); - -#if QT_CONFIG(qml_delegate_model) - if (!d->ownModel) - return; - - if (QQmlDelegateModel *dModel = qobject_cast<QQmlDelegateModel*>(d->instanceModel)) - dModel->setDelegate(c); - if (d->componentComplete) - d->regenerate(); -#endif -} - -/*! - \qmlproperty variant QtQml::Instantiator::model - - This property can be set to any of the supported \l {qml-data-models}{data models}: - - \list - \li A number that indicates the number of delegates to be created by the repeater - \li A model (e.g. a ListModel item, or a QAbstractItemModel subclass) - \li A string list - \li An object list - \endlist - - The type of model affects the properties that are exposed to the \l delegate. - - Default value is 1, which creates a single delegate instance. - - \sa {qml-data-models}{Data Models} -*/ - -QVariant QQmlInstantiator::model() const -{ - Q_D(const QQmlInstantiator); - return d->model; -} - -void QQmlInstantiator::setModel(const QVariant &v) -{ - Q_D(QQmlInstantiator); - if (d->model == v) - return; - - d->model = v; - //Don't actually set model until componentComplete in case it wants to create its delegates immediately - if (!d->componentComplete) - return; - - QQmlInstanceModel *prevModel = d->instanceModel; - QObject *object = qvariant_cast<QObject*>(v); - QQmlInstanceModel *vim = nullptr; - if (object && (vim = qobject_cast<QQmlInstanceModel *>(object))) { -#if QT_CONFIG(qml_delegate_model) - if (d->ownModel) { - delete d->instanceModel; - prevModel = nullptr; - d->ownModel = false; - } -#endif - d->instanceModel = vim; -#if QT_CONFIG(qml_delegate_model) - } else if (v != QVariant(0)){ - if (!d->ownModel) - d->makeModel(); - - if (QQmlDelegateModel *dataModel = qobject_cast<QQmlDelegateModel *>(d->instanceModel)) { - d->effectiveReset = true; - dataModel->setModel(v); - d->effectiveReset = false; - } -#endif - } - - if (d->instanceModel != prevModel) { - if (prevModel) { - disconnect(prevModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), - this, SLOT(_q_modelUpdated(QQmlChangeSet,bool))); - disconnect(prevModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*))); - //disconnect(prevModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); - } - - if (d->instanceModel) { - connect(d->instanceModel, SIGNAL(modelUpdated(QQmlChangeSet,bool)), - this, SLOT(_q_modelUpdated(QQmlChangeSet,bool))); - connect(d->instanceModel, SIGNAL(createdItem(int,QObject*)), this, SLOT(_q_createdItem(int,QObject*))); - //connect(d->instanceModel, SIGNAL(initItem(int,QObject*)), this, SLOT(initItem(int,QObject*))); - } - } - - d->regenerate(); - emit modelChanged(); -} - -/*! - \qmlproperty QtObject QtQml::Instantiator::object - - This is a reference to the first created object, intended as a convenience - for the case where only one object has been created. -*/ -QObject *QQmlInstantiator::object() const -{ - Q_D(const QQmlInstantiator); - if (d->objects.count()) - return d->objects[0]; - return nullptr; -} - -/*! - \qmlmethod QtObject QtQml::Instantiator::objectAt(int index) - - Returns a reference to the object with the given \a index. -*/ -QObject *QQmlInstantiator::objectAt(int index) const -{ - Q_D(const QQmlInstantiator); - if (index >= 0 && index < d->objects.count()) - return d->objects[index]; - return nullptr; -} - -/*! - \internal -*/ -void QQmlInstantiator::classBegin() -{ - Q_D(QQmlInstantiator); - d->componentComplete = false; -} - -/*! - \internal -*/ -void QQmlInstantiator::componentComplete() -{ - Q_D(QQmlInstantiator); - d->componentComplete = true; -#if QT_CONFIG(qml_delegate_model) - if (d->ownModel) { - static_cast<QQmlDelegateModel*>(d->instanceModel)->componentComplete(); - d->regenerate(); - } else -#endif - { - QVariant realModel = d->model; - d->model = QVariant(0); - setModel(realModel); //If realModel == d->model this won't do anything, but that's fine since the model's 0 - //setModel calls regenerate - } -} - -QT_END_NAMESPACE - -#include "moc_qqmlinstantiator_p.cpp" diff --git a/src/qml/types/qqmlinstantiator_p.h b/src/qml/types/qqmlinstantiator_p.h deleted file mode 100644 index ca371adc23..0000000000 --- a/src/qml/types/qqmlinstantiator_p.h +++ /dev/null @@ -1,119 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLINSTANTIATOR_P_H -#define QQMLINSTANTIATOR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtQml/qqmlcomponent.h> -#include <QtQml/qqmlparserstatus.h> -#include <QtQml/private/qtqmlglobal_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlInstantiatorPrivate; -class Q_QML_PRIVATE_EXPORT QQmlInstantiator : public QObject, public QQmlParserStatus -{ - Q_OBJECT - Q_INTERFACES(QQmlParserStatus) - - Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) - Q_PROPERTY(bool asynchronous READ isAsync WRITE setAsync NOTIFY asynchronousChanged) - Q_PROPERTY(QVariant model READ model WRITE setModel NOTIFY modelChanged) - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) - Q_PROPERTY(QObject *object READ object NOTIFY objectChanged) - Q_CLASSINFO("DefaultProperty", "delegate") - -public: - QQmlInstantiator(QObject *parent = nullptr); - ~QQmlInstantiator(); - - bool isActive() const; - void setActive(bool newVal); - - bool isAsync() const; - void setAsync(bool newVal); - - int count() const; - - QQmlComponent* delegate(); - void setDelegate(QQmlComponent* c); - - QVariant model() const; - void setModel(const QVariant &v); - - QObject *object() const; - - Q_INVOKABLE QObject *objectAt(int index) const; - - void classBegin() override; - void componentComplete() override; - -Q_SIGNALS: - void modelChanged(); - void delegateChanged(); - void countChanged(); - void objectChanged(); - void activeChanged(); - void asynchronousChanged(); - - void objectAdded(int index, QObject* object); - void objectRemoved(int index, QObject* object); - -private: - Q_DISABLE_COPY(QQmlInstantiator) - Q_DECLARE_PRIVATE(QQmlInstantiator) - Q_PRIVATE_SLOT(d_func(), void _q_createdItem(int, QObject *)) - Q_PRIVATE_SLOT(d_func(), void _q_modelUpdated(const QQmlChangeSet &, bool)) -}; - -QT_END_NAMESPACE - -#endif // QQMLCREATOR_P_H diff --git a/src/qml/types/qqmlinstantiator_p_p.h b/src/qml/types/qqmlinstantiator_p_p.h deleted file mode 100644 index 4c76d5c689..0000000000 --- a/src/qml/types/qqmlinstantiator_p_p.h +++ /dev/null @@ -1,98 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLINSTANTIATOR_P_P_H -#define QQMLINSTANTIATOR_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmlinstantiator_p.h" -#include <QObject> -#include <private/qobject_p.h> -#include <private/qqmlchangeset_p.h> -#include <private/qqmlobjectmodel_p.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlInstantiatorPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQmlInstantiator) - -public: - QQmlInstantiatorPrivate(); - ~QQmlInstantiatorPrivate(); - - void clear(); - void regenerate(); -#if QT_CONFIG(qml_delegate_model) - void makeModel(); -#endif - void _q_createdItem(int, QObject *); - void _q_modelUpdated(const QQmlChangeSet &, bool); - QObject *modelObject(int index, bool async); - - static QQmlInstantiatorPrivate *get(QQmlInstantiator *instantiator) { return instantiator->d_func(); } - static const QQmlInstantiatorPrivate *get(const QQmlInstantiator *instantiator) { return instantiator->d_func(); } - - bool componentComplete:1; - bool effectiveReset:1; - bool active:1; - bool async:1; -#if QT_CONFIG(qml_delegate_model) - bool ownModel:1; -#endif - int requestedIndex; - QVariant model; - QQmlInstanceModel *instanceModel; - QQmlComponent *delegate; - QVector<QPointer<QObject> > objects; -}; - -QT_END_NAMESPACE - -#endif // QQMLCREATOR_P_P_H diff --git a/src/qml/types/qqmlitemmodels.qdoc b/src/qml/types/qqmlitemmodels.qdoc deleted file mode 100644 index f6e1b0b1b9..0000000000 --- a/src/qml/types/qqmlitemmodels.qdoc +++ /dev/null @@ -1,110 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \page qmodelindex-and-related-classes-in-qml.html - \title QModelIndex and related Classes in QML - - Since Qt 5.5, QModelIndex and QPersistentModelIndex are exposed in QML as - value-based types. Also exposed in a similar fashion are QModelIndexList, - QItemSelectionRange and QItemSelection. All objects from these types can - be passed back and forth between QML and C++ as \c var properties or plain - JavaScript variables. - - Below you will find an overview of the API exposed to QML for these classes. - For more information, refer to their C++ documentation. - - \note Since all these types are exposed as \l{Q_GADGET}{gadgets}, there are no property - change notification signals emitted. Therefore binding to their properties - may not give the expected results. This is especially true for QPersistentModelIndex. - - \section1 QModelIndex and QPersistentModelIndex Types - - \list - \li \b row : int - \li \b column : int - \li \b parent : QModelIndex - \li \b valid : bool - \li \b model : QAbstractItemModel - \li \b internalId : quint64 - \endlist - - All these properties are read-only, as are their C++ counterparts. - - \note The usual caveats apply to QModelIndex in QML. If the underlying model changes - or gets deleted, it may become dangerous to access its properties. Therefore, you - should not store any QModelIndex objects. You can, however, store QPersistentModelIndexe - objects in a safe way. - - \section1 QModelIndexList Type - - \l QModelIndexList is exposed in QML as a JavaScript array. Conversions are - automatically made from and to C++. In fact, any JavaScript array can be - converted back to QModelIndexList, with non-QModelIndex objects replaced by - invalid \l{QModelIndex}es. - - \note QModelIndex to QPersistentModelIndex conversion happens when accessing - the array elements because any QModelIndexList property retains reference - semantics when exposed this way. - - \section1 \l QItemSelectionRange Type - - \list - \li \b top : int - \li \b left : int - \li \b bottom : int - \li \b right : int - \li \b width : int - \li \b height : int - \li \b topLeft : QPersistentModelIndex - \li \b bottomRight : QPersistentModelIndex - \li \b parent : QModelIndex - \li \b valid : bool - \li \b empty : bool - \li \b model : QAbstractItemModel - \endlist - - All these properties are read-only, as are their C++ counterparts. In addition, - we also expose the following functions: - - \list - \li bool \b{contains}(QModelIndex \e index) - \li bool \b{contains}(int \e row, int \e column, QModelIndex \e parentIndex) - \li bool \b{intersects}(QItemSelectionRange \e other) - \li QItemSelectionRange \b{intersected}(QItemSelectionRange \e other) - \endlist - - \section1 QItemSelection Type - - Similarly to QModelIndexList, \l QItemSelection is exposed in QML as a JavaScript - array of QItemSelectionRange objects. Conversions are automatically made from and to C++. - In fact, any JavaScript array can be converted back to QItemSelection, with - non-QItemSelectionRange objects replaced by empty \l {QItemSelectionRange}s. - - - \sa ItemSelectionModel -*/ diff --git a/src/qml/types/qqmlitemselectionmodel.qdoc b/src/qml/types/qqmlitemselectionmodel.qdoc deleted file mode 100644 index 43da4f7a55..0000000000 --- a/src/qml/types/qqmlitemselectionmodel.qdoc +++ /dev/null @@ -1,239 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the documentation of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:FDL$ -** 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU Free Documentation License Usage -** Alternatively, this file may be used under the terms of the GNU Free -** Documentation License version 1.3 as published by the Free Software -** Foundation and appearing in the file included in the packaging of -** this file. Please review the following information to ensure -** the GNU Free Documentation License version 1.3 requirements -** will be met: https://www.gnu.org/licenses/fdl-1.3.html. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -/*! - \qmltype ItemSelectionModel - \instantiates QItemSelectionModel - \inqmlmodule QtQml.Models - \since 5.5 - \ingroup qtquick-models - - \brief Instantiates a QItemSelectionModel to be used in conjunction - with a QAbstractItemModel and any view supporting it. - - \sa QItemSelectionModel, {Models and Views in Qt Quick} -*/ - - -/*! - \qmlproperty QAbstractItemModel ItemSelectionModel::model - - This property's value must match the view's model. -*/ - -/*! - \qmlproperty bool ItemSelectionModel::hasSelection - \readonly - - It will trigger property binding updates every time \l selectionChanged() - is emitted, even though its value hasn't changed. - - \sa selection, selectedIndexes, select(), selectionChanged() -*/ - -/*! - \qmlproperty QModelIndex ItemSelectionModel::currentIndex - \readonly - - Use \l setCurrentIndex() to set its value. - - \sa setCurrentIndex(), currentChanged() -*/ - -/*! - \qmlproperty QModelIndexList ItemSelectionModel::selectedIndexes - \readonly - - Contains the list of all the indexes in the selection model. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::isSelected(QModelIndex index) - - Returns \c true if the given model item \a index is selected. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::isRowSelected(int row, QModelIndex parent) - - Returns \c true if all items are selected in the \a row with the given - \a parent. - - Note that this function is usually faster than calling isSelected() - on all items in the same row, and that unselectable items are ignored. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::isColumnSelected(int column, QModelIndex parent) - - Returns \c true if all items are selected in the \a column with the given - \a parent. - - Note that this function is usually faster than calling isSelected() - on all items in the same column, and that unselectable items are ignored. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::rowIntersectsSelection(int row, QModelIndex parent) - - Returns \c true if there are any items selected in the \a row with the - given \a parent. -*/ - -/*! - \qmlmethod bool ItemSelectionModel::columnIntersectsSelection(int column, QModelIndex parent) - - Returns \c true if there are any items selected in the \a column with the - given \a parent. -*/ - -/*! - \qmlmethod QModelIndexList ItemSelectionModel::selectedRows(int column) - - Returns the indexes in the given \a column for the rows where all columns - are selected. - - \sa selectedColumns() -*/ - -/*! - \qmlmethod QModelIndexList ItemSelectionModel::selectedColumns(int row) - - Returns the indexes in the given \a row for columns where all rows are - selected. - - \sa selectedRows() -*/ - -/*! - \qmlproperty object ItemSelectionModel::selection - \readonly - - Holds the selection ranges stored in the selection model. -*/ - -/*! - \qmlmethod void ItemSelectionModel::setCurrentIndex(QModelIndex index, SelectionFlags command) - - Sets the model item \a index to be the current item, and emits - currentChanged(). The current item is used for keyboard navigation and - focus indication; it is independent of any selected items, although a - selected item can also be the current item. - - Depending on the specified \a command, the \a index can also become part - of the current selection. - - Valid \a command values are described in \l {itemselectionmodelselectindex} - {select(\e index, \e command)}. - - \sa select() -*/ - -/*! - \qmlmethod void ItemSelectionModel::select(QModelIndex index, SelectionFlags command) - \keyword itemselectionmodelselectindex - - Selects the model item \a index using the specified \a command, and emits - selectionChanged(). - - Valid values for the \a command parameter, are: - - \value NoUpdate No selection will be made. - \value Clear The complete selection will be cleared. - \value Select All specified indexes will be selected. - \value Deselect All specified indexes will be deselected. - \value Toggle All specified indexes will be selected or - deselected depending on their current state. - \value Current The current selection will be updated. - \value Rows All indexes will be expanded to span rows. - \value Columns All indexes will be expanded to span columns. - \value SelectCurrent A combination of Select and Current, provided for - convenience. - \value ToggleCurrent A combination of Toggle and Current, provided for - convenience. - \value ClearAndSelect A combination of Clear and Select, provided for - convenience. -*/ - -/*! - \qmlmethod void ItemSelectionModel::select(QItemSelection selection, SelectionFlags command) - - Selects the item \a selection using the specified \a command, and emits - selectionChanged(). - - Valid \a command values are described in \l {itemselectionmodelselectindex} - {select(\e index, \e command)}. -*/ - -/*! - \qmlmethod void ItemSelectionModel::clear() - - Clears the selection model. Emits selectionChanged() and currentChanged(). -*/ - -/*! - \qmlmethod void ItemSelectionModel::reset() - - Clears the selection model. Does not emit any signals. -*/ - -/*! - \qmlmethod void ItemSelectionModel::clearSelection() - - Clears the selection in the selection model. Emits selectionChanged(). -*/ - -/*! - \qmlmethod void ItemSelectionModel::clearCurrentIndex() - - Clears the current index. Emits currentChanged(). -*/ - -/*! - \qmlsignal ItemSelectionModel::selectionChanged(QItemSelection selected, QItemSelection deselected) - - This signal is emitted whenever the selection changes. The change in the - selection is represented as an item selection of \a deselected items and - an item selection of \a selected items. - - Note the that the current index changes independently from the selection. - Also note that this signal will not be emitted when the item model is reset. - - \sa select(), currentChanged() -*/ - -/*! - \qmlsignal ItemSelectionModel::currentChanged(QModelIndex current, QModelIndex previous) - - This signal is emitted whenever the current item changes. The \a previous - model item index is replaced by the \a current index as the selection's - current item. - - Note that this signal will not be emitted when the item model is reset. - - \sa currentIndex, setCurrentIndex(), selectionChanged() -*/ diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp deleted file mode 100644 index 5b5bcd8464..0000000000 --- a/src/qml/types/qqmllistmodel.cpp +++ /dev/null @@ -1,2900 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmllistmodel_p_p.h" -#include "qqmllistmodelworkeragent_p.h" -#include <private/qqmlopenmetaobject_p.h> -#include <private/qqmljsast_p.h> -#include <private/qqmljsengine_p.h> -#include <private/qjsvalue_p.h> - -#include <private/qqmlcustomparser_p.h> -#include <private/qqmlengine_p.h> -#include <private/qqmlnotifier_p.h> - -#include <private/qv4object_p.h> -#include <private/qv4dateobject_p.h> -#include <private/qv4objectiterator_p.h> -#include <private/qv4alloca_p.h> -#include <private/qv4lookup_p.h> - -#include <qqmlcontext.h> -#include <qqmlinfo.h> - -#include <QtCore/qdebug.h> -#include <QtCore/qstack.h> -#include <QXmlStreamReader> -#include <QtCore/qdatetime.h> -#include <QScopedValueRollback> - -Q_DECLARE_METATYPE(const QV4::CompiledData::Binding*); - -QT_BEGIN_NAMESPACE - -// Set to 1024 as a debugging aid - easier to distinguish uids from indices of elements/models. -enum { MIN_LISTMODEL_UID = 1024 }; - -static QAtomicInt uidCounter(MIN_LISTMODEL_UID); - -template <typename T> -static bool isMemoryUsed(const char *mem) -{ - for (size_t i=0 ; i < sizeof(T) ; ++i) { - if (mem[i] != 0) - return true; - } - - return false; -} - -static QString roleTypeName(ListLayout::Role::DataType t) -{ - static const QString roleTypeNames[] = { - QStringLiteral("String"), QStringLiteral("Number"), QStringLiteral("Bool"), - QStringLiteral("List"), QStringLiteral("QObject"), QStringLiteral("VariantMap"), - QStringLiteral("DateTime"), QStringLiteral("Function") - }; - - if (t > ListLayout::Role::Invalid && t < ListLayout::Role::MaxDataType) - return roleTypeNames[t]; - - return QString(); -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(const QString &key, Role::DataType type) -{ - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlWarning(nullptr) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - return createRole(key, type); -} - -const ListLayout::Role &ListLayout::getRoleOrCreate(QV4::String *key, Role::DataType type) -{ - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) { - const Role &r = *node->value; - if (type != r.type) - qmlWarning(nullptr) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(r.name).arg(roleTypeName(type)).arg(roleTypeName(r.type)); - return r; - } - - QString qkey = key->toQString(); - - return createRole(qkey, type); -} - -const ListLayout::Role &ListLayout::createRole(const QString &key, ListLayout::Role::DataType type) -{ - const int dataSizes[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QPointer<QObject>), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; - const int dataAlignments[] = { sizeof(StringOrTranslation), sizeof(double), sizeof(bool), sizeof(ListModel *), sizeof(QObject *), sizeof(QVariantMap), sizeof(QDateTime), sizeof(QJSValue) }; - - Role *r = new Role; - r->name = key; - r->type = type; - - if (type == Role::List) { - r->subLayout = new ListLayout; - } else { - r->subLayout = nullptr; - } - - int dataSize = dataSizes[type]; - int dataAlignment = dataAlignments[type]; - - int dataOffset = (currentBlockOffset + dataAlignment-1) & ~(dataAlignment-1); - if (dataOffset + dataSize > ListElement::BLOCK_SIZE) { - r->blockIndex = ++currentBlock; - r->blockOffset = 0; - currentBlockOffset = dataSize; - } else { - r->blockIndex = currentBlock; - r->blockOffset = dataOffset; - currentBlockOffset = dataOffset + dataSize; - } - - int roleIndex = roles.count(); - r->index = roleIndex; - - roles.append(r); - roleHash.insert(key, r); - - return *r; -} - -ListLayout::ListLayout(const ListLayout *other) : currentBlock(0), currentBlockOffset(0) -{ - const int otherRolesCount = other->roles.count(); - roles.reserve(otherRolesCount); - for (int i=0 ; i < otherRolesCount; ++i) { - Role *role = new Role(other->roles[i]); - roles.append(role); - roleHash.insert(role->name, role); - } - currentBlockOffset = other->currentBlockOffset; - currentBlock = other->currentBlock; -} - -ListLayout::~ListLayout() -{ - qDeleteAll(roles); -} - -void ListLayout::sync(ListLayout *src, ListLayout *target) -{ - int roleOffset = target->roles.count(); - int newRoleCount = src->roles.count() - roleOffset; - - for (int i=0 ; i < newRoleCount ; ++i) { - Role *role = new Role(src->roles[roleOffset + i]); - target->roles.append(role); - target->roleHash.insert(role->name, role); - } - - target->currentBlockOffset = src->currentBlockOffset; - target->currentBlock = src->currentBlock; -} - -ListLayout::Role::Role(const Role *other) -{ - name = other->name; - type = other->type; - blockIndex = other->blockIndex; - blockOffset = other->blockOffset; - index = other->index; - if (other->subLayout) - subLayout = new ListLayout(other->subLayout); - else - subLayout = nullptr; -} - -ListLayout::Role::~Role() -{ - delete subLayout; -} - -const ListLayout::Role *ListLayout::getRoleOrCreate(const QString &key, const QVariant &data) -{ - Role::DataType type; - - switch (data.type()) { - case QVariant::Double: type = Role::Number; break; - case QVariant::Int: type = Role::Number; break; - case QVariant::Bool: type = Role::Bool; break; - case QVariant::String: type = Role::String; break; - case QVariant::Map: type = Role::VariantMap; break; - case QVariant::DateTime: type = Role::DateTime; break; - case QVariant::UserType: { - if (data.userType() == qMetaTypeId<QJSValue>() && - data.value<QJSValue>().isCallable()) { - type = Role::Function; - break; - } else if (data.userType() == qMetaTypeId<const QV4::CompiledData::Binding*>() - && data.value<const QV4::CompiledData::Binding*>()->isTranslationBinding()) { - type = Role::String; - break; - } else { - type = Role::List; - break; - } - } - default: type = Role::Invalid; break; - } - - if (type == Role::Invalid) { - qmlWarning(nullptr) << "Can't create role for unsupported data type"; - return nullptr; - } - - return &getRoleOrCreate(key, type); -} - -const ListLayout::Role *ListLayout::getExistingRole(const QString &key) const -{ - Role *r = nullptr; - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) - r = node->value; - return r; -} - -const ListLayout::Role *ListLayout::getExistingRole(QV4::String *key) const -{ - Role *r = nullptr; - QStringHash<Role *>::Node *node = roleHash.findNode(key); - if (node) - r = node->value; - return r; -} - -StringOrTranslation::StringOrTranslation(const QString &s) -{ - d.setFlag(); - setString(s); -} - -StringOrTranslation::StringOrTranslation(const QV4::CompiledData::Binding *binding) -{ - d.setFlag(); - clear(); - d = binding; -} - -StringOrTranslation::~StringOrTranslation() -{ - clear(); -} - -void StringOrTranslation::setString(const QString &s) -{ - d.setFlag(); - clear(); - QStringData *stringData = const_cast<QString &>(s).data_ptr(); - d = stringData; - if (stringData) - stringData->ref.ref(); -} - -void StringOrTranslation::setTranslation(const QV4::CompiledData::Binding *binding) -{ - d.setFlag(); - clear(); - d = binding; -} - -QString StringOrTranslation::toString(const QQmlListModel *owner) const -{ - if (d.isNull()) - return QString(); - if (d.isT1()) { - QStringDataPtr holder = { d.asT1() }; - holder.ptr->ref.ref(); - return QString(holder); - } - if (!owner) - return QString(); - return d.asT2()->valueAsString(owner->m_compilationUnit.data()); -} - -QString StringOrTranslation::asString() const -{ - if (d.isNull()) - return QString(); - if (!d.isT1()) - return QString(); - QStringDataPtr holder = { d.asT1() }; - holder.ptr->ref.ref(); - return QString(holder); -} - -void StringOrTranslation::clear() -{ - if (QStringData *strData = d.isT1() ? d.asT1() : nullptr) { - if (!strData->ref.deref()) - QStringData::deallocate(strData); - } - d = static_cast<QStringData *>(nullptr); -} - -QObject *ListModel::getOrCreateModelObject(QQmlListModel *model, int elementIndex) -{ - ListElement *e = elements[elementIndex]; - if (e->m_objectCache == nullptr) { - void *memory = operator new(sizeof(QObject) + sizeof(QQmlData)); - void *ddataMemory = ((char *)memory) + sizeof(QObject); - e->m_objectCache = new (memory) QObject; - QQmlData *ddata = new (ddataMemory) QQmlData; - ddata->ownMemory = false; - QObjectPrivate::get(e->m_objectCache)->declarativeData = ddata; - (void)new ModelNodeMetaObject(e->m_objectCache, model, elementIndex); - } - return e->m_objectCache; -} - -bool ListModel::sync(ListModel *src, ListModel *target) -{ - // Sanity check - - bool hasChanges = false; - - // Build hash of elements <-> uid for each of the lists - QHash<int, ElementSync> elementHash; - for (int i = 0; i < target->elements.count(); ++i) { - ListElement *e = target->elements.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - sync.targetIndex = i; - elementHash.insert(uid, sync); - } - for (int i = 0; i < src->elements.count(); ++i) { - ListElement *e = src->elements.at(i); - int uid = e->getUid(); - - QHash<int, ElementSync>::iterator it = elementHash.find(uid); - if (it == elementHash.end()) { - ElementSync sync; - sync.src = e; - sync.srcIndex = i; - elementHash.insert(uid, sync); - } else { - ElementSync &sync = it.value(); - sync.src = e; - sync.srcIndex = i; - } - } - - QQmlListModel *targetModel = target->m_modelCache; - - // Get list of elements that are in the target but no longer in the source. These get deleted first. - int rowsRemoved = 0; - for (int i = 0 ; i < target->elements.count() ; ++i) { - ListElement *element = target->elements.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.targetIndex >= 0); - // need to update the targetIndex, to keep it correct after removals - s.targetIndex -= rowsRemoved; - if (s.src == nullptr) { - Q_ASSERT(s.targetIndex == i); - hasChanges = true; - if (targetModel) - targetModel->beginRemoveRows(QModelIndex(), i, i); - s.target->destroy(target->m_layout); - target->elements.removeOne(s.target); - delete s.target; - if (targetModel) - targetModel->endRemoveRows(); - ++rowsRemoved; - --i; - continue; - } - } - - // Sync the layouts - ListLayout::sync(src->m_layout, target->m_layout); - - // Clear the target list, and append in correct order from the source - target->elements.clear(); - for (int i = 0; i < src->elements.count(); ++i) { - ListElement *srcElement = src->elements.at(i); - ElementSync &s = elementHash.find(srcElement->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - ListElement *targetElement = s.target; - if (targetElement == nullptr) { - targetElement = new ListElement(srcElement->getUid()); - } - s.changedRoles = ListElement::sync(srcElement, src->m_layout, targetElement, target->m_layout); - target->elements.append(targetElement); - } - - target->updateCacheIndices(); - - // Update values stored in target meta objects - for (int i=0 ; i < target->elements.count() ; ++i) { - ListElement *e = target->elements[i]; - if (ModelNodeMetaObject *mo = e->objectCache()) - mo->updateValues(); - } - - // now emit the change notifications required. This can be safely done, as we're only emitting changes, moves and inserts, - // so the model indices can't be out of bounds - // - // to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent - // model indices are updated correctly - int rowsInserted = 0; - for (int i = 0 ; i < target->elements.count() ; ++i) { - ListElement *element = target->elements.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - s.srcIndex += rowsInserted; - if (s.srcIndex != s.targetIndex) { - if (targetModel) { - if (s.targetIndex == -1) { - targetModel->beginInsertRows(QModelIndex(), i, i); - targetModel->endInsertRows(); - } else { - targetModel->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex); - targetModel->endMoveRows(); - } - } - hasChanges = true; - ++rowsInserted; - } - if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) { - QModelIndex idx = targetModel->createIndex(i, 0); - if (targetModel) - targetModel->dataChanged(idx, idx, s.changedRoles); - hasChanges = true; - } - } - return hasChanges; -} - -ListModel::ListModel(ListLayout *layout, QQmlListModel *modelCache) : m_layout(layout), m_modelCache(modelCache) -{ -} - -void ListModel::destroy() -{ - for (const auto &destroyer : remove(0, elements.count())) - destroyer(); - - m_layout = nullptr; - if (m_modelCache && m_modelCache->m_primary == false) - delete m_modelCache; - m_modelCache = nullptr; -} - -int ListModel::appendElement() -{ - int elementIndex = elements.count(); - newElement(elementIndex); - return elementIndex; -} - -void ListModel::insertElement(int index) -{ - newElement(index); - updateCacheIndices(index); -} - -void ListModel::move(int from, int to, int n) -{ - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - from = tto; - to = tto+n; - n = tfrom-tto; - } - - QPODVector<ListElement *, 4> store; - for (int i=0 ; i < (to-from) ; ++i) - store.append(elements[from+n+i]); - for (int i=0 ; i < n ; ++i) - store.append(elements[from+i]); - for (int i=0 ; i < store.count() ; ++i) - elements[from+i] = store[i]; - - updateCacheIndices(from, to + n); -} - -void ListModel::newElement(int index) -{ - ListElement *e = new ListElement; - elements.insert(index, e); -} - -void ListModel::updateCacheIndices(int start, int end) -{ - int count = elements.count(); - - if (end < 0 || end > count) - end = count; - - for (int i = start; i < end; ++i) { - ListElement *e = elements.at(i); - if (ModelNodeMetaObject *mo = e->objectCache()) - mo->m_elementIndex = i; - } -} - -QVariant ListModel::getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng) -{ - if (roleIndex >= m_layout->roleCount()) - return QVariant(); - ListElement *e = elements[elementIndex]; - const ListLayout::Role &r = m_layout->getExistingRole(roleIndex); - return e->getProperty(r, owner, eng); -} - -ListModel *ListModel::getListProperty(int elementIndex, const ListLayout::Role &role) -{ - ListElement *e = elements[elementIndex]; - return e->getListProperty(role); -} - -void ListModel::set(int elementIndex, QV4::Object *object, QVector<int> *roles) -{ - ListElement *e = elements[elementIndex]; - - QV4::ExecutionEngine *v4 = object->engine(); - QV4::Scope scope(v4); - QV4::ScopedObject o(scope); - - QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly); - QV4::ScopedString propertyName(scope); - QV4::ScopedValue propertyValue(scope); - while (1) { - propertyName = it.nextPropertyNameAsString(propertyValue); - if (!propertyName) - break; - - // Check if this key exists yet - int roleIndex = -1; - - // Add the value now - if (const QV4::String *s = propertyValue->as<QV4::String>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); - roleIndex = e->setStringProperty(r, s->toQString()); - } else if (propertyValue->isNumber()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); - roleIndex = e->setDoubleProperty(r, propertyValue->asDouble()); - } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - ListModel *subModel = new ListModel(r.subLayout, nullptr); - - int arrayLength = a->getLength(); - for (int j=0 ; j < arrayLength ; ++j) { - o = a->get(j); - subModel->append(o); - } - - roleIndex = e->setListProperty(r, subModel); - } else if (propertyValue->isBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - roleIndex = e->setBoolProperty(r, propertyValue->booleanValue()); - } else if (QV4::DateObject *dd = propertyValue->as<QV4::DateObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - QDateTime dt = dd->toQDateTime(); - roleIndex = e->setDateTimeProperty(r, dt); - } else if (QV4::FunctionObject *f = propertyValue->as<QV4::FunctionObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Function); - QV4::ScopedFunctionObject func(scope, f); - QJSValue jsv; - QJSValuePrivate::setValue(&jsv, v4, func); - roleIndex = e->setFunctionProperty(r, jsv); - } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { - if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { - QObject *o = wrapper->object(); - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); - if (role.type == ListLayout::Role::QObject) - roleIndex = e->setQObjectProperty(role, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) { - QV4::ScopedObject obj(scope, o); - roleIndex = e->setVariantMapProperty(role, obj); - } - } - } else if (propertyValue->isNullOrUndefined()) { - const ListLayout::Role *r = m_layout->getExistingRole(propertyName); - if (r) - e->clearProperty(*r); - } - - if (roleIndex != -1) - roles->append(roleIndex); - } - - if (ModelNodeMetaObject *mo = e->objectCache()) - mo->updateValues(*roles); -} - -void ListModel::set(int elementIndex, QV4::Object *object) -{ - if (!object) - return; - - ListElement *e = elements[elementIndex]; - - QV4::ExecutionEngine *v4 = object->engine(); - QV4::Scope scope(v4); - - QV4::ObjectIterator it(scope, object, QV4::ObjectIterator::EnumerableOnly); - QV4::ScopedString propertyName(scope); - QV4::ScopedValue propertyValue(scope); - QV4::ScopedObject o(scope); - while (1) { - propertyName = it.nextPropertyNameAsString(propertyValue); - if (!propertyName) - break; - - // Add the value now - if (QV4::String *s = propertyValue->stringValue()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::String); - if (r.type == ListLayout::Role::String) - e->setStringPropertyFast(r, s->toQString()); - } else if (propertyValue->isNumber()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Number); - if (r.type == ListLayout::Role::Number) { - e->setDoublePropertyFast(r, propertyValue->asDouble()); - } - } else if (QV4::ArrayObject *a = propertyValue->as<QV4::ArrayObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::List); - if (r.type == ListLayout::Role::List) { - ListModel *subModel = new ListModel(r.subLayout, nullptr); - - int arrayLength = a->getLength(); - for (int j=0 ; j < arrayLength ; ++j) { - o = a->get(j); - subModel->append(o); - } - - e->setListPropertyFast(r, subModel); - } - } else if (propertyValue->isBoolean()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::Bool); - if (r.type == ListLayout::Role::Bool) { - e->setBoolPropertyFast(r, propertyValue->booleanValue()); - } - } else if (QV4::DateObject *date = propertyValue->as<QV4::DateObject>()) { - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::DateTime); - if (r.type == ListLayout::Role::DateTime) { - QDateTime dt = date->toQDateTime();; - e->setDateTimePropertyFast(r, dt); - } - } else if (QV4::Object *o = propertyValue->as<QV4::Object>()) { - if (QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>()) { - QObject *o = wrapper->object(); - const ListLayout::Role &r = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::QObject); - if (r.type == ListLayout::Role::QObject) - e->setQObjectPropertyFast(r, o); - } else { - const ListLayout::Role &role = m_layout->getRoleOrCreate(propertyName, ListLayout::Role::VariantMap); - if (role.type == ListLayout::Role::VariantMap) - e->setVariantMapFast(role, o); - } - } else if (propertyValue->isNullOrUndefined()) { - const ListLayout::Role *r = m_layout->getExistingRole(propertyName); - if (r) - e->clearProperty(*r); - } - } -} - -QVector<std::function<void()>> ListModel::remove(int index, int count) -{ - QVector<std::function<void()>> toDestroy; - auto layout = m_layout; - for (int i=0 ; i < count ; ++i) { - auto element = elements[index+i]; - toDestroy.append([element, layout](){ - element->destroy(layout); - delete element; - }); - } - elements.remove(index, count); - updateCacheIndices(index); - return toDestroy; -} - -void ListModel::insert(int elementIndex, QV4::Object *object) -{ - insertElement(elementIndex); - set(elementIndex, object); -} - -int ListModel::append(QV4::Object *object) -{ - int elementIndex = appendElement(); - set(elementIndex, object); - return elementIndex; -} - -int ListModel::setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data) -{ - int roleIndex = -1; - - if (elementIndex >= 0 && elementIndex < elements.count()) { - ListElement *e = elements[elementIndex]; - - const ListLayout::Role *r = m_layout->getRoleOrCreate(key, data); - if (r) { - roleIndex = e->setVariantProperty(*r, data); - - ModelNodeMetaObject *cache = e->objectCache(); - - if (roleIndex != -1 && cache) - cache->updateValues(QVector<int>(1, roleIndex)); - } - } - - return roleIndex; -} - -int ListModel::setExistingProperty(int elementIndex, const QString &key, const QV4::Value &data, QV4::ExecutionEngine *eng) -{ - int roleIndex = -1; - - if (elementIndex >= 0 && elementIndex < elements.count()) { - ListElement *e = elements[elementIndex]; - const ListLayout::Role *r = m_layout->getExistingRole(key); - if (r) - roleIndex = e->setJsProperty(*r, data, eng); - } - - return roleIndex; -} - -inline char *ListElement::getPropertyMemory(const ListLayout::Role &role) -{ - ListElement *e = this; - int blockIndex = 0; - while (blockIndex < role.blockIndex) { - if (e->next == nullptr) { - e->next = new ListElement; - e->next->uid = uid; - } - e = e->next; - ++blockIndex; - } - - char *mem = &e->data[role.blockOffset]; - return mem; -} - -ModelNodeMetaObject *ListElement::objectCache() -{ - if (!m_objectCache) - return nullptr; - return ModelNodeMetaObject::get(m_objectCache); -} - -StringOrTranslation *ListElement::getStringProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); - return s; -} - -QObject *ListElement::getQObjectProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - QPointer<QObject> *o = reinterpret_cast<QPointer<QObject> *>(mem); - return o->data(); -} - -QVariantMap *ListElement::getVariantMapProperty(const ListLayout::Role &role) -{ - QVariantMap *map = nullptr; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QVariantMap>(mem)) - map = reinterpret_cast<QVariantMap *>(mem); - - return map; -} - -QDateTime *ListElement::getDateTimeProperty(const ListLayout::Role &role) -{ - QDateTime *dt = nullptr; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QDateTime>(mem)) - dt = reinterpret_cast<QDateTime *>(mem); - - return dt; -} - -QJSValue *ListElement::getFunctionProperty(const ListLayout::Role &role) -{ - QJSValue *f = nullptr; - - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QJSValue>(mem)) - f = reinterpret_cast<QJSValue *>(mem); - - return f; -} - -QPointer<QObject> *ListElement::getGuardProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QPointer<QObject>) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - - QPointer<QObject> *o = nullptr; - - if (existingGuard) - o = reinterpret_cast<QPointer<QObject> *>(mem); - - return o; -} - -ListModel *ListElement::getListProperty(const ListLayout::Role &role) -{ - char *mem = getPropertyMemory(role); - ListModel **value = reinterpret_cast<ListModel **>(mem); - return *value; -} - -QVariant ListElement::getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng) -{ - char *mem = getPropertyMemory(role); - - QVariant data; - - switch (role.type) { - case ListLayout::Role::Number: - { - double *value = reinterpret_cast<double *>(mem); - data = *value; - } - break; - case ListLayout::Role::String: - { - StringOrTranslation *value = reinterpret_cast<StringOrTranslation *>(mem); - if (value->isSet()) - data = value->toString(owner); - } - break; - case ListLayout::Role::Bool: - { - bool *value = reinterpret_cast<bool *>(mem); - data = *value; - } - break; - case ListLayout::Role::List: - { - ListModel **value = reinterpret_cast<ListModel **>(mem); - ListModel *model = *value; - - if (model) { - if (model->m_modelCache == nullptr) { - model->m_modelCache = new QQmlListModel(owner, model, eng); - QQmlEngine::setContextForObject(model->m_modelCache, QQmlEngine::contextForObject(owner)); - } - - QObject *object = model->m_modelCache; - data = QVariant::fromValue(object); - } - } - break; - case ListLayout::Role::QObject: - { - QPointer<QObject> *guard = reinterpret_cast<QPointer<QObject> *>(mem); - QObject *object = guard->data(); - if (object) - data = QVariant::fromValue(object); - } - break; - case ListLayout::Role::VariantMap: - { - if (isMemoryUsed<QVariantMap>(mem)) { - QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); - data = *map; - } - } - break; - case ListLayout::Role::DateTime: - { - if (isMemoryUsed<QDateTime>(mem)) { - QDateTime *dt = reinterpret_cast<QDateTime *>(mem); - data = *dt; - } - } - break; - case ListLayout::Role::Function: - { - if (isMemoryUsed<QJSValue>(mem)) { - QJSValue *func = reinterpret_cast<QJSValue *>(mem); - data = QVariant::fromValue(*func); - } - } - break; - default: - break; - } - - return data; -} - -int ListElement::setStringProperty(const ListLayout::Role &role, const QString &s) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::String) { - char *mem = getPropertyMemory(role); - StringOrTranslation *c = reinterpret_cast<StringOrTranslation *>(mem); - bool changed; - if (!c->isSet() || c->isTranslation()) - changed = true; - else - changed = c->asString().compare(s) != 0; - c->setString(s); - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDoubleProperty(const ListLayout::Role &role, double d) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Number) { - char *mem = getPropertyMemory(role); - double *value = reinterpret_cast<double *>(mem); - bool changed = *value != d; - *value = d; - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setBoolProperty(const ListLayout::Role &role, bool b) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Bool) { - char *mem = getPropertyMemory(role); - bool *value = reinterpret_cast<bool *>(mem); - bool changed = *value != b; - *value = b; - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setListProperty(const ListLayout::Role &role, ListModel *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::List) { - char *mem = getPropertyMemory(role); - ListModel **value = reinterpret_cast<ListModel **>(mem); - if (*value && *value != m) { - (*value)->destroy(); - delete *value; - } - *value = m; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setQObjectProperty(const ListLayout::Role &role, QObject *o) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::QObject) { - char *mem = getPropertyMemory(role); - QPointer<QObject> *g = reinterpret_cast<QPointer<QObject> *>(mem); - bool existingGuard = false; - for (size_t i=0 ; i < sizeof(QPointer<QObject>) ; ++i) { - if (mem[i] != 0) { - existingGuard = true; - break; - } - } - bool changed; - if (existingGuard) { - changed = g->data() != o; - g->~QPointer(); - } else { - changed = true; - } - new (mem) QPointer<QObject>(o); - if (changed) - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QVariantMap>(mem)) { - QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); - map->~QMap(); - } - new (mem) QVariantMap(o->engine()->variantMapFromJS(o)); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::VariantMap) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QVariantMap>(mem)) { - QVariantMap *map = reinterpret_cast<QVariantMap *>(mem); - if (m && map->isSharedWith(*m)) - return roleIndex; - map->~QMap(); - } else if (!m) { - return roleIndex; - } - if (m) - new (mem) QVariantMap(*m); - else - new (mem) QVariantMap; - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::DateTime) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QDateTime>(mem)) { - QDateTime *dt = reinterpret_cast<QDateTime *>(mem); - dt->~QDateTime(); - } - new (mem) QDateTime(dt); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setFunctionProperty(const ListLayout::Role &role, const QJSValue &f) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::Function) { - char *mem = getPropertyMemory(role); - if (isMemoryUsed<QJSValue>(mem)) { - QJSValue *f = reinterpret_cast<QJSValue *>(mem); - f->~QJSValue(); - } - new (mem) QJSValue(f); - roleIndex = role.index; - } - - return roleIndex; -} - -int ListElement::setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b) -{ - int roleIndex = -1; - - if (role.type == ListLayout::Role::String) { - char *mem = getPropertyMemory(role); - StringOrTranslation *s = reinterpret_cast<StringOrTranslation *>(mem); - s->setTranslation(b); - roleIndex = role.index; - } - - return roleIndex; -} - - -void ListElement::setStringPropertyFast(const ListLayout::Role &role, const QString &s) -{ - char *mem = getPropertyMemory(role); - new (mem) StringOrTranslation(s); -} - -void ListElement::setDoublePropertyFast(const ListLayout::Role &role, double d) -{ - char *mem = getPropertyMemory(role); - double *value = new (mem) double; - *value = d; -} - -void ListElement::setBoolPropertyFast(const ListLayout::Role &role, bool b) -{ - char *mem = getPropertyMemory(role); - bool *value = new (mem) bool; - *value = b; -} - -void ListElement::setQObjectPropertyFast(const ListLayout::Role &role, QObject *o) -{ - char *mem = getPropertyMemory(role); - new (mem) QPointer<QObject>(o); -} - -void ListElement::setListPropertyFast(const ListLayout::Role &role, ListModel *m) -{ - char *mem = getPropertyMemory(role); - ListModel **value = new (mem) ListModel *; - *value = m; -} - -void ListElement::setVariantMapFast(const ListLayout::Role &role, QV4::Object *o) -{ - char *mem = getPropertyMemory(role); - QVariantMap *map = new (mem) QVariantMap; - *map = o->engine()->variantMapFromJS(o); -} - -void ListElement::setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt) -{ - char *mem = getPropertyMemory(role); - new (mem) QDateTime(dt); -} - -void ListElement::setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f) -{ - char *mem = getPropertyMemory(role); - new (mem) QJSValue(f); -} - -void ListElement::clearProperty(const ListLayout::Role &role) -{ - switch (role.type) { - case ListLayout::Role::String: - setStringProperty(role, QString()); - break; - case ListLayout::Role::Number: - setDoubleProperty(role, 0.0); - break; - case ListLayout::Role::Bool: - setBoolProperty(role, false); - break; - case ListLayout::Role::List: - setListProperty(role, nullptr); - break; - case ListLayout::Role::QObject: - setQObjectProperty(role, nullptr); - break; - case ListLayout::Role::DateTime: - setDateTimeProperty(role, QDateTime()); - break; - case ListLayout::Role::VariantMap: - setVariantMapProperty(role, (QVariantMap *)nullptr); - break; - case ListLayout::Role::Function: - setFunctionProperty(role, QJSValue()); - break; - default: - break; - } -} - -ListElement::ListElement() -{ - m_objectCache = nullptr; - uid = uidCounter.fetchAndAddOrdered(1); - next = nullptr; - memset(data, 0, sizeof(data)); -} - -ListElement::ListElement(int existingUid) -{ - m_objectCache = nullptr; - uid = existingUid; - next = nullptr; - memset(data, 0, sizeof(data)); -} - -ListElement::~ListElement() -{ - delete next; -} - -QVector<int> ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout) -{ - QVector<int> changedRoles; - for (int i=0 ; i < srcLayout->roleCount() ; ++i) { - const ListLayout::Role &srcRole = srcLayout->getExistingRole(i); - const ListLayout::Role &targetRole = targetLayout->getExistingRole(i); - - int roleIndex = -1; - switch (srcRole.type) { - case ListLayout::Role::List: - { - ListModel *srcSubModel = src->getListProperty(srcRole); - ListModel *targetSubModel = target->getListProperty(targetRole); - - if (srcSubModel) { - if (targetSubModel == nullptr) { - targetSubModel = new ListModel(targetRole.subLayout, nullptr); - target->setListPropertyFast(targetRole, targetSubModel); - } - if (ListModel::sync(srcSubModel, targetSubModel)) - roleIndex = targetRole.index; - } - } - break; - case ListLayout::Role::QObject: - { - QObject *object = src->getQObjectProperty(srcRole); - roleIndex = target->setQObjectProperty(targetRole, object); - } - break; - case ListLayout::Role::String: - case ListLayout::Role::Number: - case ListLayout::Role::Bool: - case ListLayout::Role::DateTime: - case ListLayout::Role::Function: - { - QVariant v = src->getProperty(srcRole, nullptr, nullptr); - roleIndex = target->setVariantProperty(targetRole, v); - } - break; - case ListLayout::Role::VariantMap: - { - QVariantMap *map = src->getVariantMapProperty(srcRole); - roleIndex = target->setVariantMapProperty(targetRole, map); - } - break; - default: - break; - } - if (roleIndex >= 0) - changedRoles << roleIndex; - } - - return changedRoles; -} - -void ListElement::destroy(ListLayout *layout) -{ - if (layout) { - for (int i=0 ; i < layout->roleCount() ; ++i) { - const ListLayout::Role &r = layout->getExistingRole(i); - - switch (r.type) { - case ListLayout::Role::String: - { - StringOrTranslation *string = getStringProperty(r); - if (string) - string->~StringOrTranslation(); - } - break; - case ListLayout::Role::List: - { - ListModel *model = getListProperty(r); - if (model) { - model->destroy(); - delete model; - } - } - break; - case ListLayout::Role::QObject: - { - QPointer<QObject> *guard = getGuardProperty(r); - if (guard) - guard->~QPointer(); - } - break; - case ListLayout::Role::VariantMap: - { - QVariantMap *map = getVariantMapProperty(r); - if (map) - map->~QMap(); - } - break; - case ListLayout::Role::DateTime: - { - QDateTime *dt = getDateTimeProperty(r); - if (dt) - dt->~QDateTime(); - } - break; - case ListLayout::Role::Function: - { - QJSValue *f = getFunctionProperty(r); - if (f) - f->~QJSValue(); - } - break; - default: - // other types don't need explicit cleanup. - break; - } - } - - if (m_objectCache) { - m_objectCache->~QObject(); - operator delete(m_objectCache); - } - } - - if (next) - next->destroy(nullptr); - uid = -1; -} - -int ListElement::setVariantProperty(const ListLayout::Role &role, const QVariant &d) -{ - int roleIndex = -1; - - switch (role.type) { - case ListLayout::Role::Number: - roleIndex = setDoubleProperty(role, d.toDouble()); - break; - case ListLayout::Role::String: - if (d.userType() == qMetaTypeId<const QV4::CompiledData::Binding *>()) - roleIndex = setTranslationProperty(role, d.value<const QV4::CompiledData::Binding*>()); - else - roleIndex = setStringProperty(role, d.toString()); - break; - case ListLayout::Role::Bool: - roleIndex = setBoolProperty(role, d.toBool()); - break; - case ListLayout::Role::List: - roleIndex = setListProperty(role, d.value<ListModel *>()); - break; - case ListLayout::Role::VariantMap: { - QVariantMap map = d.toMap(); - roleIndex = setVariantMapProperty(role, &map); - } - break; - case ListLayout::Role::DateTime: - roleIndex = setDateTimeProperty(role, d.toDateTime()); - break; - case ListLayout::Role::Function: - roleIndex = setFunctionProperty(role, d.value<QJSValue>()); - break; - default: - break; - } - - return roleIndex; -} - -int ListElement::setJsProperty(const ListLayout::Role &role, const QV4::Value &d, QV4::ExecutionEngine *eng) -{ - // Check if this key exists yet - int roleIndex = -1; - - QV4::Scope scope(eng); - - // Add the value now - if (d.isString()) { - QString qstr = d.toQString(); - roleIndex = setStringProperty(role, qstr); - } else if (d.isNumber()) { - roleIndex = setDoubleProperty(role, d.asDouble()); - } else if (d.as<QV4::ArrayObject>()) { - QV4::ScopedArrayObject a(scope, d); - if (role.type == ListLayout::Role::List) { - QV4::Scope scope(a->engine()); - QV4::ScopedObject o(scope); - - ListModel *subModel = new ListModel(role.subLayout, nullptr); - int arrayLength = a->getLength(); - for (int j=0 ; j < arrayLength ; ++j) { - o = a->get(j); - subModel->append(o); - } - roleIndex = setListProperty(role, subModel); - } else { - qmlWarning(nullptr) << QStringLiteral("Can't assign to existing role '%1' of different type [%2 -> %3]").arg(role.name).arg(roleTypeName(role.type)).arg(roleTypeName(ListLayout::Role::List)); - } - } else if (d.isBoolean()) { - roleIndex = setBoolProperty(role, d.booleanValue()); - } else if (d.as<QV4::DateObject>()) { - QV4::Scoped<QV4::DateObject> dd(scope, d); - QDateTime dt = dd->toQDateTime(); - roleIndex = setDateTimeProperty(role, dt); - } else if (d.as<QV4::FunctionObject>()) { - QV4::ScopedFunctionObject f(scope, d); - QJSValue jsv; - QJSValuePrivate::setValue(&jsv, eng, f); - roleIndex = setFunctionProperty(role, jsv); - } else if (d.isObject()) { - QV4::ScopedObject o(scope, d); - QV4::QObjectWrapper *wrapper = o->as<QV4::QObjectWrapper>(); - if (role.type == ListLayout::Role::QObject && wrapper) { - QObject *o = wrapper->object(); - roleIndex = setQObjectProperty(role, o); - } else if (role.type == ListLayout::Role::VariantMap) { - roleIndex = setVariantMapProperty(role, o); - } - } else if (d.isNullOrUndefined()) { - clearProperty(role); - } - - return roleIndex; -} - -ModelNodeMetaObject::ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex) -: QQmlOpenMetaObject(object), m_enabled(false), m_model(model), m_elementIndex(elementIndex), m_initialized(false) -{} - -void ModelNodeMetaObject::initialize() -{ - const int roleCount = m_model->m_listModel->roleCount(); - QVector<QByteArray> properties; - properties.reserve(roleCount); - for (int i = 0 ; i < roleCount ; ++i) { - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); - QByteArray name = role.name.toUtf8(); - properties << name; - } - type()->createProperties(properties); - updateValues(); - m_enabled = true; -} - -ModelNodeMetaObject::~ModelNodeMetaObject() -{ -} - -QAbstractDynamicMetaObject *ModelNodeMetaObject::toDynamicMetaObject(QObject *object) -{ - if (!m_initialized) { - m_initialized = true; - initialize(); - } - return QQmlOpenMetaObject::toDynamicMetaObject(object); -} - -ModelNodeMetaObject *ModelNodeMetaObject::get(QObject *obj) -{ - QObjectPrivate *op = QObjectPrivate::get(obj); - return static_cast<ModelNodeMetaObject*>(op->metaObject); -} - -void ModelNodeMetaObject::updateValues() -{ - const int roleCount = m_model->m_listModel->roleCount(); - if (!m_initialized) { - if (roleCount) { - Q_ALLOCA_VAR(int, changedRoles, roleCount * sizeof(int)); - for (int i = 0; i < roleCount; ++i) - changedRoles[i] = i; - emitDirectNotifies(changedRoles, roleCount); - } - return; - } - for (int i=0 ; i < roleCount ; ++i) { - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(i); - QByteArray name = role.name.toUtf8(); - const QVariant &data = m_model->data(m_elementIndex, i); - setValue(name, data, role.type == ListLayout::Role::List); - } -} - -void ModelNodeMetaObject::updateValues(const QVector<int> &roles) -{ - if (!m_initialized) { - emitDirectNotifies(roles.constData(), roles.count()); - return; - } - int roleCount = roles.count(); - for (int i=0 ; i < roleCount ; ++i) { - int roleIndex = roles.at(i); - const ListLayout::Role &role = m_model->m_listModel->getExistingRole(roleIndex); - QByteArray name = role.name.toUtf8(); - const QVariant &data = m_model->data(m_elementIndex, roleIndex); - setValue(name, data, role.type == ListLayout::Role::List); - } -} - -void ModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QString propName = QString::fromUtf8(name(index)); - const QVariant value = this->value(index); - - QV4::Scope scope(m_model->engine()); - QV4::ScopedValue v(scope, scope.engine->fromVariant(value)); - - int roleIndex = m_model->m_listModel->setExistingProperty(m_elementIndex, propName, v, scope.engine); - if (roleIndex != -1) - m_model->emitItemsChanged(m_elementIndex, 1, QVector<int>(1, roleIndex)); -} - -// Does the emission of the notifiers when we haven't created the meta-object yet -void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCount) -{ - Q_ASSERT(!m_initialized); - QQmlData *ddata = QQmlData::get(object(), /*create*/false); - if (!ddata) - return; - // There's nothing to emit if we're a list model in a worker thread. - if (!qmlEngine(m_model)) - return; - for (int i = 0; i < roleCount; ++i) { - const int changedRole = changedRoles[i]; - QQmlNotifier::notify(ddata, changedRole); - } -} - -namespace QV4 { - -bool ModelObject::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) -{ - if (!id.isString()) - return Object::virtualPut(m, id, value, receiver); - QString propName = id.toQString(); - - ModelObject *that = static_cast<ModelObject*>(m); - - ExecutionEngine *eng = that->engine(); - const int elementIndex = that->d()->elementIndex(); - int roleIndex = that->d()->m_model->m_listModel->setExistingProperty(elementIndex, propName, value, eng); - if (roleIndex != -1) - that->d()->m_model->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); - - ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object()); - if (mo->initialized()) - mo->emitPropertyNotification(propName.toUtf8()); - return true; -} - -ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) -{ - if (!id.isString()) - return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); - - const ModelObject *that = static_cast<const ModelObject*>(m); - Scope scope(that); - ScopedString name(scope, id.asStringOrSymbol()); - const ListLayout::Role *role = that->d()->m_model->m_listModel->getExistingRole(name); - if (!role) - return QObjectWrapper::virtualGet(m, id, receiver, hasProperty); - if (hasProperty) - *hasProperty = true; - - if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) { - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine); - if (ep && ep->propertyCapture) - ep->propertyCapture->captureProperty(that->object(), -1, role->index, /*doNotify=*/ false); - } - - const int elementIndex = that->d()->elementIndex(); - QVariant value = that->d()->m_model->data(elementIndex, role->index); - return that->engine()->fromVariant(value); -} - -ReturnedValue ModelObject::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) -{ - lookup->getter = Lookup::getterFallback; - return lookup->getter(lookup, engine, *object); -} - -struct ModelObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator -{ - int roleNameIndex = 0; - ~ModelObjectOwnPropertyKeyIterator() override = default; - PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override; - -}; - -PropertyKey ModelObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs) -{ - const ModelObject *that = static_cast<const ModelObject *>(o); - - ExecutionEngine *v4 = that->engine(); - if (roleNameIndex < that->listModel()->roleCount()) { - Scope scope(that->engine()); - const ListLayout::Role &role = that->listModel()->getExistingRole(roleNameIndex); - ++roleNameIndex; - ScopedString roleName(scope, v4->newString(role.name)); - if (attrs) - *attrs = QV4::Attr_Data; - if (pd) { - QVariant value = that->d()->m_model->data(that->d()->elementIndex(), role.index); - pd->value = v4->fromVariant(value); - } - return roleName->toPropertyKey(); - } - - // Fall back to QV4::Object as opposed to QV4::QObjectWrapper otherwise it will add - // unnecessary entries that relate to the roles used. These just create extra work - // later on as they will just be ignored. - return ObjectOwnPropertyKeyIterator::next(o, pd, attrs); -} - -OwnPropertyKeyIterator *ModelObject::virtualOwnPropertyKeys(const Object *m, Value *target) -{ - *target = *m; - return new ModelObjectOwnPropertyKeyIterator; -} - -DEFINE_OBJECT_VTABLE(ModelObject); - -} // namespace QV4 - -DynamicRoleModelNode::DynamicRoleModelNode(QQmlListModel *owner, int uid) : m_owner(owner), m_uid(uid), m_meta(new DynamicRoleModelNodeMetaObject(this)) -{ - setNodeUpdatesEnabled(true); -} - -DynamicRoleModelNode *DynamicRoleModelNode::create(const QVariantMap &obj, QQmlListModel *owner) -{ - DynamicRoleModelNode *object = new DynamicRoleModelNode(owner, uidCounter.fetchAndAddOrdered(1)); - QVector<int> roles; - object->updateValues(obj, roles); - return object; -} - -QVector<int> DynamicRoleModelNode::sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target) -{ - QVector<int> changedRoles; - for (int i = 0; i < src->m_meta->count(); ++i) { - const QByteArray &name = src->m_meta->name(i); - QVariant value = src->m_meta->value(i); - - QQmlListModel *srcModel = qobject_cast<QQmlListModel *>(value.value<QObject *>()); - QQmlListModel *targetModel = qobject_cast<QQmlListModel *>(target->m_meta->value(i).value<QObject *>()); - - bool modelHasChanges = false; - if (srcModel) { - if (targetModel == nullptr) - targetModel = QQmlListModel::createWithOwner(target->m_owner); - - modelHasChanges = QQmlListModel::sync(srcModel, targetModel); - - QObject *targetModelObject = targetModel; - value = QVariant::fromValue(targetModelObject); - } else if (targetModel) { - delete targetModel; - } - - if (target->setValue(name, value) || modelHasChanges) - changedRoles << target->m_owner->m_roles.indexOf(QString::fromUtf8(name)); - } - return changedRoles; -} - -void DynamicRoleModelNode::updateValues(const QVariantMap &object, QVector<int> &roles) -{ - for (auto it = object.cbegin(), end = object.cend(); it != end; ++it) { - const QString &key = it.key(); - - int roleIndex = m_owner->m_roles.indexOf(key); - if (roleIndex == -1) { - roleIndex = m_owner->m_roles.count(); - m_owner->m_roles.append(key); - } - - QVariant value = it.value(); - - // A JS array/object is translated into a (hierarchical) QQmlListModel, - // so translate to a variant map/list first with toVariant(). - if (value.userType() == qMetaTypeId<QJSValue>()) - value = value.value<QJSValue>().toVariant(); - - if (value.type() == QVariant::List) { - QQmlListModel *subModel = QQmlListModel::createWithOwner(m_owner); - - QVariantList subArray = value.toList(); - QVariantList::const_iterator subIt = subArray.cbegin(); - QVariantList::const_iterator subEnd = subArray.cend(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - value = QVariant::fromValue(subModelObject); - } - - const QByteArray &keyUtf8 = key.toUtf8(); - - QQmlListModel *existingModel = qobject_cast<QQmlListModel *>(m_meta->value(keyUtf8).value<QObject *>()); - delete existingModel; - - if (m_meta->setValue(keyUtf8, value)) - roles << roleIndex; - } -} - -DynamicRoleModelNodeMetaObject::DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object) - : QQmlOpenMetaObject(object), m_enabled(false), m_owner(object) -{ -} - -DynamicRoleModelNodeMetaObject::~DynamicRoleModelNodeMetaObject() -{ - for (int i=0 ; i < count() ; ++i) { - QQmlListModel *subModel = qobject_cast<QQmlListModel *>(value(i).value<QObject *>()); - delete subModel; - } -} - -void DynamicRoleModelNodeMetaObject::propertyWrite(int index) -{ - if (!m_enabled) - return; - - QVariant v = value(index); - QQmlListModel *model = qobject_cast<QQmlListModel *>(v.value<QObject *>()); - delete model; -} - -void DynamicRoleModelNodeMetaObject::propertyWritten(int index) -{ - if (!m_enabled) - return; - - QQmlListModel *parentModel = m_owner->m_owner; - - QVariant v = value(index); - - // A JS array/object is translated into a (hierarchical) QQmlListModel, - // so translate to a variant map/list first with toVariant(). - if (v.userType() == qMetaTypeId<QJSValue>()) - v= v.value<QJSValue>().toVariant(); - - if (v.type() == QVariant::List) { - QQmlListModel *subModel = QQmlListModel::createWithOwner(parentModel); - - QVariantList subArray = v.toList(); - QVariantList::const_iterator subIt = subArray.cbegin(); - QVariantList::const_iterator subEnd = subArray.cend(); - while (subIt != subEnd) { - const QVariantMap &subObject = subIt->toMap(); - subModel->m_modelObjects.append(DynamicRoleModelNode::create(subObject, subModel)); - ++subIt; - } - - QObject *subModelObject = subModel; - v = QVariant::fromValue(subModelObject); - - setValue(index, v); - } - - int elementIndex = parentModel->m_modelObjects.indexOf(m_owner); - if (elementIndex != -1) { - int roleIndex = parentModel->m_roles.indexOf(QString::fromLatin1(name(index).constData())); - if (roleIndex != -1) - parentModel->emitItemsChanged(elementIndex, 1, QVector<int>(1, roleIndex)); - } -} - -/*! - \qmltype ListModel - \instantiates QQmlListModel - \inqmlmodule QtQml.Models - \ingroup qtquick-models - \brief Defines a free-form list data source. - - The ListModel is a simple container of ListElement definitions, each - containing data roles. The contents can be defined dynamically, or - explicitly in QML. - - The number of elements in the model can be obtained from its \l count property. - A number of familiar methods are also provided to manipulate the contents of the - model, including append(), insert(), move(), remove() and set(). These methods - accept dictionaries as their arguments; these are translated to ListElement objects - by the model. - - Elements can be manipulated via the model using the setProperty() method, which - allows the roles of the specified element to be set and changed. - - \section1 Example Usage - - The following example shows a ListModel containing three elements, with the roles - "name" and "cost". - - \div {class="float-right"} - \inlineimage listmodel.png - \enddiv - - \snippet qml/listmodel/listmodel.qml 0 - - Roles (properties) in each element must begin with a lower-case letter and - should be common to all elements in a model. The ListElement documentation - provides more guidelines for how elements should be defined. - - Since the example model contains an \c id property, it can be referenced - by views, such as the ListView in this example: - - \snippet qml/listmodel/listmodel-simple.qml 0 - \dots 8 - \snippet qml/listmodel/listmodel-simple.qml 1 - - It is possible for roles to contain list data. In the following example we - create a list of fruit attributes: - - \snippet qml/listmodel/listmodel-nested.qml model - - The delegate displays all the fruit attributes: - - \div {class="float-right"} - \inlineimage listmodel-nested.png - \enddiv - - \snippet qml/listmodel/listmodel-nested.qml delegate - - \section1 Modifying List Models - - The content of a ListModel may be created and modified using the clear(), - append(), set(), insert() and setProperty() methods. For example: - - \snippet qml/listmodel/listmodel-modify.qml delegate - - Note that when creating content dynamically the set of available properties - cannot be changed once set. Whatever properties are first added to the model - are the only permitted properties in the model. - - \section1 Using Threaded List Models with WorkerScript - - ListModel can be used together with WorkerScript access a list model - from multiple threads. This is useful if list modifications are - synchronous and take some time: the list operations can be moved to a - different thread to avoid blocking of the main GUI thread. - - Here is an example that uses WorkerScript to periodically append the - current time to a list model: - - \snippet ../quick/threading/threadedlistmodel/timedisplay.qml 0 - - The included file, \tt dataloader.mjs, looks like this: - - \snippet ../quick/threading/threadedlistmodel/dataloader.mjs 0 - - The timer in the main example sends messages to the worker script by calling - \l WorkerScript::sendMessage(). When this message is received, - \c WorkerScript.onMessage() is invoked in \c dataloader.mjs, - which appends the current time to the list model. - - Note the call to sync() from the external thread. - You must call sync() or else the changes made to the list from that - thread will not be reflected in the list model in the main thread. - - \sa {qml-data-models}{Data Models}, {Qt Quick Examples - Threading}, {Qt QML} -*/ - -QQmlListModel::QQmlListModel(QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = true; - m_primary = true; - m_agent = nullptr; - m_dynamicRoles = false; - - m_layout = new ListLayout; - m_listModel = new ListModel(m_layout, this); - - m_engine = nullptr; -} - -QQmlListModel::QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent) -: QAbstractListModel(parent) -{ - m_mainThread = owner->m_mainThread; - m_primary = false; - m_agent = owner->m_agent; - - Q_ASSERT(owner->m_dynamicRoles == false); - m_dynamicRoles = false; - m_layout = nullptr; - m_listModel = data; - - m_engine = engine; - m_compilationUnit = owner->m_compilationUnit; -} - -QQmlListModel::QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent) -: QAbstractListModel(agent) -{ - m_mainThread = false; - m_primary = true; - m_agent = agent; - m_dynamicRoles = orig->m_dynamicRoles; - - m_layout = new ListLayout(orig->m_layout); - m_listModel = new ListModel(m_layout, this); - - if (m_dynamicRoles) - sync(orig, this); - else - ListModel::sync(orig->m_listModel, m_listModel); - - m_engine = nullptr; - m_compilationUnit = orig->m_compilationUnit; -} - -QQmlListModel::~QQmlListModel() -{ - qDeleteAll(m_modelObjects); - - if (m_primary) { - m_listModel->destroy(); - delete m_listModel; - - if (m_mainThread && m_agent) { - m_agent->modelDestroyed(); - m_agent->release(); - } - } - - m_listModel = nullptr; - - delete m_layout; - m_layout = nullptr; -} - -QQmlListModel *QQmlListModel::createWithOwner(QQmlListModel *newOwner) -{ - QQmlListModel *model = new QQmlListModel; - - model->m_mainThread = newOwner->m_mainThread; - model->m_engine = newOwner->m_engine; - model->m_agent = newOwner->m_agent; - model->m_dynamicRoles = newOwner->m_dynamicRoles; - - if (model->m_mainThread && model->m_agent) - model->m_agent->addref(); - - QQmlEngine::setContextForObject(model, QQmlEngine::contextForObject(newOwner)); - - return model; -} - -QV4::ExecutionEngine *QQmlListModel::engine() const -{ - if (m_engine == nullptr) { - m_engine = qmlEngine(this)->handle(); - } - - return m_engine; -} - -bool QQmlListModel::sync(QQmlListModel *src, QQmlListModel *target) -{ - Q_ASSERT(src->m_dynamicRoles && target->m_dynamicRoles); - - bool hasChanges = false; - - target->m_roles = src->m_roles; - - // Build hash of elements <-> uid for each of the lists - QHash<int, ElementSync> elementHash; - for (int i = 0 ; i < target->m_modelObjects.count(); ++i) { - DynamicRoleModelNode *e = target->m_modelObjects.at(i); - int uid = e->getUid(); - ElementSync sync; - sync.target = e; - sync.targetIndex = i; - elementHash.insert(uid, sync); - } - for (int i = 0 ; i < src->m_modelObjects.count(); ++i) { - DynamicRoleModelNode *e = src->m_modelObjects.at(i); - int uid = e->getUid(); - - QHash<int, ElementSync>::iterator it = elementHash.find(uid); - if (it == elementHash.end()) { - ElementSync sync; - sync.src = e; - sync.srcIndex = i; - elementHash.insert(uid, sync); - } else { - ElementSync &sync = it.value(); - sync.src = e; - sync.srcIndex = i; - } - } - - // Get list of elements that are in the target but no longer in the source. These get deleted first. - int rowsRemoved = 0; - for (int i = 0 ; i < target->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *element = target->m_modelObjects.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.targetIndex >= 0); - // need to update the targetIndex, to keep it correct after removals - s.targetIndex -= rowsRemoved; - if (s.src == nullptr) { - Q_ASSERT(s.targetIndex == i); - hasChanges = true; - target->beginRemoveRows(QModelIndex(), i, i); - target->m_modelObjects.remove(i, 1); - target->endRemoveRows(); - delete s.target; - ++rowsRemoved; - --i; - continue; - } - } - - // Clear the target list, and append in correct order from the source - target->m_modelObjects.clear(); - for (int i = 0 ; i < src->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *element = src->m_modelObjects.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - DynamicRoleModelNode *targetElement = s.target; - if (targetElement == nullptr) { - targetElement = new DynamicRoleModelNode(target, element->getUid()); - } - s.changedRoles = DynamicRoleModelNode::sync(element, targetElement); - target->m_modelObjects.append(targetElement); - } - - // now emit the change notifications required. This can be safely done, as we're only emitting changes, moves and inserts, - // so the model indices can't be out of bounds - // - // to ensure things are kept in the correct order, emit inserts and moves first. This shouls ensure all persistent - // model indices are updated correctly - int rowsInserted = 0; - for (int i = 0 ; i < target->m_modelObjects.count() ; ++i) { - DynamicRoleModelNode *element = target->m_modelObjects.at(i); - ElementSync &s = elementHash.find(element->getUid()).value(); - Q_ASSERT(s.srcIndex >= 0); - s.srcIndex += rowsInserted; - if (s.srcIndex != s.targetIndex) { - if (s.targetIndex == -1) { - target->beginInsertRows(QModelIndex(), i, i); - target->endInsertRows(); - } else { - target->beginMoveRows(QModelIndex(), i, i, QModelIndex(), s.srcIndex); - target->endMoveRows(); - } - hasChanges = true; - ++rowsInserted; - } - if (s.targetIndex != -1 && !s.changedRoles.isEmpty()) { - QModelIndex idx = target->createIndex(i, 0); - emit target->dataChanged(idx, idx, s.changedRoles); - hasChanges = true; - } - } - return hasChanges; -} - -void QQmlListModel::emitItemsChanged(int index, int count, const QVector<int> &roles) -{ - if (count <= 0) - return; - - if (m_mainThread) - emit dataChanged(createIndex(index, 0), createIndex(index + count - 1, 0), roles);; -} - -void QQmlListModel::emitItemsAboutToBeInserted(int index, int count) -{ - Q_ASSERT(index >= 0 && count >= 0); - if (m_mainThread) - beginInsertRows(QModelIndex(), index, index + count - 1); -} - -void QQmlListModel::emitItemsInserted() -{ - if (m_mainThread) { - endInsertRows(); - emit countChanged(); - } -} - -QQmlListModelWorkerAgent *QQmlListModel::agent() -{ - if (m_agent) - return m_agent; - - m_agent = new QQmlListModelWorkerAgent(this); - return m_agent; -} - -QModelIndex QQmlListModel::index(int row, int column, const QModelIndex &parent) const -{ - return row >= 0 && row < count() && column == 0 && !parent.isValid() - ? createIndex(row, column) - : QModelIndex(); -} - -int QQmlListModel::rowCount(const QModelIndex &parent) const -{ - return !parent.isValid() ? count() : 0; -} - -QVariant QQmlListModel::data(const QModelIndex &index, int role) const -{ - return data(index.row(), role); -} - -bool QQmlListModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - const int row = index.row(); - if (row >= count() || row < 0) - return false; - - if (m_dynamicRoles) { - const QByteArray property = m_roles.at(role).toUtf8(); - if (m_modelObjects[row]->setValue(property, value)) { - emitItemsChanged(row, 1, QVector<int>(1, role)); - return true; - } - } else { - const ListLayout::Role &r = m_listModel->getExistingRole(role); - const int roleIndex = m_listModel->setOrCreateProperty(row, r.name, value); - if (roleIndex != -1) { - emitItemsChanged(row, 1, QVector<int>(1, role)); - return true; - } - } - - return false; -} - -QVariant QQmlListModel::data(int index, int role) const -{ - QVariant v; - - if (index >= count() || index < 0) - return v; - - if (m_dynamicRoles) - v = m_modelObjects[index]->getValue(m_roles[role]); - else - v = m_listModel->getProperty(index, role, this, engine()); - - return v; -} - -QHash<int, QByteArray> QQmlListModel::roleNames() const -{ - QHash<int, QByteArray> roleNames; - - if (m_dynamicRoles) { - for (int i = 0 ; i < m_roles.count() ; ++i) - roleNames.insert(i, m_roles.at(i).toUtf8()); - } else { - for (int i = 0 ; i < m_listModel->roleCount() ; ++i) { - const ListLayout::Role &r = m_listModel->getExistingRole(i); - roleNames.insert(i, r.name.toUtf8()); - } - } - - return roleNames; -} - -/*! - \qmlproperty bool ListModel::dynamicRoles - - By default, the type of a role is fixed the first time - the role is used. For example, if you create a role called - "data" and assign a number to it, you can no longer assign - a string to the "data" role. However, when the dynamicRoles - property is enabled, the type of a given role is not fixed - and can be different between elements. - - The dynamicRoles property must be set before any data is - added to the ListModel, and must be set from the main - thread. - - A ListModel that has data statically defined (via the - ListElement QML syntax) cannot have the dynamicRoles - property enabled. - - There is a significant performance cost to using a - ListModel with dynamic roles enabled. The cost varies - from platform to platform but is typically somewhere - between 4-6x slower than using static role types. - - Due to the performance cost of using dynamic roles, - they are disabled by default. -*/ -void QQmlListModel::setDynamicRoles(bool enableDynamicRoles) -{ - if (m_mainThread && m_agent == nullptr) { - if (enableDynamicRoles) { - if (m_layout->roleCount()) - qmlWarning(this) << tr("unable to enable dynamic roles as this model is not empty"); - else - m_dynamicRoles = true; - } else { - if (m_roles.count()) { - qmlWarning(this) << tr("unable to enable static roles as this model is not empty"); - } else { - m_dynamicRoles = false; - } - } - } else { - qmlWarning(this) << tr("dynamic role setting must be made from the main thread, before any worker scripts are created"); - } -} - -/*! - \qmlproperty int ListModel::count - The number of data entries in the model. -*/ -int QQmlListModel::count() const -{ - return m_dynamicRoles ? m_modelObjects.count() : m_listModel->elementCount(); -} - -/*! - \qmlmethod ListModel::clear() - - Deletes all content from the model. - - \sa append(), remove() -*/ -void QQmlListModel::clear() -{ - removeElements(0, count()); -} - -/*! - \qmlmethod ListModel::remove(int index, int count = 1) - - Deletes the content at \a index from the model. - - \sa clear() -*/ -void QQmlListModel::remove(QQmlV4Function *args) -{ - int argLength = args->length(); - - if (argLength == 1 || argLength == 2) { - QV4::Scope scope(args->v4engine()); - int index = QV4::ScopedValue(scope, (*args)[0])->toInt32(); - int removeCount = (argLength == 2 ? QV4::ScopedValue(scope, (*args)[1])->toInt32() : 1); - - if (index < 0 || index+removeCount > count() || removeCount <= 0) { - qmlWarning(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+removeCount).arg(count()); - return; - } - - removeElements(index, removeCount); - } else { - qmlWarning(this) << tr("remove: incorrect number of arguments"); - } -} - -void QQmlListModel::removeElements(int index, int removeCount) -{ - Q_ASSERT(index >= 0 && removeCount >= 0); - - if (!removeCount) - return; - - if (m_mainThread) - beginRemoveRows(QModelIndex(), index, index + removeCount - 1); - - QVector<std::function<void()>> toDestroy; - if (m_dynamicRoles) { - for (int i=0 ; i < removeCount ; ++i) { - auto modelObject = m_modelObjects[index+i]; - toDestroy.append([modelObject](){ - delete modelObject; - }); - } - m_modelObjects.remove(index, removeCount); - } else { - toDestroy = m_listModel->remove(index, removeCount); - } - - if (m_mainThread) { - endRemoveRows(); - emit countChanged(); - } - for (const auto &destroyer : toDestroy) - destroyer(); -} - -/*! - \qmlmethod ListModel::insert(int index, jsobject dict) - - Adds a new item to the list model at position \a index, with the - values in \a dict. - - \code - fruitModel.insert(2, {"cost": 5.95, "name":"Pizza"}) - \endcode - - The \a index must be to an existing item in the list, or one past - the end of the list (equivalent to append). - - \sa set(), append() -*/ - -void QQmlListModel::insert(QQmlV4Function *args) -{ - if (args->length() == 2) { - QV4::Scope scope(args->v4engine()); - QV4::ScopedValue arg0(scope, (*args)[0]); - int index = arg0->toInt32(); - - if (index < 0 || index > count()) { - qmlWarning(this) << tr("insert: index %1 out of range").arg(index); - return; - } - - QV4::ScopedObject argObject(scope, (*args)[1]); - QV4::ScopedArrayObject objectArray(scope, (*args)[1]); - if (objectArray) { - QV4::ScopedObject argObject(scope); - - int objectArrayLength = objectArray->getLength(); - emitItemsAboutToBeInserted(index, objectArrayLength); - for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->get(i); - - if (m_dynamicRoles) { - m_modelObjects.insert(index+i, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index+i, argObject); - } - } - emitItemsInserted(); - } else if (argObject) { - emitItemsAboutToBeInserted(index, 1); - - if (m_dynamicRoles) { - m_modelObjects.insert(index, DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - m_listModel->insert(index, argObject); - } - - emitItemsInserted(); - } else { - qmlWarning(this) << tr("insert: value is not an object"); - } - } else { - qmlWarning(this) << tr("insert: value is not an object"); - } -} - -/*! - \qmlmethod ListModel::move(int from, int to, int n) - - Moves \a n items \a from one position \a to another. - - The from and to ranges must exist; for example, to move the first 3 items - to the end of the list: - - \code - fruitModel.move(0, fruitModel.count - 3, 3) - \endcode - - \sa append() -*/ -void QQmlListModel::move(int from, int to, int n) -{ - if (n == 0 || from == to) - return; - if (!canMove(from, to, n)) { - qmlWarning(this) << tr("move: out of range"); - return; - } - - if (m_mainThread) - beginMoveRows(QModelIndex(), from, from + n - 1, QModelIndex(), to > from ? to + n : to); - - if (m_dynamicRoles) { - - int realFrom = from; - int realTo = to; - int realN = n; - - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - realFrom = tto; - realTo = tto+n; - realN = tfrom-tto; - } - - QPODVector<DynamicRoleModelNode *, 4> store; - for (int i=0 ; i < (realTo-realFrom) ; ++i) - store.append(m_modelObjects[realFrom+realN+i]); - for (int i=0 ; i < realN ; ++i) - store.append(m_modelObjects[realFrom+i]); - for (int i=0 ; i < store.count() ; ++i) - m_modelObjects[realFrom+i] = store[i]; - - } else { - m_listModel->move(from, to, n); - } - - if (m_mainThread) - endMoveRows(); -} - -/*! - \qmlmethod ListModel::append(jsobject dict) - - Adds a new item to the end of the list model, with the - values in \a dict. - - \code - fruitModel.append({"cost": 5.95, "name":"Pizza"}) - \endcode - - \sa set(), remove() -*/ -void QQmlListModel::append(QQmlV4Function *args) -{ - if (args->length() == 1) { - QV4::Scope scope(args->v4engine()); - QV4::ScopedObject argObject(scope, (*args)[0]); - QV4::ScopedArrayObject objectArray(scope, (*args)[0]); - - if (objectArray) { - QV4::ScopedObject argObject(scope); - - int objectArrayLength = objectArray->getLength(); - if (objectArrayLength > 0) { - int index = count(); - emitItemsAboutToBeInserted(index, objectArrayLength); - - for (int i=0 ; i < objectArrayLength ; ++i) { - argObject = objectArray->get(i); - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - m_listModel->append(argObject); - } - } - - emitItemsInserted(); - } - } else if (argObject) { - int index; - - if (m_dynamicRoles) { - index = m_modelObjects.count(); - emitItemsAboutToBeInserted(index, 1); - m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(argObject), this)); - } else { - index = m_listModel->elementCount(); - emitItemsAboutToBeInserted(index, 1); - m_listModel->append(argObject); - } - - emitItemsInserted(); - } else { - qmlWarning(this) << tr("append: value is not an object"); - } - } else { - qmlWarning(this) << tr("append: value is not an object"); - } -} - -/*! - \qmlmethod object ListModel::get(int index) - - Returns the item at \a index in the list model. This allows the item - data to be accessed or modified from JavaScript: - - \code - Component.onCompleted: { - fruitModel.append({"cost": 5.95, "name":"Jackfruit"}); - console.log(fruitModel.get(0).cost); - fruitModel.get(0).cost = 10.95; - } - \endcode - - The \a index must be an element in the list. - - Note that properties of the returned object that are themselves objects - will also be models, and this get() method is used to access elements: - - \code - fruitModel.append(..., "attributes": - [{"name":"spikes","value":"7mm"}, - {"name":"color","value":"green"}]); - fruitModel.get(0).attributes.get(1).value; // == "green" - \endcode - - \warning The returned object is not guaranteed to remain valid. It - should not be used in \l{Property Binding}{property bindings}. - - \sa append() -*/ -QJSValue QQmlListModel::get(int index) const -{ - QV4::Scope scope(engine()); - QV4::ScopedValue result(scope, QV4::Value::undefinedValue()); - - if (index >= 0 && index < count()) { - - if (m_dynamicRoles) { - DynamicRoleModelNode *object = m_modelObjects[index]; - result = QV4::QObjectWrapper::wrap(scope.engine, object); - } else { - QObject *object = m_listModel->getOrCreateModelObject(const_cast<QQmlListModel *>(this), index); - QQmlData *ddata = QQmlData::get(object); - if (ddata->jsWrapper.isNullOrUndefined()) { - result = scope.engine->memoryManager->allocate<QV4::ModelObject>(object, const_cast<QQmlListModel *>(this)); - // Keep track of the QObjectWrapper in persistent value storage - ddata->jsWrapper.set(scope.engine, result); - } else { - result = ddata->jsWrapper.value(); - } - } - } - - return QJSValue(engine(), result->asReturnedValue()); -} - -/*! - \qmlmethod ListModel::set(int index, jsobject dict) - - Changes the item at \a index in the list model with the - values in \a dict. Properties not appearing in \a dict - are left unchanged. - - \code - fruitModel.set(3, {"cost": 5.95, "name":"Pizza"}) - \endcode - - If \a index is equal to count() then a new item is appended to the - list. Otherwise, \a index must be an element in the list. - - \sa append() -*/ -void QQmlListModel::set(int index, const QJSValue &value) -{ - QV4::Scope scope(engine()); - QV4::ScopedObject object(scope, QJSValuePrivate::getValue(&value)); - - if (!object) { - qmlWarning(this) << tr("set: value is not an object"); - return; - } - if (index > count() || index < 0) { - qmlWarning(this) << tr("set: index %1 out of range").arg(index); - return; - } - - - if (index == count()) { - emitItemsAboutToBeInserted(index, 1); - - if (m_dynamicRoles) { - m_modelObjects.append(DynamicRoleModelNode::create(scope.engine->variantMapFromJS(object), this)); - } else { - m_listModel->insert(index, object); - } - - emitItemsInserted(); - } else { - - QVector<int> roles; - - if (m_dynamicRoles) { - m_modelObjects[index]->updateValues(scope.engine->variantMapFromJS(object), roles); - } else { - m_listModel->set(index, object, &roles); - } - - if (roles.count()) - emitItemsChanged(index, 1, roles); - } -} - -/*! - \qmlmethod ListModel::setProperty(int index, string property, variant value) - - Changes the \a property of the item at \a index in the list model to \a value. - - \code - fruitModel.setProperty(3, "cost", 5.95) - \endcode - - The \a index must be an element in the list. - - \sa append() -*/ -void QQmlListModel::setProperty(int index, const QString& property, const QVariant& value) -{ - if (count() == 0 || index >= count() || index < 0) { - qmlWarning(this) << tr("set: index %1 out of range").arg(index); - return; - } - - if (m_dynamicRoles) { - int roleIndex = m_roles.indexOf(property); - if (roleIndex == -1) { - roleIndex = m_roles.count(); - m_roles.append(property); - } - if (m_modelObjects[index]->setValue(property.toUtf8(), value)) - emitItemsChanged(index, 1, QVector<int>(1, roleIndex)); - } else { - int roleIndex = m_listModel->setOrCreateProperty(index, property, value); - if (roleIndex != -1) - emitItemsChanged(index, 1, QVector<int>(1, roleIndex)); - } -} - -/*! - \qmlmethod ListModel::sync() - - Writes any unsaved changes to the list model after it has been modified - from a worker script. -*/ -void QQmlListModel::sync() -{ - // This is just a dummy method to make it look like sync() exists in - // ListModel (and not just QQmlListModelWorkerAgent) and to let - // us document sync(). - qmlWarning(this) << "List sync() can only be called from a WorkerScript"; -} - -bool QQmlListModelParser::verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding) -{ - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - const quint32 targetObjectIndex = binding->value.objectIndex; - const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); - QString objName = compilationUnit->stringAt(target->inheritedTypeNameIndex); - if (objName != listElementTypeName) { - const QMetaObject *mo = resolveType(objName); - if (mo != &QQmlListElement::staticMetaObject) { - error(target, QQmlListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - listElementTypeName = objName; // cache right name for next time - } - - if (!compilationUnit->stringAt(target->idNameIndex).isEmpty()) { - error(target->locationOfIdProperty, QQmlListModel::tr("ListElement: cannot use reserved \"id\" property")); - return false; - } - - const QV4::CompiledData::Binding *binding = target->bindingTable(); - for (quint32 i = 0; i < target->nBindings; ++i, ++binding) { - QString propName = compilationUnit->stringAt(binding->propertyNameIndex); - if (propName.isEmpty()) { - error(binding, QQmlListModel::tr("ListElement: cannot contain nested elements")); - return false; - } - if (!verifyProperty(compilationUnit, binding)) - return false; - } - } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); - if (!binding->isFunctionExpression() && !definesEmptyList(scriptStr)) { - QByteArray script = scriptStr.toUtf8(); - bool ok; - evaluateEnum(script, &ok); - if (!ok) { - error(binding, QQmlListModel::tr("ListElement: cannot use script for property value")); - return false; - } - } - } - - return true; -} - -bool QQmlListModelParser::applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex) -{ - const QString elementName = compilationUnit->stringAt(binding->propertyNameIndex); - - bool roleSet = false; - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { - const quint32 targetObjectIndex = binding->value.objectIndex; - const QV4::CompiledData::Object *target = compilationUnit->objectAt(targetObjectIndex); - - ListModel *subModel = nullptr; - if (outterElementIndex == -1) { - subModel = model; - } else { - const ListLayout::Role &role = model->getOrCreateListRole(elementName); - if (role.type == ListLayout::Role::List) { - subModel = model->getListProperty(outterElementIndex, role); - if (subModel == nullptr) { - subModel = new ListModel(role.subLayout, nullptr); - QVariant vModel = QVariant::fromValue(subModel); - model->setOrCreateProperty(outterElementIndex, elementName, vModel); - } - } - } - - int elementIndex = subModel ? subModel->appendElement() : -1; - - const QV4::CompiledData::Binding *subBinding = target->bindingTable(); - for (quint32 i = 0; i < target->nBindings; ++i, ++subBinding) { - roleSet |= applyProperty(compilationUnit, subBinding, subModel, elementIndex); - } - - } else { - QVariant value; - - if (binding->isTranslationBinding()) { - value = QVariant::fromValue<const QV4::CompiledData::Binding*>(binding); - } else if (binding->evaluatesToString()) { - value = binding->valueAsString(compilationUnit.data()); - } else if (binding->type == QV4::CompiledData::Binding::Type_Number) { - value = binding->valueAsNumber(compilationUnit->constants); - } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { - value = binding->valueAsBoolean(); - } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { - value = QVariant::fromValue(nullptr); - } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { - QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); - if (definesEmptyList(scriptStr)) { - const ListLayout::Role &role = model->getOrCreateListRole(elementName); - ListModel *emptyModel = new ListModel(role.subLayout, nullptr); - value = QVariant::fromValue(emptyModel); - } else if (binding->isFunctionExpression()) { - QQmlBinding::Identifier id = binding->value.compiledScriptIndex; - Q_ASSERT(id != QQmlBinding::Invalid); - - auto v4 = compilationUnit->engine; - QV4::Scope scope(v4); - // for now we do not provide a context object; data from the ListElement must be passed to the function - QV4::ScopedContext context(scope, QV4::QmlContext::create(v4->rootContext(), QQmlContextData::get(qmlContext(model->m_modelCache)), nullptr)); - QV4::ScopedFunctionObject function(scope, QV4::FunctionObject::createScriptFunction(context, compilationUnit->runtimeFunctions[id])); - - QV4::ReturnedValue result = function->call(v4->globalObject, nullptr, 0); - - QJSValue v; - QJSValuePrivate::setValue(&v, v4, result); - value.setValue<QJSValue>(v); - } else { - QByteArray script = scriptStr.toUtf8(); - bool ok; - value = evaluateEnum(script, &ok); - } - } else { - Q_UNREACHABLE(); - } - - model->setOrCreateProperty(outterElementIndex, elementName, value); - roleSet = true; - } - return roleSet; -} - -void QQmlListModelParser::verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) -{ - listElementTypeName = QString(); // unknown - - for (const QV4::CompiledData::Binding *binding : bindings) { - QString propName = compilationUnit->stringAt(binding->propertyNameIndex); - if (!propName.isEmpty()) { // isn't default property - error(binding, QQmlListModel::tr("ListModel: undefined property '%1'").arg(propName)); - return; - } - if (!verifyProperty(compilationUnit, binding)) - return; - } -} - -void QQmlListModelParser::applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) -{ - QQmlListModel *rv = static_cast<QQmlListModel *>(obj); - - rv->m_engine = qmlEngine(rv)->handle(); - rv->m_compilationUnit = compilationUnit; - - bool setRoles = false; - - for (const QV4::CompiledData::Binding *binding : bindings) { - if (binding->type != QV4::CompiledData::Binding::Type_Object) - continue; - setRoles |= applyProperty(compilationUnit, binding, rv->m_listModel, /*outter element index*/-1); - } - - if (setRoles == false) - qmlWarning(obj) << "All ListElement declarations are empty, no roles can be created unless dynamicRoles is set."; -} - -bool QQmlListModelParser::definesEmptyList(const QString &s) -{ - if (s.startsWith(QLatin1Char('[')) && s.endsWith(QLatin1Char(']'))) { - for (int i=1; i<s.length()-1; i++) { - if (!s[i].isSpace()) - return false; - } - return true; - } - return false; -} - - -/*! - \qmltype ListElement - \instantiates QQmlListElement - \inqmlmodule QtQml.Models - \brief Defines a data item in a ListModel. - \ingroup qtquick-models - - List elements are defined inside ListModel definitions, and represent items in a - list that will be displayed using ListView or \l Repeater items. - - List elements are defined like other QML elements except that they contain - a collection of \e role definitions instead of properties. Using the same - syntax as property definitions, roles both define how the data is accessed - and include the data itself. - - The names used for roles must begin with a lower-case letter and should be - common to all elements in a given model. Values must be simple constants; either - strings (quoted and optionally within a call to QT_TR_NOOP), boolean values - (true, false), numbers, or enumeration values (such as AlignText.AlignHCenter). - - Beginning with Qt 5.11 ListElement also allows assigning a function declaration to - a role. This allows the definition of ListElements with callable actions. - - \section1 Referencing Roles - - The role names are used by delegates to obtain data from list elements. - Each role name is accessible in the delegate's scope, and refers to the - corresponding role in the current element. Where a role name would be - ambiguous to use, it can be accessed via the \l{ListView::}{model} - property (e.g., \c{model.cost} instead of \c{cost}). - - \section1 Example Usage - - The following model defines a series of list elements, each of which - contain "name" and "cost" roles and their associated values. - - \snippet qml/listmodel/listelements.qml model - - The delegate obtains the name and cost for each element by simply referring - to \c name and \c cost: - - \snippet qml/listmodel/listelements.qml view - - \sa ListModel -*/ - -QT_END_NAMESPACE - -#include "moc_qqmllistmodel_p.cpp" diff --git a/src/qml/types/qqmllistmodel_p.h b/src/qml/types/qqmllistmodel_p.h deleted file mode 100644 index 471e33aa5a..0000000000 --- a/src/qml/types/qqmllistmodel_p.h +++ /dev/null @@ -1,208 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLLISTMODEL_H -#define QQMLLISTMODEL_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qqml.h> -#include <private/qqmlcustomparser_p.h> - -#include <QtCore/QObject> -#include <QtCore/QStringList> -#include <QtCore/QHash> -#include <QtCore/QList> -#include <QtCore/QVariant> -#include <QtCore/qabstractitemmodel.h> - -#include <private/qv4engine_p.h> -#include <private/qpodvector_p.h> - -QT_REQUIRE_CONFIG(qml_list_model); - -QT_BEGIN_NAMESPACE - - -class QQmlListModelWorkerAgent; -class ListModel; -class ListLayout; - -namespace QV4 { -struct ModelObject; -} - -class Q_QML_PRIVATE_EXPORT QQmlListModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) - Q_PROPERTY(bool dynamicRoles READ dynamicRoles WRITE setDynamicRoles) - -public: - QQmlListModel(QObject *parent=nullptr); - ~QQmlListModel(); - - QModelIndex index(int row, int column, const QModelIndex &parent) const override; - int rowCount(const QModelIndex &parent) const override; - QVariant data(const QModelIndex &index, int role) const override; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; - QHash<int,QByteArray> roleNames() const override; - - QVariant data(int index, int role) const; - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV4Function *args); - Q_INVOKABLE void append(QQmlV4Function *args); - Q_INVOKABLE void insert(QQmlV4Function *args); - Q_INVOKABLE QJSValue get(int index) const; - Q_INVOKABLE void set(int index, const QJSValue &value); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - QQmlListModelWorkerAgent *agent(); - - bool dynamicRoles() const { return m_dynamicRoles; } - void setDynamicRoles(bool enableDynamicRoles); - -Q_SIGNALS: - void countChanged(); - -private: - friend class QQmlListModelParser; - friend class QQmlListModelWorkerAgent; - friend class ModelObject; - friend struct QV4::ModelObject; - friend class ModelNodeMetaObject; - friend class ListModel; - friend class ListElement; - friend class DynamicRoleModelNode; - friend class DynamicRoleModelNodeMetaObject; - friend struct StringOrTranslation; - - // Constructs a flat list model for a worker agent - QQmlListModel(QQmlListModel *orig, QQmlListModelWorkerAgent *agent); - QQmlListModel(const QQmlListModel *owner, ListModel *data, QV4::ExecutionEngine *engine, QObject *parent=nullptr); - - QV4::ExecutionEngine *engine() const; - - inline bool canMove(int from, int to, int n) const { return !(from+n > count() || to+n > count() || from < 0 || to < 0 || n < 0); } - - QQmlListModelWorkerAgent *m_agent; - mutable QV4::ExecutionEngine *m_engine; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_compilationUnit; - bool m_mainThread; - bool m_primary; - - bool m_dynamicRoles; - - ListLayout *m_layout; - ListModel *m_listModel; - - QVector<class DynamicRoleModelNode *> m_modelObjects; - QVector<QString> m_roles; - - struct ElementSync - { - DynamicRoleModelNode *src = nullptr; - DynamicRoleModelNode *target = nullptr; - int srcIndex = -1; - int targetIndex = -1; - QVector<int> changedRoles; - }; - - static bool sync(QQmlListModel *src, QQmlListModel *target); - static QQmlListModel *createWithOwner(QQmlListModel *newOwner); - - void emitItemsChanged(int index, int count, const QVector<int> &roles); - void emitItemsAboutToBeInserted(int index, int count); - void emitItemsInserted(); - - void removeElements(int index, int removeCount); -}; - -// ### FIXME -class QQmlListElement : public QObject -{ -Q_OBJECT -}; - -class QQmlListModelParser : public QQmlCustomParser -{ -public: - enum PropertyType { - Invalid, - Boolean, - Number, - String, - Script - }; - - - QQmlListModelParser() : QQmlCustomParser(QQmlCustomParser::AcceptsSignalHandlers) {} - - void verifyBindings(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; - void applyBindings(QObject *obj, const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override; - -private: - bool verifyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding); - // returns true if a role was set - bool applyProperty(const QQmlRefPointer<QV4::CompiledData::CompilationUnit> &compilationUnit, const QV4::CompiledData::Binding *binding, ListModel *model, int outterElementIndex); - - static bool definesEmptyList(const QString &); - - QString listElementTypeName; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlListModel) -QML_DECLARE_TYPE(QQmlListElement) - -#endif // QQMLLISTMODEL_H diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h deleted file mode 100644 index 2876c71de6..0000000000 --- a/src/qml/types/qqmllistmodel_p_p.h +++ /dev/null @@ -1,428 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLLISTMODEL_P_P_H -#define QQMLLISTMODEL_P_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include "qqmllistmodel_p.h" -#include <private/qqmlengine_p.h> -#include <private/qqmlopenmetaobject_p.h> -#include <private/qv4qobjectwrapper_p.h> -#include <qqml.h> - -QT_REQUIRE_CONFIG(qml_list_model); - -QT_BEGIN_NAMESPACE - - -class DynamicRoleModelNode; - -class DynamicRoleModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - DynamicRoleModelNodeMetaObject(DynamicRoleModelNode *object); - ~DynamicRoleModelNodeMetaObject(); - - bool m_enabled; - -protected: - void propertyWrite(int index) override; - void propertyWritten(int index) override; - -private: - DynamicRoleModelNode *m_owner; -}; - -class DynamicRoleModelNode : public QObject -{ - Q_OBJECT -public: - DynamicRoleModelNode(QQmlListModel *owner, int uid); - - static DynamicRoleModelNode *create(const QVariantMap &obj, QQmlListModel *owner); - - void updateValues(const QVariantMap &object, QVector<int> &roles); - - QVariant getValue(const QString &name) const - { - return m_meta->value(name.toUtf8()); - } - - bool setValue(const QByteArray &name, const QVariant &val) - { - return m_meta->setValue(name, val); - } - - void setNodeUpdatesEnabled(bool enable) - { - m_meta->m_enabled = enable; - } - - int getUid() const - { - return m_uid; - } - - static QVector<int> sync(DynamicRoleModelNode *src, DynamicRoleModelNode *target); - -private: - QQmlListModel *m_owner; - int m_uid; - DynamicRoleModelNodeMetaObject *m_meta; - - friend class DynamicRoleModelNodeMetaObject; -}; - -class ModelNodeMetaObject : public QQmlOpenMetaObject -{ -public: - ModelNodeMetaObject(QObject *object, QQmlListModel *model, int elementIndex); - ~ModelNodeMetaObject(); - - QAbstractDynamicMetaObject *toDynamicMetaObject(QObject *object) override; - - static ModelNodeMetaObject *get(QObject *obj); - - bool m_enabled; - QQmlListModel *m_model; - int m_elementIndex; - - void updateValues(); - void updateValues(const QVector<int> &roles); - - bool initialized() const { return m_initialized; } - -protected: - void propertyWritten(int index) override; - -private: - using QQmlOpenMetaObject::setValue; - - void emitDirectNotifies(const int *changedRoles, int roleCount); - - void initialize(); - bool m_initialized; -}; - -namespace QV4 { - -namespace Heap { - -struct ModelObject : public QObjectWrapper { - void init(QObject *object, QQmlListModel *model) - { - QObjectWrapper::init(object); - m_model = model; - QObjectPrivate *op = QObjectPrivate::get(object); - m_nodeModelMetaObject = static_cast<ModelNodeMetaObject *>(op->metaObject); - } - void destroy() { QObjectWrapper::destroy(); } - int elementIndex() const { return m_nodeModelMetaObject->m_elementIndex; } - QQmlListModel *m_model; - ModelNodeMetaObject *m_nodeModelMetaObject; -}; - -} - -struct ModelObject : public QObjectWrapper -{ - V4_OBJECT2(ModelObject, QObjectWrapper) - V4_NEEDS_DESTROY - - ListModel *listModel() const { return d()->m_model->m_listModel; } - -protected: - static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver); - static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); - static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); - static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); - static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); -}; - -} // namespace QV4 - -class ListLayout -{ -public: - ListLayout() : currentBlock(0), currentBlockOffset(0) {} - ListLayout(const ListLayout *other); - ~ListLayout(); - - class Role - { - public: - - Role() : type(Invalid), blockIndex(-1), blockOffset(-1), index(-1), subLayout(0) {} - explicit Role(const Role *other); - ~Role(); - - // This enum must be kept in sync with the roleTypeNames variable in qqmllistmodel.cpp - enum DataType - { - Invalid = -1, - - String, - Number, - Bool, - List, - QObject, - VariantMap, - DateTime, - Function, - - MaxDataType - }; - - QString name; - DataType type; - int blockIndex; - int blockOffset; - int index; - ListLayout *subLayout; - }; - - const Role *getRoleOrCreate(const QString &key, const QVariant &data); - const Role &getRoleOrCreate(QV4::String *key, Role::DataType type); - const Role &getRoleOrCreate(const QString &key, Role::DataType type); - - const Role &getExistingRole(int index) const { return *roles.at(index); } - const Role *getExistingRole(const QString &key) const; - const Role *getExistingRole(QV4::String *key) const; - - int roleCount() const { return roles.count(); } - - static void sync(ListLayout *src, ListLayout *target); - -private: - const Role &createRole(const QString &key, Role::DataType type); - - int currentBlock; - int currentBlockOffset; - QVector<Role *> roles; - QStringHash<Role *> roleHash; -}; - -struct StringOrTranslation -{ - explicit StringOrTranslation(const QString &s); - explicit StringOrTranslation(const QV4::CompiledData::Binding *binding); - ~StringOrTranslation(); - bool isSet() const { return d.flag(); } - bool isTranslation() const { return d.isT2(); } - void setString(const QString &s); - void setTranslation(const QV4::CompiledData::Binding *binding); - QString toString(const QQmlListModel *owner) const; - QString asString() const; -private: - void clear(); - QBiPointer<QStringData, const QV4::CompiledData::Binding> d; -}; - -/*! -\internal -*/ -class ListElement -{ -public: - - ListElement(); - ListElement(int existingUid); - ~ListElement(); - - static QVector<int> sync(ListElement *src, ListLayout *srcLayout, ListElement *target, ListLayout *targetLayout); - - enum - { - BLOCK_SIZE = 64 - sizeof(int) - sizeof(ListElement *) - sizeof(ModelNodeMetaObject *) - }; - -private: - - void destroy(ListLayout *layout); - - int setVariantProperty(const ListLayout::Role &role, const QVariant &d); - - int setJsProperty(const ListLayout::Role &role, const QV4::Value &d, QV4::ExecutionEngine *eng); - - int setStringProperty(const ListLayout::Role &role, const QString &s); - int setDoubleProperty(const ListLayout::Role &role, double n); - int setBoolProperty(const ListLayout::Role &role, bool b); - int setListProperty(const ListLayout::Role &role, ListModel *m); - int setQObjectProperty(const ListLayout::Role &role, QObject *o); - int setVariantMapProperty(const ListLayout::Role &role, QV4::Object *o); - int setVariantMapProperty(const ListLayout::Role &role, QVariantMap *m); - int setDateTimeProperty(const ListLayout::Role &role, const QDateTime &dt); - int setFunctionProperty(const ListLayout::Role &role, const QJSValue &f); - int setTranslationProperty(const ListLayout::Role &role, const QV4::CompiledData::Binding *b); - - void setStringPropertyFast(const ListLayout::Role &role, const QString &s); - void setDoublePropertyFast(const ListLayout::Role &role, double n); - void setBoolPropertyFast(const ListLayout::Role &role, bool b); - void setQObjectPropertyFast(const ListLayout::Role &role, QObject *o); - void setListPropertyFast(const ListLayout::Role &role, ListModel *m); - void setVariantMapFast(const ListLayout::Role &role, QV4::Object *o); - void setDateTimePropertyFast(const ListLayout::Role &role, const QDateTime &dt); - void setFunctionPropertyFast(const ListLayout::Role &role, const QJSValue &f); - - void clearProperty(const ListLayout::Role &role); - - QVariant getProperty(const ListLayout::Role &role, const QQmlListModel *owner, QV4::ExecutionEngine *eng); - ListModel *getListProperty(const ListLayout::Role &role); - StringOrTranslation *getStringProperty(const ListLayout::Role &role); - QObject *getQObjectProperty(const ListLayout::Role &role); - QPointer<QObject> *getGuardProperty(const ListLayout::Role &role); - QVariantMap *getVariantMapProperty(const ListLayout::Role &role); - QDateTime *getDateTimeProperty(const ListLayout::Role &role); - QJSValue *getFunctionProperty(const ListLayout::Role &role); - - inline char *getPropertyMemory(const ListLayout::Role &role); - - int getUid() const { return uid; } - - ModelNodeMetaObject *objectCache(); - - char data[BLOCK_SIZE]; - ListElement *next; - - int uid; - QObject *m_objectCache; - - friend class ListModel; -}; - -/*! -\internal -*/ -class ListModel -{ -public: - - ListModel(ListLayout *layout, QQmlListModel *modelCache); - ~ListModel() {} - - void destroy(); - - int setOrCreateProperty(int elementIndex, const QString &key, const QVariant &data); - int setExistingProperty(int uid, const QString &key, const QV4::Value &data, QV4::ExecutionEngine *eng); - - QVariant getProperty(int elementIndex, int roleIndex, const QQmlListModel *owner, QV4::ExecutionEngine *eng); - ListModel *getListProperty(int elementIndex, const ListLayout::Role &role); - - int roleCount() const - { - return m_layout->roleCount(); - } - - const ListLayout::Role &getExistingRole(int index) const - { - return m_layout->getExistingRole(index); - } - - const ListLayout::Role *getExistingRole(QV4::String *key) const - { - return m_layout->getExistingRole(key); - } - - const ListLayout::Role &getOrCreateListRole(const QString &name) - { - return m_layout->getRoleOrCreate(name, ListLayout::Role::List); - } - - int elementCount() const - { - return elements.count(); - } - - void set(int elementIndex, QV4::Object *object, QVector<int> *roles); - void set(int elementIndex, QV4::Object *object); - - int append(QV4::Object *object); - void insert(int elementIndex, QV4::Object *object); - - Q_REQUIRED_RESULT QVector<std::function<void()>> remove(int index, int count); - - int appendElement(); - void insertElement(int index); - - void move(int from, int to, int n); - - static bool sync(ListModel *src, ListModel *target); - - QObject *getOrCreateModelObject(QQmlListModel *model, int elementIndex); - -private: - QPODVector<ListElement *, 4> elements; - ListLayout *m_layout; - - QQmlListModel *m_modelCache; - - struct ElementSync - { - ListElement *src = nullptr; - ListElement *target = nullptr; - int srcIndex = -1; - int targetIndex = -1; - QVector<int> changedRoles; - }; - - void newElement(int index); - - void updateCacheIndices(int start = 0, int end = -1); - - friend class ListElement; - friend class QQmlListModelWorkerAgent; - friend class QQmlListModelParser; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(ListModel *); - -#endif // QQUICKLISTMODEL_P_P_H diff --git a/src/qml/types/qqmllistmodelworkeragent.cpp b/src/qml/types/qqmllistmodelworkeragent.cpp deleted file mode 100644 index f7cb08dcf4..0000000000 --- a/src/qml/types/qqmllistmodelworkeragent.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmllistmodelworkeragent_p.h" -#include "qqmllistmodel_p_p.h" -#include <private/qqmldata_p.h> -#include <private/qqmlengine_p.h> -#include <qqmlinfo.h> - -#include <QtCore/qcoreevent.h> -#include <QtCore/qcoreapplication.h> -#include <QtCore/qdebug.h> - - -QT_BEGIN_NAMESPACE - -QQmlListModelWorkerAgent::Sync::~Sync() -{ -} - -QQmlListModelWorkerAgent::QQmlListModelWorkerAgent(QQmlListModel *model) -: m_ref(1), m_orig(model), m_copy(new QQmlListModel(model, this)) -{ -} - -QQmlListModelWorkerAgent::~QQmlListModelWorkerAgent() -{ - mutex.lock(); - syncDone.wakeAll(); - mutex.unlock(); -} - -void QQmlListModelWorkerAgent::setEngine(QV4::ExecutionEngine *eng) -{ - m_copy->m_engine = eng; -} - -void QQmlListModelWorkerAgent::addref() -{ - m_ref.ref(); -} - -void QQmlListModelWorkerAgent::release() -{ - bool del = !m_ref.deref(); - - if (del) - deleteLater(); -} - -void QQmlListModelWorkerAgent::modelDestroyed() -{ - m_orig = nullptr; -} - -int QQmlListModelWorkerAgent::count() const -{ - return m_copy->count(); -} - -void QQmlListModelWorkerAgent::clear() -{ - m_copy->clear(); -} - -void QQmlListModelWorkerAgent::remove(QQmlV4Function *args) -{ - m_copy->remove(args); -} - -void QQmlListModelWorkerAgent::append(QQmlV4Function *args) -{ - m_copy->append(args); -} - -void QQmlListModelWorkerAgent::insert(QQmlV4Function *args) -{ - m_copy->insert(args); -} - -QJSValue QQmlListModelWorkerAgent::get(int index) const -{ - return m_copy->get(index); -} - -void QQmlListModelWorkerAgent::set(int index, const QJSValue &value) -{ - m_copy->set(index, value); -} - -void QQmlListModelWorkerAgent::setProperty(int index, const QString& property, const QVariant& value) -{ - m_copy->setProperty(index, property, value); -} - -void QQmlListModelWorkerAgent::move(int from, int to, int count) -{ - m_copy->move(from, to, count); -} - -void QQmlListModelWorkerAgent::sync() -{ - Sync *s = new Sync(m_copy); - - mutex.lock(); - QCoreApplication::postEvent(this, s); - syncDone.wait(&mutex); - mutex.unlock(); -} - -bool QQmlListModelWorkerAgent::event(QEvent *e) -{ - if (e->type() == QEvent::User) { - bool cc = false; - QMutexLocker locker(&mutex); - if (m_orig) { - Sync *s = static_cast<Sync *>(e); - - cc = (m_orig->count() != s->list->count()); - - Q_ASSERT(m_orig->m_dynamicRoles == s->list->m_dynamicRoles); - if (m_orig->m_dynamicRoles) - QQmlListModel::sync(s->list, m_orig); - else - ListModel::sync(s->list->m_listModel, m_orig->m_listModel); - } - - syncDone.wakeAll(); - locker.unlock(); - - if (cc) - emit m_orig->countChanged(); - return true; - } - - return QObject::event(e); -} - -QT_END_NAMESPACE - -#include "moc_qqmllistmodelworkeragent_p.cpp" diff --git a/src/qml/types/qqmllistmodelworkeragent_p.h b/src/qml/types/qqmllistmodelworkeragent_p.h deleted file mode 100644 index 69d1785618..0000000000 --- a/src/qml/types/qqmllistmodelworkeragent_p.h +++ /dev/null @@ -1,140 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKLISTMODELWORKERAGENT_P_H -#define QQUICKLISTMODELWORKERAGENT_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qqml.h> - -#include <QEvent> -#include <QMutex> -#include <QWaitCondition> - -#include <private/qv8engine_p.h> - -QT_REQUIRE_CONFIG(qml_list_model); - -QT_BEGIN_NAMESPACE - - -class QQmlListModel; - -class QQmlListModelWorkerAgent : public QObject -{ - Q_OBJECT - Q_PROPERTY(int count READ count) - -public: - QQmlListModelWorkerAgent(QQmlListModel *); - ~QQmlListModelWorkerAgent(); - void setEngine(QV4::ExecutionEngine *eng); - - void addref(); - void release(); - - int count() const; - - Q_INVOKABLE void clear(); - Q_INVOKABLE void remove(QQmlV4Function *args); - Q_INVOKABLE void append(QQmlV4Function *args); - Q_INVOKABLE void insert(QQmlV4Function *args); - Q_INVOKABLE QJSValue get(int index) const; - Q_INVOKABLE void set(int index, const QJSValue &value); - Q_INVOKABLE void setProperty(int index, const QString& property, const QVariant& value); - Q_INVOKABLE void move(int from, int to, int count); - Q_INVOKABLE void sync(); - - struct VariantRef - { - VariantRef() : a(nullptr) {} - VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); } - VariantRef(QQmlListModelWorkerAgent *_a) : a(_a) { if (a) a->addref(); } - ~VariantRef() { if (a) a->release(); } - - VariantRef &operator=(const VariantRef &o) { - if (o.a) o.a->addref(); - if (a) a->release(); - a = o.a; - return *this; - } - - QQmlListModelWorkerAgent *a; - }; - - void modelDestroyed(); -protected: - bool event(QEvent *) override; - -private: - friend class QQuickWorkerScriptEnginePrivate; - friend class QQmlListModel; - - struct Sync : public QEvent { - Sync(QQmlListModel *l) - : QEvent(QEvent::User) - , list(l) - {} - ~Sync(); - QQmlListModel *list; - }; - - QAtomicInt m_ref; - QQmlListModel *m_orig; - QQmlListModel *m_copy; - QMutex mutex; - QWaitCondition syncDone; -}; - -QT_END_NAMESPACE - -Q_DECLARE_METATYPE(QQmlListModelWorkerAgent::VariantRef) - -#endif // QQUICKLISTMODELWORKERAGENT_P_H - diff --git a/src/qml/types/qqmlmodelsmodule.cpp b/src/qml/types/qqmlmodelsmodule.cpp deleted file mode 100644 index 840b435d18..0000000000 --- a/src/qml/types/qqmlmodelsmodule.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 Research In Motion. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlmodelsmodule_p.h" -#include <QtCore/qitemselectionmodel.h> -#if QT_CONFIG(qml_list_model) -#include <private/qqmllistmodel_p.h> -#endif -#if QT_CONFIG(qml_delegate_model) -#include <private/qqmldelegatemodel_p.h> -#include <private/qqmldelegatecomponent_p.h> -#endif -#include <private/qqmlobjectmodel_p.h> -#include <private/qqmltablemodel_p.h> -#include <private/qqmltablemodelcolumn_p.h> -#include <private/qqmlinstantiator_p.h> -#include <private/qquickpackage_p.h> - -QT_BEGIN_NAMESPACE - -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - -void QQmlModelsModule::registerQmlTypes() -{ - // Don't add anything here. These are only for backwards compatibility. - qmlRegisterType<QQmlInstantiator>("QtQml", 2, 1, "Instantiator"); // Only available in >= 2.1 - qmlRegisterType<QQmlInstanceModel>(); -} - -void QQmlModelsModule::registerQuickTypes() -{ - // Don't add anything here. These are only for backwards compatibility. - - const char uri[] = "QtQuick"; - - qmlRegisterType<QQmlInstantiator>(uri, 2, 1, "Instantiator"); - qmlRegisterType<QQmlInstanceModel>(); -#if QT_CONFIG(qml_list_model) - qmlRegisterType<QQmlListElement>(uri, 2, 0, "ListElement"); - qmlRegisterCustomType<QQmlListModel>(uri, 2, 0, "ListModel", new QQmlListModelParser); -#endif - qmlRegisterType<QQuickPackage>(uri, 2, 0, "Package"); -#if QT_CONFIG(qml_delegate_model) - qmlRegisterType<QQmlDelegateModel>(uri, 2, 0, "VisualDataModel"); - qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 0, "VisualDataGroup"); -#endif - qmlRegisterType<QQmlObjectModel>(uri, 2, 0, "VisualItemModel"); -} - -#endif // QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - -void QQmlModelsModule::defineModule() -{ - const char uri[] = "QtQml.Models"; - -#if QT_CONFIG(qml_list_model) - qmlRegisterType<QQmlListElement>(uri, 2, 1, "ListElement"); - qmlRegisterCustomType<QQmlListModel>(uri, 2, 1, "ListModel", new QQmlListModelParser); -#endif -#if QT_CONFIG(qml_delegate_model) - qmlRegisterType<QQmlDelegateModel>(uri, 2, 1, "DelegateModel"); - qmlRegisterType<QQmlDelegateModelGroup>(uri, 2, 1, "DelegateModelGroup"); -#endif - qmlRegisterType<QQmlObjectModel>(uri, 2, 1, "ObjectModel"); - qmlRegisterType<QQmlObjectModel,3>(uri, 2, 3, "ObjectModel"); - - qmlRegisterType<QItemSelectionModel>(uri, 2, 2, "ItemSelectionModel"); - - qmlRegisterType<QQuickPackage>(uri, 2, 14, "Package"); - qmlRegisterType<QQmlInstantiator>(uri, 2, 14, "Instantiator"); - qmlRegisterType<QQmlInstanceModel>(); -} - -void QQmlModelsModule::defineLabsModule() -{ - const char uri[] = "Qt.labs.qmlmodels"; - -#if QT_CONFIG(qml_delegate_model) - qmlRegisterUncreatableType<QQmlAbstractDelegateComponent>(uri, 1, 0, "AbstractDelegateComponent", QQmlAbstractDelegateComponent::tr("Cannot create instance of abstract class AbstractDelegateComponent.")); - qmlRegisterType<QQmlDelegateChooser>(uri, 1, 0, "DelegateChooser"); - qmlRegisterType<QQmlDelegateChoice>(uri, 1, 0, "DelegateChoice"); -#endif - qmlRegisterType<QQmlTableModel>(uri, 1, 0, "TableModel"); - qmlRegisterType<QQmlTableModelColumn>(uri, 1, 0, "TableModelColumn"); -} - -QT_END_NAMESPACE diff --git a/src/qml/types/qqmlobjectmodel.cpp b/src/qml/types/qqmlobjectmodel.cpp deleted file mode 100644 index b6330b4295..0000000000 --- a/src/qml/types/qqmlobjectmodel.cpp +++ /dev/null @@ -1,431 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlobjectmodel_p.h" - -#include <QtCore/qcoreapplication.h> -#include <QtQml/qqmlcontext.h> -#include <QtQml/qqmlengine.h> -#include <QtQml/qqmlinfo.h> - -#include <private/qqmlchangeset_p.h> -#include <private/qqmlglobal_p.h> -#include <private/qobject_p.h> -#include <private/qpodvector_p.h> - -#include <QtCore/qhash.h> -#include <QtCore/qlist.h> - -QT_BEGIN_NAMESPACE - -QHash<QObject*, QQmlObjectModelAttached*> QQmlObjectModelAttached::attachedProperties; - - -class QQmlObjectModelPrivate : public QObjectPrivate -{ - Q_DECLARE_PUBLIC(QQmlObjectModel) -public: - class Item { - public: - Item(QObject *i) : item(i), ref(0) {} - - void addRef() { ++ref; } - bool deref() { return --ref == 0; } - - QObject *item; - int ref; - }; - - QQmlObjectModelPrivate() : QObjectPrivate(), moveId(0) {} - - static void children_append(QQmlListProperty<QObject> *prop, QObject *item) { - int index = static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count(); - static_cast<QQmlObjectModelPrivate *>(prop->data)->insert(index, item); - } - - static int children_count(QQmlListProperty<QObject> *prop) { - return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.count(); - } - - static QObject *children_at(QQmlListProperty<QObject> *prop, int index) { - return static_cast<QQmlObjectModelPrivate *>(prop->data)->children.at(index).item; - } - - static void children_clear(QQmlListProperty<QObject> *prop) { - static_cast<QQmlObjectModelPrivate *>(prop->data)->clear(); - } - - void insert(int index, QObject *item) { - Q_Q(QQmlObjectModel); - children.insert(index, Item(item)); - for (int i = index; i < children.count(); ++i) { - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); - attached->setIndex(i); - } - QQmlChangeSet changeSet; - changeSet.insert(index, 1); - emit q->modelUpdated(changeSet, false); - emit q->countChanged(); - emit q->childrenChanged(); - } - - void move(int from, int to, int n) { - Q_Q(QQmlObjectModel); - if (from > to) { - // Only move forwards - flip if backwards moving - int tfrom = from; - int tto = to; - from = tto; - to = tto+n; - n = tfrom-tto; - } - - QPODVector<QQmlObjectModelPrivate::Item, 4> store; - for (int i = 0; i < to - from; ++i) - store.append(children[from + n + i]); - for (int i = 0; i < n; ++i) - store.append(children[from + i]); - - for (int i = 0; i < store.count(); ++i) { - children[from + i] = store[i]; - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(from + i).item); - attached->setIndex(from + i); - } - - QQmlChangeSet changeSet; - changeSet.move(from, to, n, ++moveId); - emit q->modelUpdated(changeSet, false); - emit q->childrenChanged(); - } - - void remove(int index, int n) { - Q_Q(QQmlObjectModel); - for (int i = index; i < index + n; ++i) { - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); - attached->setIndex(-1); - } - children.erase(children.begin() + index, children.begin() + index + n); - for (int i = index; i < children.count(); ++i) { - QQmlObjectModelAttached *attached = QQmlObjectModelAttached::properties(children.at(i).item); - attached->setIndex(i); - } - QQmlChangeSet changeSet; - changeSet.remove(index, n); - emit q->modelUpdated(changeSet, false); - emit q->countChanged(); - emit q->childrenChanged(); - } - - void clear() { - Q_Q(QQmlObjectModel); - for (const Item &child : qAsConst(children)) - emit q->destroyingItem(child.item); - remove(0, children.count()); - } - - int indexOf(QObject *item) const { - for (int i = 0; i < children.count(); ++i) - if (children.at(i).item == item) - return i; - return -1; - } - - uint moveId; - QList<Item> children; -}; - - -/*! - \qmltype ObjectModel - \instantiates QQmlObjectModel - \inqmlmodule QtQml.Models - \ingroup qtquick-models - \brief Defines a set of items to be used as a model. - - An ObjectModel contains the visual items to be used in a view. - When an ObjectModel is used in a view, the view does not require - a delegate since the ObjectModel already contains the visual - delegate (items). - - An item can determine its index within the - model via the \l{ObjectModel::index}{index} attached property. - - The example below places three colored rectangles in a ListView. - \code - import QtQuick 2.0 - import QtQml.Models 2.1 - - Rectangle { - ObjectModel { - id: itemModel - Rectangle { height: 30; width: 80; color: "red" } - Rectangle { height: 30; width: 80; color: "green" } - Rectangle { height: 30; width: 80; color: "blue" } - } - - ListView { - anchors.fill: parent - model: itemModel - } - } - \endcode - - \image objectmodel.png - - \sa {Qt Quick Examples - Views} -*/ - -QQmlObjectModel::QQmlObjectModel(QObject *parent) - : QQmlInstanceModel(*(new QQmlObjectModelPrivate), parent) -{ -} - -/*! - \qmlattachedproperty int QtQml.Models::ObjectModel::index - This attached property holds the index of this delegate's item within the model. - - It is attached to each instance of the delegate. -*/ - -QQmlListProperty<QObject> QQmlObjectModel::children() -{ - Q_D(QQmlObjectModel); - return QQmlListProperty<QObject>(this, - d, - d->children_append, - d->children_count, - d->children_at, - d->children_clear); -} - -/*! - \qmlproperty int QtQml.Models::ObjectModel::count - - The number of items in the model. This property is readonly. -*/ -int QQmlObjectModel::count() const -{ - Q_D(const QQmlObjectModel); - return d->children.count(); -} - -bool QQmlObjectModel::isValid() const -{ - return true; -} - -QObject *QQmlObjectModel::object(int index, QQmlIncubator::IncubationMode) -{ - Q_D(QQmlObjectModel); - QQmlObjectModelPrivate::Item &item = d->children[index]; - item.addRef(); - if (item.ref == 1) { - emit initItem(index, item.item); - emit createdItem(index, item.item); - } - return item.item; -} - -QQmlInstanceModel::ReleaseFlags QQmlObjectModel::release(QObject *item) -{ - Q_D(QQmlObjectModel); - int idx = d->indexOf(item); - if (idx >= 0) { - if (!d->children[idx].deref()) - return QQmlInstanceModel::Referenced; - } - return nullptr; -} - -QVariant QQmlObjectModel::variantValue(int index, const QString &role) -{ - Q_D(QQmlObjectModel); - if (index < 0 || index >= d->children.count()) - return QString(); - return QQmlEngine::contextForObject(d->children.at(index).item)->contextProperty(role); -} - -QQmlIncubator::Status QQmlObjectModel::incubationStatus(int) -{ - return QQmlIncubator::Ready; -} - -int QQmlObjectModel::indexOf(QObject *item, QObject *) const -{ - Q_D(const QQmlObjectModel); - return d->indexOf(item); -} - -QQmlObjectModelAttached *QQmlObjectModel::qmlAttachedProperties(QObject *obj) -{ - return QQmlObjectModelAttached::properties(obj); -} - -/*! - \qmlmethod object QtQml.Models::ObjectModel::get(int index) - \since 5.6 - - Returns the item at \a index in the model. This allows the item - to be accessed or modified from JavaScript: - - \code - Component.onCompleted: { - objectModel.append(objectComponent.createObject()) - console.log(objectModel.get(0).objectName); - objectModel.get(0).objectName = "first"; - } - \endcode - - The \a index must be an element in the list. - - \sa append() -*/ -QObject *QQmlObjectModel::get(int index) const -{ - Q_D(const QQmlObjectModel); - if (index < 0 || index >= d->children.count()) - return nullptr; - return d->children.at(index).item; -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::append(object item) - \since 5.6 - - Appends a new item to the end of the model. - - \code - objectModel.append(objectComponent.createObject()) - \endcode - - \sa insert(), remove() -*/ -void QQmlObjectModel::append(QObject *object) -{ - Q_D(QQmlObjectModel); - d->insert(count(), object); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::insert(int index, object item) - \since 5.6 - - Inserts a new item to the model at position \a index. - - \code - objectModel.insert(2, objectComponent.createObject()) - \endcode - - The \a index must be to an existing item in the list, or one past - the end of the list (equivalent to append). - - \sa append(), remove() -*/ -void QQmlObjectModel::insert(int index, QObject *object) -{ - Q_D(QQmlObjectModel); - if (index < 0 || index > count()) { - qmlWarning(this) << tr("insert: index %1 out of range").arg(index); - return; - } - d->insert(index, object); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::move(int from, int to, int n = 1) - \since 5.6 - - Moves \a n items \a from one position \a to another. - - The from and to ranges must exist; for example, to move the first 3 items - to the end of the model: - - \code - objectModel.move(0, objectModel.count - 3, 3) - \endcode - - \sa append() -*/ -void QQmlObjectModel::move(int from, int to, int n) -{ - Q_D(QQmlObjectModel); - if (n <= 0 || from == to) - return; - if (from < 0 || to < 0 || from + n > count() || to + n > count()) { - qmlWarning(this) << tr("move: out of range"); - return; - } - d->move(from, to, n); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::remove(int index, int n = 1) - \since 5.6 - - Removes the items at \a index from the model. - - \sa clear() -*/ -void QQmlObjectModel::remove(int index, int n) -{ - Q_D(QQmlObjectModel); - if (index < 0 || n <= 0 || index + n > count()) { - qmlWarning(this) << tr("remove: indices [%1 - %2] out of range [0 - %3]").arg(index).arg(index+n).arg(count()); - return; - } - d->remove(index, n); -} - -/*! - \qmlmethod QtQml.Models::ObjectModel::clear() - \since 5.6 - - Clears all items from the model. - - \sa append(), remove() -*/ -void QQmlObjectModel::clear() -{ - Q_D(QQmlObjectModel); - d->clear(); -} - -QT_END_NAMESPACE - -#include "moc_qqmlobjectmodel_p.cpp" diff --git a/src/qml/types/qqmlobjectmodel_p.h b/src/qml/types/qqmlobjectmodel_p.h deleted file mode 100644 index 1284ba1780..0000000000 --- a/src/qml/types/qqmlobjectmodel_p.h +++ /dev/null @@ -1,194 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLINSTANCEMODEL_P_H -#define QQMLINSTANCEMODEL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <private/qtqmlglobal_p.h> -#include <private/qqmlincubator_p.h> -#include <QtQml/qqml.h> -#include <QtCore/qobject.h> - -QT_BEGIN_NAMESPACE - -class QObject; -class QQmlChangeSet; -class QAbstractItemModel; - -class Q_QML_PRIVATE_EXPORT QQmlInstanceModel : public QObject -{ - Q_OBJECT - - Q_PROPERTY(int count READ count NOTIFY countChanged) - -public: - virtual ~QQmlInstanceModel() {} - - enum ReleaseFlag { Referenced = 0x01, Destroyed = 0x02 }; - Q_DECLARE_FLAGS(ReleaseFlags, ReleaseFlag) - - virtual int count() const = 0; - virtual bool isValid() const = 0; - virtual QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) = 0; - virtual ReleaseFlags release(QObject *object) = 0; - virtual void cancel(int) {} - QString stringValue(int index, const QString &role) { return variantValue(index, role).toString(); } - virtual QVariant variantValue(int, const QString &) = 0; - virtual void setWatchedRoles(const QList<QByteArray> &roles) = 0; - virtual QQmlIncubator::Status incubationStatus(int index) = 0; - - virtual int indexOf(QObject *object, QObject *objectContext) const = 0; - virtual const QAbstractItemModel *abstractItemModel() const { return nullptr; } - -Q_SIGNALS: - void countChanged(); - void modelUpdated(const QQmlChangeSet &changeSet, bool reset); - void createdItem(int index, QObject *object); - void initItem(int index, QObject *object); - void destroyingItem(QObject *object); - -protected: - QQmlInstanceModel(QObjectPrivate &dd, QObject *parent = nullptr) - : QObject(dd, parent) {} - -private: - Q_DISABLE_COPY(QQmlInstanceModel) -}; - -class QQmlObjectModelAttached; -class QQmlObjectModelPrivate; -class Q_QML_PRIVATE_EXPORT QQmlObjectModel : public QQmlInstanceModel -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQmlObjectModel) - - Q_PROPERTY(QQmlListProperty<QObject> children READ children NOTIFY childrenChanged DESIGNABLE false) - Q_CLASSINFO("DefaultProperty", "children") - -public: - QQmlObjectModel(QObject *parent=nullptr); - ~QQmlObjectModel() {} - - int count() const override; - bool isValid() const override; - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *object) override; - QVariant variantValue(int index, const QString &role) override; - void setWatchedRoles(const QList<QByteArray> &) override {} - QQmlIncubator::Status incubationStatus(int index) override; - - int indexOf(QObject *object, QObject *objectContext) const override; - - QQmlListProperty<QObject> children(); - - static QQmlObjectModelAttached *qmlAttachedProperties(QObject *obj); - - Q_REVISION(3) Q_INVOKABLE QObject *get(int index) const; - Q_REVISION(3) Q_INVOKABLE void append(QObject *object); - Q_REVISION(3) Q_INVOKABLE void insert(int index, QObject *object); - Q_REVISION(3) Q_INVOKABLE void move(int from, int to, int n = 1); - Q_REVISION(3) Q_INVOKABLE void remove(int index, int n = 1); - -public Q_SLOTS: - Q_REVISION(3) void clear(); - -Q_SIGNALS: - void childrenChanged(); - -private: - Q_DISABLE_COPY(QQmlObjectModel) -}; - -class QQmlObjectModelAttached : public QObject -{ - Q_OBJECT - -public: - QQmlObjectModelAttached(QObject *parent) - : QObject(parent), m_index(-1) {} - ~QQmlObjectModelAttached() { - attachedProperties.remove(parent()); - } - - Q_PROPERTY(int index READ index NOTIFY indexChanged) - int index() const { return m_index; } - void setIndex(int idx) { - if (m_index != idx) { - m_index = idx; - Q_EMIT indexChanged(); - } - } - - static QQmlObjectModelAttached *properties(QObject *obj) { - QQmlObjectModelAttached *rv = attachedProperties.value(obj); - if (!rv) { - rv = new QQmlObjectModelAttached(obj); - attachedProperties.insert(obj, rv); - } - return rv; - } - -Q_SIGNALS: - void indexChanged(); - -public: - int m_index; - - static QHash<QObject*, QQmlObjectModelAttached*> attachedProperties; -}; - - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlInstanceModel) -QML_DECLARE_TYPE(QQmlObjectModel) -QML_DECLARE_TYPEINFO(QQmlObjectModel, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQMLINSTANCEMODEL_P_H diff --git a/src/qml/types/qqmltableinstancemodel.cpp b/src/qml/types/qqmltableinstancemodel.cpp deleted file mode 100644 index 2170e2daec..0000000000 --- a/src/qml/types/qqmltableinstancemodel.cpp +++ /dev/null @@ -1,547 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmltableinstancemodel_p.h" -#include "qqmldelegatecomponent_p.h" - -#include <QtCore/QTimer> - -#include <QtQml/private/qqmlincubator_p.h> -#include <QtQml/private/qqmlchangeset_p.h> -#include <QtQml/private/qqmlcomponent_p.h> - -QT_BEGIN_NAMESPACE - -const char* kModelItemTag = "_tableinstancemodel_modelItem"; - -bool QQmlTableInstanceModel::isDoneIncubating(QQmlDelegateModelItem *modelItem) -{ - if (!modelItem->incubationTask) - return true; - - const auto status = modelItem->incubationTask->status(); - return (status == QQmlIncubator::Ready) || (status == QQmlIncubator::Error); -} - -void QQmlTableInstanceModel::deleteModelItemLater(QQmlDelegateModelItem *modelItem) -{ - Q_ASSERT(modelItem); - - delete modelItem->object; - modelItem->object = nullptr; - - if (modelItem->contextData) { - modelItem->contextData->invalidate(); - Q_ASSERT(modelItem->contextData->refCount == 1); - modelItem->contextData = nullptr; - } - - modelItem->deleteLater(); -} - -QQmlTableInstanceModel::QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent) - : QQmlInstanceModel(*(new QObjectPrivate()), parent) - , m_qmlContext(qmlContext) - , m_metaType(new QQmlDelegateModelItemMetaType(m_qmlContext->engine()->handle(), nullptr, QStringList())) -{ -} - -void QQmlTableInstanceModel::useImportVersion(int minorVersion) -{ - m_adaptorModel.useImportVersion(minorVersion); -} - -QQmlTableInstanceModel::~QQmlTableInstanceModel() -{ - for (const auto modelItem : m_modelItems) { - // No item in m_modelItems should be referenced at this point. The view - // should release all its items before it deletes this model. Only model items - // that are still being incubated should be left for us to delete. - Q_ASSERT(modelItem->objectRef == 0); - Q_ASSERT(modelItem->incubationTask); - // Check that we are not being deleted while we're - // in the process of e.g emitting a created signal. - Q_ASSERT(modelItem->scriptRef == 0); - - if (modelItem->object) { - delete modelItem->object; - modelItem->object = nullptr; - modelItem->contextData->invalidate(); - modelItem->contextData = nullptr; - } - } - - deleteAllFinishedIncubationTasks(); - qDeleteAll(m_modelItems); - drainReusableItemsPool(0); -} - -QQmlComponent *QQmlTableInstanceModel::resolveDelegate(int index) -{ - if (m_delegateChooser) { - const int row = m_adaptorModel.rowAt(index); - const int column = m_adaptorModel.columnAt(index); - QQmlComponent *delegate = nullptr; - QQmlAbstractDelegateComponent *chooser = m_delegateChooser; - do { - delegate = chooser->delegate(&m_adaptorModel, row, column); - chooser = qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - } while (chooser); - return delegate; - } - - return m_delegate; -} - -QQmlDelegateModelItem *QQmlTableInstanceModel::resolveModelItem(int index) -{ - // Check if an item for the given index is already loaded and ready - QQmlDelegateModelItem *modelItem = m_modelItems.value(index, nullptr); - if (modelItem) - return modelItem; - - QQmlComponent *delegate = resolveDelegate(index); - if (!delegate) - return nullptr; - - // Check if the pool contains an item that can be reused - modelItem = takeFromReusableItemsPool(delegate); - if (modelItem) { - reuseItem(modelItem, index); - m_modelItems.insert(index, modelItem); - return modelItem; - } - - // Create a new item from scratch - modelItem = m_adaptorModel.createItem(m_metaType, index); - if (modelItem) { - modelItem->delegate = delegate; - m_modelItems.insert(index, modelItem); - return modelItem; - } - - qWarning() << Q_FUNC_INFO << "failed creating a model item for index: " << index; - return nullptr; -} - -QObject *QQmlTableInstanceModel::object(int index, QQmlIncubator::IncubationMode incubationMode) -{ - Q_ASSERT(m_delegate); - Q_ASSERT(index >= 0 && index < m_adaptorModel.count()); - Q_ASSERT(m_qmlContext && m_qmlContext->isValid()); - - QQmlDelegateModelItem *modelItem = resolveModelItem(index); - if (!modelItem) - return nullptr; - - if (modelItem->object) { - // The model item has already been incubated. So - // just bump the ref-count and return it. - modelItem->referenceObject(); - return modelItem->object; - } - - // The object is not ready, and needs to be incubated - incubateModelItem(modelItem, incubationMode); - if (!isDoneIncubating(modelItem)) - return nullptr; - - // Incubation is done, so the task should be removed - Q_ASSERT(!modelItem->incubationTask); - - if (!modelItem->object) { - // The object was incubated synchronously (otherwise we would return above). But since - // we have no object, the incubation must have failed. And when we have no object, there - // should be no object references either. And there should also not be any internal script - // refs at this point. So we delete the model item. - Q_ASSERT(!modelItem->isObjectReferenced()); - Q_ASSERT(!modelItem->isReferenced()); - m_modelItems.remove(modelItem->index); - delete modelItem; - return nullptr; - } - - // Incubation was completed sync and successful - modelItem->referenceObject(); - return modelItem->object; -} - -QQmlInstanceModel::ReleaseFlags QQmlTableInstanceModel::release(QObject *object, ReusableFlag reusable) -{ - Q_ASSERT(object); - auto modelItem = qvariant_cast<QQmlDelegateModelItem *>(object->property(kModelItemTag)); - Q_ASSERT(modelItem); - - if (!modelItem->releaseObject()) - return QQmlDelegateModel::Referenced; - - if (modelItem->isReferenced()) { - // We still have an internal reference to this object, which means that we are told to release an - // object while the createdItem signal for it is still on the stack. This can happen when objects - // are e.g delivered async, and the user flicks back and forth quicker than the loading can catch - // up with. The view might then find that the object is no longer visible and should be released. - // We detect this case in incubatorStatusChanged(), and delete it there instead. But from the callers - // point of view, it should consider it destroyed. - return QQmlDelegateModel::Destroyed; - } - - // The item is not referenced by anyone - m_modelItems.remove(modelItem->index); - - if (reusable == Reusable) { - insertIntoReusableItemsPool(modelItem); - return QQmlInstanceModel::Referenced; - } - - // The item is not reused or referenced by anyone, so just delete it - modelItem->destroyObject(); - emit destroyingItem(object); - - delete modelItem; - return QQmlInstanceModel::Destroyed; -} - -void QQmlTableInstanceModel::cancel(int index) -{ - auto modelItem = m_modelItems.value(index); - Q_ASSERT(modelItem); - - // Since the view expects the item to be incubating, there should be - // an incubation task. And since the incubation is not done, no-one - // should yet have received, and therfore hold a reference to, the object. - Q_ASSERT(modelItem->incubationTask); - Q_ASSERT(!modelItem->isObjectReferenced()); - - m_modelItems.remove(index); - - if (modelItem->object) - delete modelItem->object; - - // modelItem->incubationTask will be deleted from the modelItems destructor - delete modelItem; -} - -void QQmlTableInstanceModel::insertIntoReusableItemsPool(QQmlDelegateModelItem *modelItem) -{ - // Currently, the only way for a view to reuse items is to call QQmlTableInstanceModel::release() - // with the second argument explicitly set to QQmlTableInstanceModel::Reusable. If the released - // item is no longer referenced, it will be added to the pool. Reusing of items can be specified - // per item, in case certain items cannot be recycled. - // A QQmlDelegateModelItem knows which delegate its object was created from. So when we are - // about to create a new item, we first check if the pool contains an item based on the same - // delegate from before. If so, we take it out of the pool (instead of creating a new item), and - // update all its context-, and attached properties. - // When a view is recycling items, it should call QQmlTableInstanceModel::drainReusableItemsPool() - // regularly. As there is currently no logic to 'hibernate' items in the pool, they are only - // meant to rest there for a short while, ideally only from the time e.g a row is unloaded - // on one side of the view, and until a new row is loaded on the opposite side. In-between - // this time, the application will see the item as fully functional and 'alive' (just not - // visible on screen). Since this time is supposed to be short, we don't take any action to - // notify the application about it, since we don't want to trigger any bindings that can - // disturb performance. - // A recommended time for calling drainReusableItemsPool() is each time a view has finished - // loading e.g a new row or column. If there are more items in the pool after that, it means - // that the view most likely doesn't need them anytime soon. Those items should be destroyed to - // not consume resources. - // Depending on if a view is a list or a table, it can sometimes be performant to keep - // items in the pool for a bit longer than one "row out/row in" cycle. E.g for a table, if the - // number of visible rows in a view is much larger than the number of visible columns. - // In that case, if you flick out a row, and then flick in a column, you would throw away a lot - // of items in the pool if completely draining it. The reason is that unloading a row places more - // items in the pool than what ends up being recycled when loading a new column. And then, when you - // next flick in a new row, you would need to load all those drained items again from scratch. For - // that reason, you can specify a maxPoolTime to the drainReusableItemsPool() that allows you to keep - // items in the pool for a bit longer, effectively keeping more items in circulation. - // A recommended maxPoolTime would be equal to the number of dimenstions in the view, which - // means 1 for a list view and 2 for a table view. If you specify 0, all items will be drained. - Q_ASSERT(!modelItem->incubationTask); - Q_ASSERT(!modelItem->isObjectReferenced()); - Q_ASSERT(!modelItem->isReferenced()); - Q_ASSERT(modelItem->object); - - modelItem->poolTime = 0; - m_reusableItemsPool.append(modelItem); - emit itemPooled(modelItem->index, modelItem->object); -} - -QQmlDelegateModelItem *QQmlTableInstanceModel::takeFromReusableItemsPool(const QQmlComponent *delegate) -{ - // Find the oldest item in the pool that was made from the same delegate as - // the given argument, remove it from the pool, and return it. - if (m_reusableItemsPool.isEmpty()) - return nullptr; - - for (auto it = m_reusableItemsPool.begin(); it != m_reusableItemsPool.end(); ++it) { - if ((*it)->delegate != delegate) - continue; - auto modelItem = *it; - m_reusableItemsPool.erase(it); - return modelItem; - } - - return nullptr; -} - -void QQmlTableInstanceModel::drainReusableItemsPool(int maxPoolTime) -{ - // Rather than releasing all pooled items upon a call to this function, each - // item has a poolTime. The poolTime specifies for how many loading cycles an item - // has been resting in the pool. And for each invocation of this function, poolTime - // will increase. If poolTime is equal to, or exceeds, maxPoolTime, it will be removed - // from the pool and released. This way, the view can tweak a bit for how long - // items should stay in "circulation", even if they are not recycled right away. - for (auto it = m_reusableItemsPool.begin(); it != m_reusableItemsPool.end();) { - auto modelItem = *it; - modelItem->poolTime++; - if (modelItem->poolTime <= maxPoolTime) { - ++it; - } else { - it = m_reusableItemsPool.erase(it); - release(modelItem->object, NotReusable); - } - } -} - -void QQmlTableInstanceModel::reuseItem(QQmlDelegateModelItem *item, int newModelIndex) -{ - // Update the context properties index, row and column on - // the delegate item, and inform the application about it. - const int newRow = m_adaptorModel.rowAt(newModelIndex); - const int newColumn = m_adaptorModel.columnAt(newModelIndex); - item->setModelIndex(newModelIndex, newRow, newColumn); - - // Notify the application that all 'dynamic'/role-based context data has - // changed as well (their getter function will use the updated index). - auto const itemAsList = QList<QQmlDelegateModelItem *>() << item; - auto const updateAllRoles = QVector<int>(); - m_adaptorModel.notify(itemAsList, newModelIndex, 1, updateAllRoles); - - // Inform the view that the item is recycled. This will typically result - // in the view updating its own attached delegate item properties. - emit itemReused(newModelIndex, item->object); -} - -void QQmlTableInstanceModel::incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode) -{ - // Guard the model item temporarily so that it's not deleted from - // incubatorStatusChanged(), in case the incubation is done synchronously. - modelItem->scriptRef++; - - if (modelItem->incubationTask) { - // We're already incubating the model item from a previous request. If the previous call requested - // the item async, but the current request needs it sync, we need to force-complete the incubation. - const bool sync = (incubationMode == QQmlIncubator::Synchronous || incubationMode == QQmlIncubator::AsynchronousIfNested); - if (sync && modelItem->incubationTask->incubationMode() == QQmlIncubator::Asynchronous) - modelItem->incubationTask->forceCompletion(); - } else { - modelItem->incubationTask = new QQmlTableInstanceModelIncubationTask(this, modelItem, incubationMode); - - QQmlContextData *ctxt = new QQmlContextData; - QQmlContext *creationContext = modelItem->delegate->creationContext(); - ctxt->setParent(QQmlContextData::get(creationContext ? creationContext : m_qmlContext.data())); - ctxt->contextObject = modelItem; - modelItem->contextData = ctxt; - - QQmlComponentPrivate::get(modelItem->delegate)->incubateObject( - modelItem->incubationTask, - modelItem->delegate, - m_qmlContext->engine(), - ctxt, - QQmlContextData::get(m_qmlContext)); - } - - // Remove the temporary guard - modelItem->scriptRef--; -} - -void QQmlTableInstanceModel::incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *incubationTask, QQmlIncubator::Status status) -{ - QQmlDelegateModelItem *modelItem = incubationTask->modelItemToIncubate; - Q_ASSERT(modelItem->incubationTask); - - modelItem->incubationTask = nullptr; - incubationTask->modelItemToIncubate = nullptr; - - if (status == QQmlIncubator::Ready) { - // Tag the incubated object with the model item for easy retrieval upon release etc. - modelItem->object->setProperty(kModelItemTag, QVariant::fromValue(modelItem)); - - // Emit that the item has been created. What normally happens next is that the view - // upon receiving the signal asks for the model item once more. And since the item is - // now in the map, it will be returned directly. - Q_ASSERT(modelItem->object); - modelItem->scriptRef++; - emit createdItem(modelItem->index, modelItem->object); - modelItem->scriptRef--; - } else if (status == QQmlIncubator::Error) { - qWarning() << "Error incubating delegate:" << incubationTask->errors(); - } - - if (!modelItem->isReferenced() && !modelItem->isObjectReferenced()) { - // We have no internal reference to the model item, and the view has no - // reference to the incubated object. So just delete the model item. - // Note that being here means that the object was incubated _async_ - // (otherwise modelItem->isReferenced() would be true). - m_modelItems.remove(modelItem->index); - - if (modelItem->object) { - modelItem->scriptRef++; - emit destroyingItem(modelItem->object); - modelItem->scriptRef--; - Q_ASSERT(!modelItem->isReferenced()); - } - - deleteModelItemLater(modelItem); - } - - deleteIncubationTaskLater(incubationTask); -} - -QQmlIncubator::Status QQmlTableInstanceModel::incubationStatus(int index) { - const auto modelItem = m_modelItems.value(index, nullptr); - if (!modelItem) - return QQmlIncubator::Null; - - if (modelItem->incubationTask) - return modelItem->incubationTask->status(); - - // Since we clear the incubation task when we're done - // incubating, it means that the status is Ready. - return QQmlIncubator::Ready; -} - -void QQmlTableInstanceModel::deleteIncubationTaskLater(QQmlIncubator *incubationTask) -{ - // We often need to post-delete incubation tasks, since we cannot - // delete them while we're in the middle of an incubation change callback. - Q_ASSERT(!m_finishedIncubationTasks.contains(incubationTask)); - m_finishedIncubationTasks.append(incubationTask); - if (m_finishedIncubationTasks.count() == 1) - QTimer::singleShot(1, this, &QQmlTableInstanceModel::deleteAllFinishedIncubationTasks); -} - -void QQmlTableInstanceModel::deleteAllFinishedIncubationTasks() -{ - qDeleteAll(m_finishedIncubationTasks); - m_finishedIncubationTasks.clear(); -} - -QVariant QQmlTableInstanceModel::model() const -{ - return m_adaptorModel.model(); -} - -void QQmlTableInstanceModel::setModel(const QVariant &model) -{ - // Pooled items are still accessible/alive for the application, and - // needs to stay in sync with the model. So we need to drain the pool - // completely when the model changes. - drainReusableItemsPool(0); - if (auto const aim = abstractItemModel()) - disconnect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback); - m_adaptorModel.setModel(model, this, m_qmlContext->engine()); - if (auto const aim = abstractItemModel()) - connect(aim, &QAbstractItemModel::dataChanged, this, &QQmlTableInstanceModel::dataChangedCallback); -} - -void QQmlTableInstanceModel::dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles) -{ - // This function is called when model data has changed. In that case, we tell the adaptor model - // to go through all the items we have created, find the ones that are affected, and notify that - // their model data has changed. This will in turn update QML bindings inside the delegate items. - int numberOfRowsChanged = end.row() - begin.row() + 1; - int numberOfColumnsChanged = end.column() - begin.column() + 1; - - for (int column = 0; column < numberOfColumnsChanged; ++column) { - const int columnIndex = begin.column() + column; - const int rowIndex = begin.row() + (columnIndex * rows()); - m_adaptorModel.notify(m_modelItems.values(), rowIndex, numberOfRowsChanged, roles); - } -} - -QQmlComponent *QQmlTableInstanceModel::delegate() const -{ - return m_delegate; -} - -void QQmlTableInstanceModel::setDelegate(QQmlComponent *delegate) -{ - if (m_delegate == delegate) - return; - - m_delegateChooser = nullptr; - if (delegate) { - QQmlAbstractDelegateComponent *adc = - qobject_cast<QQmlAbstractDelegateComponent *>(delegate); - if (adc) - m_delegateChooser = adc; - } - - m_delegate = delegate; -} - -const QAbstractItemModel *QQmlTableInstanceModel::abstractItemModel() const -{ - return m_adaptorModel.adaptsAim() ? m_adaptorModel.aim() : nullptr; -} - -// -------------------------------------------------------- - -void QQmlTableInstanceModelIncubationTask::setInitialState(QObject *object) -{ - modelItemToIncubate->object = object; - emit tableInstanceModel->initItem(modelItemToIncubate->index, object); -} - -void QQmlTableInstanceModelIncubationTask::statusChanged(QQmlIncubator::Status status) -{ - if (!QQmlTableInstanceModel::isDoneIncubating(modelItemToIncubate)) - return; - - // We require the view to cancel any ongoing load - // requests before the tableInstanceModel is destructed. - Q_ASSERT(tableInstanceModel); - - tableInstanceModel->incubatorStatusChanged(this, status); -} - -#include "moc_qqmltableinstancemodel_p.cpp" - -QT_END_NAMESPACE - diff --git a/src/qml/types/qqmltableinstancemodel_p.h b/src/qml/types/qqmltableinstancemodel_p.h deleted file mode 100644 index 39ec66d136..0000000000 --- a/src/qml/types/qqmltableinstancemodel_p.h +++ /dev/null @@ -1,162 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLTABLEINSTANCEMODEL_P_H -#define QQMLTABLEINSTANCEMODEL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtQml/private/qqmldelegatemodel_p.h> -#include <QtQml/private/qqmldelegatemodel_p_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlTableInstanceModel; -class QQmlAbstractDelegateComponent; - -class QQmlTableInstanceModelIncubationTask : public QQDMIncubationTask -{ -public: - QQmlTableInstanceModelIncubationTask( - QQmlTableInstanceModel *tableInstanceModel - , QQmlDelegateModelItem* modelItemToIncubate - , IncubationMode mode) - : QQDMIncubationTask(nullptr, mode) - , modelItemToIncubate(modelItemToIncubate) - , tableInstanceModel(tableInstanceModel) { - clear(); - } - - void statusChanged(Status status) override; - void setInitialState(QObject *object) override; - - QQmlDelegateModelItem *modelItemToIncubate = nullptr; - QQmlTableInstanceModel *tableInstanceModel = nullptr; -}; - -class Q_QML_PRIVATE_EXPORT QQmlTableInstanceModel : public QQmlInstanceModel -{ - Q_OBJECT - -public: - - enum ReusableFlag { - NotReusable, - Reusable - }; - - QQmlTableInstanceModel(QQmlContext *qmlContext, QObject *parent = nullptr); - ~QQmlTableInstanceModel() override; - - void useImportVersion(int minorVersion); - - int count() const override { return m_adaptorModel.count(); } - int rows() const { return m_adaptorModel.rowCount(); } - int columns() const { return m_adaptorModel.columnCount(); } - - bool isValid() const override { return true; } - - QVariant model() const; - void setModel(const QVariant &model); - - QQmlComponent *delegate() const; - void setDelegate(QQmlComponent *); - - const QAbstractItemModel *abstractItemModel() const override; - - QObject *object(int index, QQmlIncubator::IncubationMode incubationMode = QQmlIncubator::AsynchronousIfNested) override; - ReleaseFlags release(QObject *object) override { return release(object, NotReusable); } - ReleaseFlags release(QObject *object, ReusableFlag reusable); - void cancel(int) override; - - void insertIntoReusableItemsPool(QQmlDelegateModelItem *modelItem); - QQmlDelegateModelItem *takeFromReusableItemsPool(const QQmlComponent *delegate); - void drainReusableItemsPool(int maxPoolTime); - int poolSize() { return m_reusableItemsPool.size(); } - void reuseItem(QQmlDelegateModelItem *item, int newModelIndex); - - QQmlIncubator::Status incubationStatus(int index) override; - - QVariant variantValue(int, const QString &) override { Q_UNREACHABLE(); return QVariant(); } - void setWatchedRoles(const QList<QByteArray> &) override { Q_UNREACHABLE(); } - int indexOf(QObject *, QObject *) const override { Q_UNREACHABLE(); return 0; } - -Q_SIGNALS: - void itemPooled(int index, QObject *object); - void itemReused(int index, QObject *object); - -private: - QQmlComponent *resolveDelegate(int index); - - QQmlAdaptorModel m_adaptorModel; - QQmlAbstractDelegateComponent *m_delegateChooser = nullptr; - QQmlComponent *m_delegate = nullptr; - QPointer<QQmlContext> m_qmlContext; - QQmlDelegateModelItemMetaType *m_metaType; - - QHash<int, QQmlDelegateModelItem *> m_modelItems; - QList<QQmlDelegateModelItem *> m_reusableItemsPool; - QList<QQmlIncubator *> m_finishedIncubationTasks; - - void incubateModelItem(QQmlDelegateModelItem *modelItem, QQmlIncubator::IncubationMode incubationMode); - void incubatorStatusChanged(QQmlTableInstanceModelIncubationTask *dmIncubationTask, QQmlIncubator::Status status); - void deleteIncubationTaskLater(QQmlIncubator *incubationTask); - void deleteAllFinishedIncubationTasks(); - QQmlDelegateModelItem *resolveModelItem(int index); - - void dataChangedCallback(const QModelIndex &begin, const QModelIndex &end, const QVector<int> &roles); - - static bool isDoneIncubating(QQmlDelegateModelItem *modelItem); - static void deleteModelItemLater(QQmlDelegateModelItem *modelItem); - - friend class QQmlTableInstanceModelIncubationTask; -}; - -QT_END_NAMESPACE - -#endif // QQMLTABLEINSTANCEMODEL_P_H diff --git a/src/qml/types/qqmltablemodel.cpp b/src/qml/types/qqmltablemodel.cpp deleted file mode 100644 index 4a96e7a46b..0000000000 --- a/src/qml/types/qqmltablemodel.cpp +++ /dev/null @@ -1,1059 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmltablemodel_p.h" - -#include <QtCore/qloggingcategory.h> -#include <QtQml/qqmlinfo.h> -#include <QtQml/qqmlengine.h> - -QT_BEGIN_NAMESPACE - -Q_LOGGING_CATEGORY(lcTableModel, "qt.qml.tablemodel") - -/*! - \qmltype TableModel - \instantiates QQmlTableModel - \inqmlmodule Qt.labs.qmlmodels - \brief Encapsulates a simple table model. - \since 5.14 - - The TableModel type stores JavaScript/JSON objects as data for a table - model that can be used with \l TableView. It is intended to support - very simple models without requiring the creation of a custom - QAbstractTableModel subclass in C++. - - \snippet qml/tablemodel/fruit-example-simpledelegate.qml file - - The model's initial row data is set with either the \l rows property or by - calling \l appendRow(). Each column in the model is specified by declaring - a \l TableModelColumn instance, where the order of each instance determines - its column index. Once the model's \l Component.completed() signal has been - emitted, the columns and roles will have been established and are then - fixed for the lifetime of the model. - - To access a specific row, the \l getRow() function can be used. - It's also possible to access the model's JavaScript data - directly via the \l rows property, but it is not possible to - modify the model data this way. - - To add new rows, use \l appendRow() and \l insertRow(). To modify - existing rows, use \l setRow(), \l moveRow(), \l removeRow(), and - \l clear(). - - It is also possible to modify the model's data via the delegate, - as shown in the example above: - - \snippet qml/tablemodel/fruit-example-simpledelegate.qml delegate - - If the type of the data at the modified role does not match the type of the - data that is set, it will be automatically converted via - \l {QVariant::canConvert()}{QVariant}. - - \section1 Supported Row Data Structures - - TableModel is designed to work with JavaScript/JSON data, where each row - is a simple key-pair object: - - \code - { - // Each property is one cell/column. - checked: false, - amount: 1, - fruitType: "Apple", - fruitName: "Granny Smith", - fruitPrice: 1.50 - }, - // ... - \endcode - - As model manipulation in Qt is done via row and column indices, - and because object keys are unordered, each column must be specified via - TableModelColumn. This allows mapping Qt's built-in roles to any property - in each row object. - - Complex row structures are supported, but with limited functionality. - As TableModel has no way of knowing how each row is structured, - it cannot manipulate it. As a consequence of this, the copy of the - model data that TableModel has stored in \l rows is not kept in sync - with the source data that was set in QML. For these reasons, TableModel - relies on the user to handle simple data manipulation. - - For example, suppose you wanted to have several roles per column. One way - of doing this is to use a data source where each row is an array and each - cell is an object. To use this data source with TableModel, define a - getter and setter: - - \code - TableModel { - TableModelColumn { - display: function(modelIndex) { return rows[modelIndex.row][0].checked } - setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][0].checked = cellData } - } - // ... - - rows: [ - [ - { checked: false, checkable: true }, - { amount: 1 }, - { fruitType: "Apple" }, - { fruitName: "Granny Smith" }, - { fruitPrice: 1.50 } - ] - // ... - ] - } - \endcode - - The row above is one example of a complex row. - - \note Row manipulation functions such as \l appendRow(), \l removeRow(), - etc. are not supported when using complex rows. - - \section1 Using DelegateChooser with TableModel - - For most real world use cases, it is recommended to use DelegateChooser - as the delegate of a TableView that uses TableModel. This allows you to - use specific roles in the relevant delegates. For example, the snippet - above can be rewritten to use DelegateChooser like so: - - \snippet qml/tablemodel/fruit-example-delegatechooser.qml file - - The most specific delegates are declared first: the columns at index \c 0 - and \c 1 have \c bool and \c integer data types, so they use a - \l [QtQuickControls2]{CheckBox} and \l [QtQuickControls2]{SpinBox}, - respectively. The remaining columns can simply use a - \l [QtQuickControls2]{TextField}, and so that delegate is declared - last as a fallback. - - \sa TableModelColumn, TableView, QAbstractTableModel -*/ - -QQmlTableModel::QQmlTableModel(QObject *parent) - : QAbstractTableModel(parent) -{ -} - -QQmlTableModel::~QQmlTableModel() -{ -} - -/*! - \qmlproperty object TableModel::rows - - This property holds the model data in the form of an array of rows: - - \snippet qml/tablemodel/fruit-example-simpledelegate.qml rows - - \sa getRow(), setRow(), moveRow(), appendRow(), insertRow(), clear(), rowCount, columnCount -*/ -QVariant QQmlTableModel::rows() const -{ - return mRows; -} - -void QQmlTableModel::setRows(const QVariant &rows) -{ - if (rows.userType() != qMetaTypeId<QJSValue>()) { - qmlWarning(this) << "setRows(): \"rows\" must be an array; actual type is " << rows.typeName(); - return; - } - - const QJSValue rowsAsJSValue = rows.value<QJSValue>(); - const QVariantList rowsAsVariantList = rowsAsJSValue.toVariant().toList(); - if (rowsAsVariantList == mRows) { - // No change. - return; - } - - if (!componentCompleted) { - // Store the rows until we can call doSetRows() after component completion. - mRows = rowsAsVariantList; - return; - } - - doSetRows(rowsAsVariantList); -} - -void QQmlTableModel::doSetRows(const QVariantList &rowsAsVariantList) -{ - Q_ASSERT(componentCompleted); - - // By now, all TableModelColumns should have been set. - if (mColumns.isEmpty()) { - qmlWarning(this) << "No TableModelColumns were set; model will be empty"; - return; - } - - const bool firstTimeValidRowsHaveBeenSet = mColumnMetadata.isEmpty(); - if (!firstTimeValidRowsHaveBeenSet) { - // This is not the first time rows have been set; validate each one. - for (int rowIndex = 0; rowIndex < rowsAsVariantList.size(); ++rowIndex) { - // validateNewRow() expects a QVariant wrapping a QJSValue, so to - // simplify the code, just create one here. - const QVariant row = QVariant::fromValue(rowsAsVariantList.at(rowIndex)); - if (!validateNewRow("setRows()", row, rowIndex, SetRowsOperation)) - return; - } - } - - const int oldRowCount = mRowCount; - const int oldColumnCount = mColumnCount; - - beginResetModel(); - - // We don't clear the column or role data, because a TableModel should not be reused in that way. - // Once it has valid data, its columns and roles are fixed. - mRows = rowsAsVariantList; - mRowCount = mRows.size(); - - // Gather metadata the first time rows is set. - if (firstTimeValidRowsHaveBeenSet && !mRows.isEmpty()) - fetchColumnMetadata(); - - endResetModel(); - - emit rowsChanged(); - - if (mRowCount != oldRowCount) - emit rowCountChanged(); - if (mColumnCount != oldColumnCount) - emit columnCountChanged(); -} - -QQmlTableModel::ColumnRoleMetadata QQmlTableModel::fetchColumnRoleData(const QString &roleNameKey, - QQmlTableModelColumn *tableModelColumn, int columnIndex) const -{ - const QVariant firstRow = mRows.first(); - ColumnRoleMetadata roleData; - - QJSValue columnRoleGetter = tableModelColumn->getterAtRole(roleNameKey); - if (columnRoleGetter.isUndefined()) { - // This role is not defined, which is fine; just skip it. - return roleData; - } - - if (columnRoleGetter.isString()) { - // The role is set as a string, so we assume the row is a simple object. - if (firstRow.type() != QVariant::Map) { - qmlWarning(this).quote() << "expected row for role " - << roleNameKey << " of TableModelColumn at index " - << columnIndex << " to be a simple object, but it's " - << firstRow.typeName() << " instead: " << firstRow; - return roleData; - } - const QVariantMap firstRowAsMap = firstRow.toMap(); - const QString rolePropertyName = columnRoleGetter.toString(); - const QVariant roleProperty = firstRowAsMap.value(rolePropertyName); - - roleData.isStringRole = true; - roleData.name = rolePropertyName; - roleData.type = roleProperty.type(); - roleData.typeName = QString::fromLatin1(roleProperty.typeName()); - } else if (columnRoleGetter.isCallable()) { - // The role is provided via a function, which means the row is complex and - // the user needs to provide the data for it. - const auto modelIndex = index(0, columnIndex); - const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(modelIndex); - const QVariant cellData = columnRoleGetter.call(args).toVariant(); - - // We don't know the property name since it's provided through the function. - // roleData.name = ??? - roleData.isStringRole = false; - roleData.type = cellData.type(); - roleData.typeName = QString::fromLatin1(cellData.typeName()); - } else { - // Invalid role. - qmlWarning(this) << "TableModelColumn role for column at index " - << columnIndex << " must be either a string or a function; actual type is: " - << columnRoleGetter.toString(); - } - - return roleData; -} - -void QQmlTableModel::fetchColumnMetadata() -{ - qCDebug(lcTableModel) << "gathering metadata for" << mColumnCount << "columns from first row:"; - - static const auto supportedRoleNames = QQmlTableModelColumn::supportedRoleNames(); - - // Since we support different data structures at the row level, we require that there - // is a TableModelColumn for each column. - // Collect and cache metadata for each column. This makes data lookup faster. - for (int columnIndex = 0; columnIndex < mColumns.size(); ++columnIndex) { - QQmlTableModelColumn *column = mColumns.at(columnIndex); - qCDebug(lcTableModel).nospace() << "- column " << columnIndex << ":"; - - ColumnMetadata metaData; - const auto builtInRoleKeys = supportedRoleNames.keys(); - for (const int builtInRoleKey : builtInRoleKeys) { - const QString builtInRoleName = supportedRoleNames.value(builtInRoleKey); - ColumnRoleMetadata roleData = fetchColumnRoleData(builtInRoleName, column, columnIndex); - if (roleData.type == QVariant::Invalid) { - // This built-in role was not specified in this column. - continue; - } - - qCDebug(lcTableModel).nospace() << " - added metadata for built-in role " - << builtInRoleName << " at column index " << columnIndex - << ": name=" << roleData.name << " typeName=" << roleData.typeName - << " type=" << roleData.type; - - // This column now supports this specific built-in role. - metaData.roles.insert(builtInRoleName, roleData); - // Add it if it doesn't already exist. - mRoleNames[builtInRoleKey] = builtInRoleName.toLatin1(); - } - mColumnMetadata.insert(columnIndex, metaData); - } -} - -/*! - \qmlmethod TableModel::appendRow(object row) - - Adds a new row to the end of the model, with the - values (cells) in \a row. - - \code - model.appendRow({ - checkable: true, - amount: 1, - fruitType: "Pear", - fruitName: "Williams", - fruitPrice: 1.50, - }) - \endcode - - \sa insertRow(), setRow(), removeRow() -*/ -void QQmlTableModel::appendRow(const QVariant &row) -{ - if (!validateNewRow("appendRow()", row, -1, AppendOperation)) - return; - - doInsert(mRowCount, row); -} - -/*! - \qmlmethod TableModel::clear() - - Removes all rows from the model. - - \sa removeRow() -*/ -void QQmlTableModel::clear() -{ - QQmlEngine *engine = qmlEngine(this); - Q_ASSERT(engine); - setRows(QVariant::fromValue(engine->newArray())); -} - -/*! - \qmlmethod object TableModel::getRow(int rowIndex) - - Returns the row at \a rowIndex in the model. - - Note that this equivalent to accessing the row directly - through the \l rows property: - - \code - Component.onCompleted: { - // These two lines are equivalent. - console.log(model.getRow(0).display); - console.log(model.rows[0].fruitName); - } - \endcode - - \note the returned object cannot be used to modify the contents of the - model; use setRow() instead. - - \sa setRow(), appendRow(), insertRow(), removeRow(), moveRow() -*/ -QVariant QQmlTableModel::getRow(int rowIndex) -{ - if (!validateRowIndex("getRow()", "rowIndex", rowIndex)) - return QVariant(); - - return mRows.at(rowIndex); -} - -/*! - \qmlmethod TableModel::insertRow(int rowIndex, object row) - - Adds a new row to the list model at position \a rowIndex, with the - values (cells) in \a row. - - \code - model.insertRow(2, { - checkable: true, checked: false, - amount: 1, - fruitType: "Pear", - fruitName: "Williams", - fruitPrice: 1.50, - }) - \endcode - - The \a rowIndex must be to an existing item in the list, or one past - the end of the list (equivalent to \l appendRow()). - - \sa appendRow(), setRow(), removeRow(), rowCount -*/ -void QQmlTableModel::insertRow(int rowIndex, const QVariant &row) -{ - if (!validateNewRow("insertRow()", row, rowIndex)) - return; - - doInsert(rowIndex, row); -} - -void QQmlTableModel::doInsert(int rowIndex, const QVariant &row) -{ - beginInsertRows(QModelIndex(), rowIndex, rowIndex); - - // Adding rowAsVariant.toList() will add each invidual variant in the list, - // which is definitely not what we want. - const QVariant rowAsVariant = row.value<QJSValue>().toVariant(); - mRows.insert(rowIndex, rowAsVariant); - ++mRowCount; - - qCDebug(lcTableModel).nospace() << "inserted the following row to the model at index " - << rowIndex << ":\n" << rowAsVariant.toMap(); - - // Gather metadata the first time a row is added. - if (mColumnMetadata.isEmpty()) - fetchColumnMetadata(); - - endInsertRows(); - emit rowCountChanged(); -} - -void QQmlTableModel::classBegin() -{ -} - -void QQmlTableModel::componentComplete() -{ - componentCompleted = true; - - mColumnCount = mColumns.size(); - if (mColumnCount > 0) - emit columnCountChanged(); - - doSetRows(mRows); -} - -/*! - \qmlmethod TableModel::moveRow(int fromRowIndex, int toRowIndex, int rows) - - Moves \a rows from the index at \a fromRowIndex to the index at - \a toRowIndex. - - The from and to ranges must exist; for example, to move the first 3 items - to the end of the list: - - \code - model.moveRow(0, model.rowCount - 3, 3) - \endcode - - \sa appendRow(), insertRow(), removeRow(), rowCount -*/ -void QQmlTableModel::moveRow(int fromRowIndex, int toRowIndex, int rows) -{ - if (fromRowIndex == toRowIndex) { - qmlWarning(this) << "moveRow(): \"fromRowIndex\" cannot be equal to \"toRowIndex\""; - return; - } - - if (rows <= 0) { - qmlWarning(this) << "moveRow(): \"rows\" is less than or equal to 0"; - return; - } - - if (!validateRowIndex("moveRow()", "fromRowIndex", fromRowIndex)) - return; - - if (!validateRowIndex("moveRow()", "toRowIndex", toRowIndex)) - return; - - if (fromRowIndex + rows > mRowCount) { - qmlWarning(this) << "moveRow(): \"fromRowIndex\" (" << fromRowIndex - << ") + \"rows\" (" << rows << ") = " << (fromRowIndex + rows) - << ", which is greater than rowCount() of " << mRowCount; - return; - } - - if (toRowIndex + rows > mRowCount) { - qmlWarning(this) << "moveRow(): \"toRowIndex\" (" << toRowIndex - << ") + \"rows\" (" << rows << ") = " << (toRowIndex + rows) - << ", which is greater than rowCount() of " << mRowCount; - return; - } - - qCDebug(lcTableModel).nospace() << "moving " << rows - << " row(s) from index " << fromRowIndex - << " to index " << toRowIndex; - - // Based on the same call in QQmlListModel::moveRow(). - beginMoveRows(QModelIndex(), fromRowIndex, fromRowIndex + rows - 1, QModelIndex(), - toRowIndex > fromRowIndex ? toRowIndex + rows : toRowIndex); - - // Based on ListModel::moveRow(). - if (fromRowIndex > toRowIndex) { - // Only move forwards - flip if moving backwards. - const int from = fromRowIndex; - const int to = toRowIndex; - fromRowIndex = to; - toRowIndex = to + rows; - rows = from - to; - } - - QVector<QVariant> store; - store.reserve(rows); - for (int i = 0; i < (toRowIndex - fromRowIndex); ++i) - store.append(mRows.at(fromRowIndex + rows + i)); - for (int i = 0; i < rows; ++i) - store.append(mRows.at(fromRowIndex + i)); - for (int i = 0; i < store.size(); ++i) - mRows[fromRowIndex + i] = store[i]; - - qCDebug(lcTableModel).nospace() << "after moving, rows are:\n" << mRows; - - endMoveRows(); -} - -/*! - \qmlmethod TableModel::removeRow(int rowIndex, int rows = 1) - - Removes the row at \a rowIndex from the model. - - \sa clear(), rowCount -*/ -void QQmlTableModel::removeRow(int rowIndex, int rows) -{ - if (!validateRowIndex("removeRow()", "rowIndex", rowIndex)) - return; - - if (rows <= 0) { - qmlWarning(this) << "removeRow(): \"rows\" is less than or equal to zero"; - return; - } - - if (rowIndex + rows - 1 >= mRowCount) { - qmlWarning(this) << "removeRow(): \"rows\" " << rows - << " exceeds available rowCount() of " << mRowCount - << " when removing from \"rowIndex\" " << rowIndex; - return; - } - - beginRemoveRows(QModelIndex(), rowIndex, rowIndex + rows - 1); - - auto firstIterator = mRows.begin() + rowIndex; - // The "last" argument to erase() is exclusive, so we go one past the last item. - auto lastIterator = firstIterator + rows; - mRows.erase(firstIterator, lastIterator); - mRowCount -= rows; - - endRemoveRows(); - emit rowCountChanged(); - - qCDebug(lcTableModel).nospace() << "removed " << rows - << " items from the model, starting at index " << rowIndex; -} - -/*! - \qmlmethod TableModel::setRow(int rowIndex, object row) - - Changes the row at \a rowIndex in the model with \a row. - - All columns/cells must be present in \c row, and in the correct order. - - \code - model.setRow(0, { - checkable: true, - amount: 1, - fruitType: "Pear", - fruitName: "Williams", - fruitPrice: 1.50, - }) - \endcode - - If \a rowIndex is equal to \c rowCount(), then a new row is appended to the - model. Otherwise, \a rowIndex must point to an existing row in the model. - - \sa appendRow(), insertRow(), rowCount -*/ -void QQmlTableModel::setRow(int rowIndex, const QVariant &row) -{ - if (!validateNewRow("setRow()", row, rowIndex)) - return; - - if (rowIndex != mRowCount) { - // Setting an existing row. - mRows[rowIndex] = row; - - // For now we just assume the whole row changed, as it's simpler. - const QModelIndex topLeftModelIndex(createIndex(rowIndex, 0)); - const QModelIndex bottomRightModelIndex(createIndex(rowIndex, mColumnCount - 1)); - emit dataChanged(topLeftModelIndex, bottomRightModelIndex); - } else { - // Appending a row. - doInsert(rowIndex, row); - } -} - -QQmlListProperty<QQmlTableModelColumn> QQmlTableModel::columns() -{ - return QQmlListProperty<QQmlTableModelColumn>(this, nullptr, - &QQmlTableModel::columns_append, - &QQmlTableModel::columns_count, - &QQmlTableModel::columns_at, - &QQmlTableModel::columns_clear); -} - -void QQmlTableModel::columns_append(QQmlListProperty<QQmlTableModelColumn> *property, - QQmlTableModelColumn *value) -{ - QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object); - QQmlTableModelColumn *column = qobject_cast<QQmlTableModelColumn*>(value); - if (column) - model->mColumns.append(column); -} - -int QQmlTableModel::columns_count(QQmlListProperty<QQmlTableModelColumn> *property) -{ - const QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object); - return model->mColumns.count(); -} - -QQmlTableModelColumn *QQmlTableModel::columns_at(QQmlListProperty<QQmlTableModelColumn> *property, int index) -{ - const QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object); - return model->mColumns.at(index); -} - -void QQmlTableModel::columns_clear(QQmlListProperty<QQmlTableModelColumn> *property) -{ - QQmlTableModel *model = static_cast<QQmlTableModel*>(property->object); - return model->mColumns.clear(); -} - -/*! - \qmlmethod QModelIndex TableModel::index(int row, int column) - - Returns a \l QModelIndex object referencing the given \a row and \a column, - which can be passed to the data() function to get the data from that cell, - or to setData() to edit the contents of that cell. - - \code - import QtQml 2.14 - import Qt.labs.qmlmodels 1.0 - - TableModel { - id: model - - TableModelColumn { display: "fruitType" } - TableModelColumn { display: "fruitPrice" } - - rows: [ - { fruitType: "Apple", fruitPrice: 1.50 }, - { fruitType: "Orange", fruitPrice: 2.50 } - ] - - Component.onCompleted: { - for (var r = 0; r < model.rowCount; ++r) { - console.log("An " + model.data(model.index(r, 0)).display + - " costs " + model.data(model.index(r, 1)).display.toFixed(2)) - } - } - } - \endcode - - \sa {QModelIndex and related Classes in QML}, data() -*/ -// Note: we don't document the parent argument, because you never need it, because -// cells in a TableModel don't have parents. But it is there because this function is an override. -QModelIndex QQmlTableModel::index(int row, int column, const QModelIndex &parent) const -{ - return row >= 0 && row < rowCount() && column >= 0 && column < columnCount() && !parent.isValid() - ? createIndex(row, column) - : QModelIndex(); -} - -/*! - \qmlproperty int TableModel::rowCount - \readonly - - This read-only property holds the number of rows in the model. - - This value changes whenever rows are added or removed from the model. -*/ -int QQmlTableModel::rowCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - - return mRowCount; -} - -/*! - \qmlproperty int TableModel::columnCount - \readonly - - This read-only property holds the number of columns in the model. - - The number of columns is fixed for the lifetime of the model - after the \l rows property is set or \l appendRow() is called for the first - time. -*/ -int QQmlTableModel::columnCount(const QModelIndex &parent) const -{ - if (parent.isValid()) - return 0; - - return mColumnCount; -} - -/*! - \qmlmethod variant TableModel::data(QModelIndex index, string role) - - Returns the data from the table cell at the given \a index belonging to the - given \a role. - - \sa index() -*/ -QVariant QQmlTableModel::data(const QModelIndex &index, const QString &role) const -{ - const int iRole = mRoleNames.key(role.toUtf8(), -1); - if (iRole >= 0) - return data(index, iRole); - return QVariant(); -} - -QVariant QQmlTableModel::data(const QModelIndex &index, int role) const -{ - const int row = index.row(); - if (row < 0 || row >= rowCount()) - return QVariant(); - - const int column = index.column(); - if (column < 0 || column >= columnCount()) - return QVariant(); - - const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column()); - const QString roleName = QString::fromUtf8(mRoleNames.value(role)); - if (!columnMetadata.roles.contains(roleName)) { - qmlWarning(this) << "setData(): no role named " << roleName - << " at column index " << column << ". The available roles for that column are: " - << columnMetadata.roles.keys(); - return QVariant(); - } - - const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName); - if (roleData.isStringRole) { - // We know the data structure, so we can get the data for the user. - const QVariantMap rowData = mRows.at(row).toMap(); - const QString propertyName = columnMetadata.roles.value(roleName).name; - const QVariant value = rowData.value(propertyName); - return value; - } - - // We don't know the data structure, so the user has to modify their data themselves. - // First, find the getter for this column and role. - QJSValue getter = mColumns.at(column)->getterAtRole(roleName); - - // Then, call it and return what it returned. - const auto args = QJSValueList() << qmlEngine(this)->toScriptValue(index); - return getter.call(args).toVariant(); -} - -/*! - \qmlmethod bool TableModel::setData(QModelIndex index, string role, variant value) - - Inserts or updates the data field named by \a role in the table cell at the - given \a index with \a value. Returns true if sucessful, false if not. - - \sa index() -*/ -bool QQmlTableModel::setData(const QModelIndex &index, const QString &role, const QVariant &value) -{ - const int intRole = mRoleNames.key(role.toUtf8(), -1); - if (intRole >= 0) - return setData(index, value, intRole); - return false; -} - -bool QQmlTableModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - const int row = index.row(); - if (row < 0 || row >= rowCount()) - return false; - - const int column = index.column(); - if (column < 0 || column >= columnCount()) - return false; - - const QString roleName = QString::fromUtf8(mRoleNames.value(role)); - - qCDebug(lcTableModel).nospace() << "setData() called with index " - << index << ", value " << value << " and role " << roleName; - - // Verify that the role exists for this column. - const ColumnMetadata columnMetadata = mColumnMetadata.at(index.column()); - if (!columnMetadata.roles.contains(roleName)) { - qmlWarning(this) << "setData(): no role named \"" << roleName - << "\" at column index " << column << ". The available roles for that column are: " - << columnMetadata.roles.keys(); - return false; - } - - // Verify that the type of the value is what we expect. - // If the value set is not of the expected type, we can try to convert it automatically. - const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName); - QVariant effectiveValue = value; - if (value.type() != roleData.type) { - if (!value.canConvert(int(roleData.type))) { - qmlWarning(this).nospace() << "setData(): the value " << value - << " set at row " << row << " column " << column << " with role " << roleName - << " cannot be converted to " << roleData.typeName; - return false; - } - - if (!effectiveValue.convert(int(roleData.type))) { - qmlWarning(this).nospace() << "setData(): failed converting value " << value - << " set at row " << row << " column " << column << " with role " << roleName - << " to " << roleData.typeName; - return false; - } - } - - if (roleData.isStringRole) { - // We know the data structure, so we can set it for the user. - QVariantMap modifiedRow = mRows.at(row).toMap(); - modifiedRow[roleData.name] = value; - - mRows[row] = modifiedRow; - } else { - // We don't know the data structure, so the user has to modify their data themselves. - auto engine = qmlEngine(this); - auto args = QJSValueList() - // arg 0: modelIndex. - << engine->toScriptValue(index) - // arg 1: cellData. - << engine->toScriptValue(value); - // Do the actual setting. - QJSValue setter = mColumns.at(column)->setterAtRole(roleName); - setter.call(args); - - /* - The chain of events so far: - - - User did e.g.: model.edit = textInput.text - - setData() is called - - setData() calls the setter - (remember that we need to emit the dataChanged() signal, - which is why the user can't just set the data directly in the delegate) - - Now the user's setter function has modified *their* copy of the - data, but *our* copy of the data is old. Imagine the getters and setters looked like this: - - display: function(modelIndex) { return rows[modelIndex.row][1].amount } - setDisplay: function(modelIndex, cellData) { rows[modelIndex.row][1].amount = cellData } - - We don't know the structure of the user's data, so we can't just do - what we do above for the isStringRole case: - - modifiedRow[column][roleName] = value - - This means that, besides getting the implicit row count when rows is initially set, - our copy of the data is unused when it comes to complex columns. - - Another point to note is that we can't pass rowData in to the getter as a convenience, - because we would be passing in *our* copy of the row, which is not up-to-date. - Since the user already has access to the data, it's not a big deal for them to do: - - display: function(modelIndex) { return rows[modelIndex.row][1].amount } - - instead of: - - display: function(modelIndex, rowData) { return rowData[1].amount } - */ - } - - QVector<int> rolesChanged; - rolesChanged.append(role); - emit dataChanged(index, index, rolesChanged); - - return true; -} - -QHash<int, QByteArray> QQmlTableModel::roleNames() const -{ - return mRoleNames; -} - -QQmlTableModel::ColumnRoleMetadata::ColumnRoleMetadata() -{ -} - -QQmlTableModel::ColumnRoleMetadata::ColumnRoleMetadata( - bool isStringRole, const QString &name, QVariant::Type type, const QString &typeName) : - isStringRole(isStringRole), - name(name), - type(type), - typeName(typeName) -{ -} - -bool QQmlTableModel::ColumnRoleMetadata::isValid() const -{ - return !name.isEmpty(); -} - -bool QQmlTableModel::validateRowType(const char *functionName, const QVariant &row) const -{ - if (!row.canConvert<QJSValue>()) { - qmlWarning(this) << functionName << ": expected \"row\" argument to be a QJSValue," - << " but got " << row.typeName() << " instead:\n" << row; - return false; - } - - const QJSValue rowAsJSValue = row.value<QJSValue>(); - if (!rowAsJSValue.isObject() && !rowAsJSValue.isArray()) { - qmlWarning(this) << functionName << ": expected \"row\" argument " - << "to be an object or array, but got:\n" << rowAsJSValue.toString(); - return false; - } - - return true; -} - -bool QQmlTableModel::validateNewRow(const char *functionName, const QVariant &row, - int rowIndex, NewRowOperationFlag operation) const -{ - if (mColumnMetadata.isEmpty()) { - // There is no column metadata, so we have nothing to validate the row against. - // Rows have to be added before we can gather metadata from them, so just this - // once we'll return true to allow the rows to be added. - return true; - } - - // Don't require each row to be a QJSValue when setting all rows, - // as they won't be; they'll be QVariantMap. - if (operation != SetRowsOperation && !validateRowType(functionName, row)) - return false; - - if (operation == OtherOperation) { - // Inserting/setting. - if (rowIndex < 0) { - qmlWarning(this) << functionName << ": \"rowIndex\" cannot be negative"; - return false; - } - - if (rowIndex > mRowCount) { - qmlWarning(this) << functionName << ": \"rowIndex\" " << rowIndex - << " is greater than rowCount() of " << mRowCount; - return false; - } - } - - const QVariant rowAsVariant = operation == SetRowsOperation - ? row : row.value<QJSValue>().toVariant(); - if (rowAsVariant.type() != QVariant::Map) { - qmlWarning(this) << functionName << ": row manipulation functions " - << "do not support complex rows (row index: " << rowIndex << ")"; - return false; - } - - const QVariantMap rowAsMap = rowAsVariant.toMap(); - const int columnCount = rowAsMap.size(); - if (columnCount < mColumnCount) { - qmlWarning(this) << functionName << ": expected " << mColumnCount - << " columns, but only got " << columnCount; - return false; - } - - // We can't validate complex structures, but we can make sure that - // each simple string-based role in each column is correct. - for (int columnIndex = 0; columnIndex < mColumns.size(); ++columnIndex) { - QQmlTableModelColumn *column = mColumns.at(columnIndex); - const QHash<QString, QJSValue> getters = column->getters(); - const auto roleNames = getters.keys(); - const ColumnMetadata columnMetadata = mColumnMetadata.at(columnIndex); - for (const QString &roleName : roleNames) { - const ColumnRoleMetadata roleData = columnMetadata.roles.value(roleName); - if (!roleData.isStringRole) - continue; - - if (!rowAsMap.contains(roleData.name)) { - qmlWarning(this).quote() << functionName << ": expected a property named " - << roleData.name << " in row at index " << rowIndex << ", but couldn't find one"; - return false; - } - - const QVariant rolePropertyValue = rowAsMap.value(roleData.name); - if (rolePropertyValue.type() != roleData.type) { - qmlWarning(this).quote() << functionName << ": expected the property named " - << roleData.name << " to be of type " << roleData.typeName - << ", but got " << QString::fromLatin1(rolePropertyValue.typeName()) << " instead"; - return false; - } - } - } - - return true; -} - -bool QQmlTableModel::validateRowIndex(const char *functionName, const char *argumentName, int rowIndex) const -{ - if (rowIndex < 0) { - qmlWarning(this) << functionName << ": \"" << argumentName << "\" cannot be negative"; - return false; - } - - if (rowIndex >= mRowCount) { - qmlWarning(this) << functionName << ": \"" << argumentName - << "\" " << rowIndex << " is greater than or equal to rowCount() of " << mRowCount; - return false; - } - - return true; -} - -QT_END_NAMESPACE diff --git a/src/qml/types/qqmltablemodel_p.h b/src/qml/types/qqmltablemodel_p.h deleted file mode 100644 index a1bb97e7d4..0000000000 --- a/src/qml/types/qqmltablemodel_p.h +++ /dev/null @@ -1,170 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLTABLEMODEL_P_H -#define QQMLTABLEMODEL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/QObject> -#include <QtCore/QAbstractTableModel> -#include <QtQml/qqml.h> -#include <QtQml/private/qtqmlglobal_p.h> -#include <QtQml/private/qqmltablemodelcolumn_p.h> -#include <QtQml/QJSValue> -#include <QtQml/QQmlListProperty> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlTableModel : public QAbstractTableModel, public QQmlParserStatus -{ - Q_OBJECT - Q_PROPERTY(int columnCount READ columnCount NOTIFY columnCountChanged FINAL) - Q_PROPERTY(int rowCount READ rowCount NOTIFY rowCountChanged FINAL) - Q_PROPERTY(QVariant rows READ rows WRITE setRows NOTIFY rowsChanged FINAL) - Q_PROPERTY(QQmlListProperty<QQmlTableModelColumn> columns READ columns CONSTANT FINAL) - Q_INTERFACES(QQmlParserStatus) - Q_CLASSINFO("DefaultProperty", "columns") - -public: - QQmlTableModel(QObject *parent = nullptr); - ~QQmlTableModel() override; - - QVariant rows() const; - void setRows(const QVariant &rows); - - Q_INVOKABLE void appendRow(const QVariant &row); - Q_INVOKABLE void clear(); - Q_INVOKABLE QVariant getRow(int rowIndex); - Q_INVOKABLE void insertRow(int rowIndex, const QVariant &row); - Q_INVOKABLE void moveRow(int fromRowIndex, int toRowIndex, int rows = 1); - Q_INVOKABLE void removeRow(int rowIndex, int rows = 1); - Q_INVOKABLE void setRow(int rowIndex, const QVariant &row); - - QQmlListProperty<QQmlTableModelColumn> columns(); - - static void columns_append(QQmlListProperty<QQmlTableModelColumn> *property, QQmlTableModelColumn *value); - static int columns_count(QQmlListProperty<QQmlTableModelColumn> *property); - static QQmlTableModelColumn *columns_at(QQmlListProperty<QQmlTableModelColumn> *property, int index); - static void columns_clear(QQmlListProperty<QQmlTableModelColumn> *property); - - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - int rowCount(const QModelIndex &parent = QModelIndex()) const override; - int columnCount(const QModelIndex &parent = QModelIndex()) const override; - Q_INVOKABLE QVariant data(const QModelIndex &index, const QString &role) const; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; - Q_INVOKABLE bool setData(const QModelIndex &index, const QString &role, const QVariant &value); - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::DisplayRole) override; - QHash<int, QByteArray> roleNames() const override; - -Q_SIGNALS: - void columnCountChanged(); - void rowCountChanged(); - void rowsChanged(); - -private: - class ColumnRoleMetadata - { - public: - ColumnRoleMetadata(); - ColumnRoleMetadata(bool isStringRole, const QString &name, QVariant::Type type, const QString &typeName); - - bool isValid() const; - - // If this is false, it's a function role. - bool isStringRole = false; - QString name; - QVariant::Type type = QVariant::Invalid; - QString typeName; - }; - - struct ColumnMetadata - { - // Key = role name that will be made visible to the delegate - // Value = metadata about that role, including actual name in the model data, type, etc. - QHash<QString, ColumnRoleMetadata> roles; - }; - - enum NewRowOperationFlag { - OtherOperation, // insert(), set(), etc. - SetRowsOperation, - AppendOperation - }; - - void doSetRows(const QVariantList &rowsAsVariantList); - ColumnRoleMetadata fetchColumnRoleData(const QString &roleNameKey, - QQmlTableModelColumn *tableModelColumn, int columnIndex) const; - void fetchColumnMetadata(); - - bool validateRowType(const char *functionName, const QVariant &row) const; - bool validateNewRow(const char *functionName, const QVariant &row, - int rowIndex, NewRowOperationFlag operation = OtherOperation) const; - bool validateRowIndex(const char *functionName, const char *argumentName, int rowIndex) const; - - void doInsert(int rowIndex, const QVariant &row); - - void classBegin() override; - void componentComplete() override; - - bool componentCompleted = false; - QVariantList mRows; - QList<QQmlTableModelColumn *> mColumns; - int mRowCount = 0; - int mColumnCount = 0; - // Each entry contains information about the properties of the column at that index. - QVector<ColumnMetadata> mColumnMetadata; - // key = property index (0 to number of properties across all columns) - // value = role name - QHash<int, QByteArray> mRoleNames; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlTableModel) - -#endif // QQMLTABLEMODEL_P_H diff --git a/src/qml/types/qqmltablemodelcolumn.cpp b/src/qml/types/qqmltablemodelcolumn.cpp deleted file mode 100644 index 93da0642de..0000000000 --- a/src/qml/types/qqmltablemodelcolumn.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmltablemodelcolumn_p.h" - -#include <QtQml/qqmlinfo.h> - -QT_BEGIN_NAMESPACE - -/*! - \qmltype TableModelColumn - \instantiates QQmlTableModelColumn - \inqmlmodule Qt.labs.qmlmodels - \brief Represents a column in a model. - \since 5.14 - - \section1 Supported Roles - - TableModelColumn supports all of \l {Qt::ItemDataRole}{Qt's roles}, - with the exception of \c Qt::InitialSortOrderRole. - - \sa TableModel, TableView -*/ - -static const QString displayRoleName = QStringLiteral("display"); -static const QString decorationRoleName = QStringLiteral("decoration"); -static const QString editRoleName = QStringLiteral("edit"); -static const QString toolTipRoleName = QStringLiteral("toolTip"); -static const QString statusTipRoleName = QStringLiteral("statusTip"); -static const QString whatsThisRoleName = QStringLiteral("whatsThis"); - -static const QString fontRoleName = QStringLiteral("font"); -static const QString textAlignmentRoleName = QStringLiteral("textAlignment"); -static const QString backgroundRoleName = QStringLiteral("background"); -static const QString foregroundRoleName = QStringLiteral("foreground"); -static const QString checkStateRoleName = QStringLiteral("checkState"); - -static const QString accessibleTextRoleName = QStringLiteral("accessibleText"); -static const QString accessibleDescriptionRoleName = QStringLiteral("accessibleDescription"); - -static const QString sizeHintRoleName = QStringLiteral("sizeHint"); - - -QQmlTableModelColumn::QQmlTableModelColumn(QObject *parent) - : QObject(parent) -{ -} - -QQmlTableModelColumn::~QQmlTableModelColumn() -{ -} - -#define DEFINE_ROLE_PROPERTIES(getterGetterName, getterSetterName, getterSignal, setterGetterName, setterSetterName, setterSignal, roleName) \ -QJSValue QQmlTableModelColumn::getterGetterName() const \ -{ \ - return mGetters.value(roleName); \ -} \ -\ -void QQmlTableModelColumn::getterSetterName(const QJSValue &stringOrFunction) \ -{ \ - if (!stringOrFunction.isString() && !stringOrFunction.isCallable()) { \ - qmlWarning(this).quote() << "getter for " << roleName << " must be a function"; \ - return; \ - } \ - if (stringOrFunction.strictlyEquals(decoration())) \ - return; \ -\ - mGetters[roleName] = stringOrFunction; \ - emit decorationChanged(); \ -} \ -\ -QJSValue QQmlTableModelColumn::setterGetterName() const \ -{ \ - return mSetters.value(roleName); \ -} \ -\ -void QQmlTableModelColumn::setterSetterName(const QJSValue &function) \ -{ \ - if (!function.isCallable()) { \ - qmlWarning(this).quote() << "setter for " << roleName << " must be a function"; \ - return; \ - } \ -\ - if (function.strictlyEquals(getSetDisplay())) \ - return; \ -\ - mSetters[roleName] = function; \ - emit setDisplayChanged(); \ -} - -DEFINE_ROLE_PROPERTIES(display, setDisplay, displayChanged, - getSetDisplay, setSetDisplay, setDisplayChanged, displayRoleName) -DEFINE_ROLE_PROPERTIES(decoration, setDecoration, decorationChanged, - getSetDecoration, setSetDecoration, setDecorationChanged, decorationRoleName) -DEFINE_ROLE_PROPERTIES(edit, setEdit, editChanged, - getSetEdit, setSetEdit, setEditChanged, editRoleName) -DEFINE_ROLE_PROPERTIES(toolTip, setToolTip, toolTipChanged, - getSetToolTip, setSetToolTip, setToolTipChanged, toolTipRoleName) -DEFINE_ROLE_PROPERTIES(statusTip, setStatusTip, statusTipChanged, - getSetStatusTip, setSetStatusTip, setStatusTipChanged, statusTipRoleName) -DEFINE_ROLE_PROPERTIES(whatsThis, setWhatsThis, whatsThisChanged, - getSetWhatsThis, setSetWhatsThis, setWhatsThisChanged, whatsThisRoleName) - -DEFINE_ROLE_PROPERTIES(font, setFont, fontChanged, - getSetFont, setSetFont, setFontChanged, fontRoleName) -DEFINE_ROLE_PROPERTIES(textAlignment, setTextAlignment, textAlignmentChanged, - getSetTextAlignment, setSetTextAlignment, setTextAlignmentChanged, textAlignmentRoleName) -DEFINE_ROLE_PROPERTIES(background, setBackground, backgroundChanged, - getSetBackground, setSetBackground, setBackgroundChanged, backgroundRoleName) -DEFINE_ROLE_PROPERTIES(foreground, setForeground, foregroundChanged, - getSetForeground, setSetForeground, setForegroundChanged, foregroundRoleName) -DEFINE_ROLE_PROPERTIES(checkState, setCheckState, checkStateChanged, - getSetCheckState, setSetCheckState, setCheckStateChanged, checkStateRoleName) - -DEFINE_ROLE_PROPERTIES(accessibleText, setAccessibleText, accessibleTextChanged, - getSetAccessibleText, setSetAccessibleText, setAccessibleTextChanged, accessibleTextRoleName) -DEFINE_ROLE_PROPERTIES(accessibleDescription, setAccessibleDescription, accessibleDescriptionChanged, - getSetAccessibleDescription, setSetAccessibleDescription, setAccessibleDescriptionChanged, accessibleDescriptionRoleName) - -DEFINE_ROLE_PROPERTIES(sizeHint, setSizeHint, sizeHintChanged, - getSetSizeHint, setSetSizeHint, setSizeHintChanged, sizeHintRoleName) - -QJSValue QQmlTableModelColumn::getterAtRole(const QString &roleName) -{ - auto it = mGetters.find(roleName); - if (it == mGetters.end()) - return QJSValue(); - return *it; -} - -QJSValue QQmlTableModelColumn::setterAtRole(const QString &roleName) -{ - auto it = mSetters.find(roleName); - if (it == mSetters.end()) - return QJSValue(); - return *it; -} - -const QHash<QString, QJSValue> QQmlTableModelColumn::getters() const -{ - return mGetters; -} - -const QHash<int, QString> QQmlTableModelColumn::supportedRoleNames() -{ - QHash<int, QString> names; - names[Qt::DisplayRole] = QLatin1String("display"); - names[Qt::DecorationRole] = QLatin1String("decoration"); - names[Qt::EditRole] = QLatin1String("edit"); - names[Qt::ToolTipRole] = QLatin1String("toolTip"); - names[Qt::StatusTipRole] = QLatin1String("statusTip"); - names[Qt::WhatsThisRole] = QLatin1String("whatsThis"); - names[Qt::FontRole] = QLatin1String("font"); - names[Qt::TextAlignmentRole] = QLatin1String("textAlignment"); - names[Qt::BackgroundRole] = QLatin1String("background"); - names[Qt::ForegroundRole] = QLatin1String("foreground"); - names[Qt::CheckStateRole] = QLatin1String("checkState"); - names[Qt::AccessibleTextRole] = QLatin1String("accessibleText"); - names[Qt::AccessibleDescriptionRole] = QLatin1String("accessibleDescription"); - names[Qt::SizeHintRole] = QLatin1String("sizeHint"); - return names; -} - -QT_END_NAMESPACE diff --git a/src/qml/types/qqmltablemodelcolumn_p.h b/src/qml/types/qqmltablemodelcolumn_p.h deleted file mode 100644 index 41c02482c0..0000000000 --- a/src/qml/types/qqmltablemodelcolumn_p.h +++ /dev/null @@ -1,224 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2019 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLTABLEMODELCOLUMN_P_H -#define QQMLTABLEMODELCOLUMN_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/QObject> -#include <QtQml/qqml.h> -#include <QtQml/private/qtqmlglobal_p.h> -#include <QtQml/qjsvalue.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_AUTOTEST_EXPORT QQmlTableModelColumn : public QObject -{ - Q_OBJECT - Q_PROPERTY(QJSValue display READ display WRITE setDisplay NOTIFY displayChanged FINAL) - Q_PROPERTY(QJSValue setDisplay READ getSetDisplay WRITE setSetDisplay NOTIFY setDisplayChanged) - Q_PROPERTY(QJSValue decoration READ decoration WRITE setDecoration NOTIFY decorationChanged FINAL) - Q_PROPERTY(QJSValue setDecoration READ getSetDecoration WRITE setSetDecoration NOTIFY setDecorationChanged FINAL) - Q_PROPERTY(QJSValue edit READ edit WRITE setEdit NOTIFY editChanged FINAL) - Q_PROPERTY(QJSValue setEdit READ getSetEdit WRITE setSetEdit NOTIFY setEditChanged FINAL) - Q_PROPERTY(QJSValue toolTip READ toolTip WRITE setToolTip NOTIFY toolTipChanged FINAL) - Q_PROPERTY(QJSValue setToolTip READ getSetToolTip WRITE setSetToolTip NOTIFY setToolTipChanged FINAL) - Q_PROPERTY(QJSValue statusTip READ statusTip WRITE setStatusTip NOTIFY statusTipChanged FINAL) - Q_PROPERTY(QJSValue setStatusTip READ getSetStatusTip WRITE setSetStatusTip NOTIFY setStatusTipChanged FINAL) - Q_PROPERTY(QJSValue whatsThis READ whatsThis WRITE setWhatsThis NOTIFY whatsThisChanged FINAL) - Q_PROPERTY(QJSValue setWhatsThis READ getSetWhatsThis WRITE setSetWhatsThis NOTIFY setWhatsThisChanged FINAL) - - Q_PROPERTY(QJSValue font READ font WRITE setFont NOTIFY fontChanged FINAL) - Q_PROPERTY(QJSValue setFont READ getSetFont WRITE setSetFont NOTIFY setFontChanged FINAL) - Q_PROPERTY(QJSValue textAlignment READ textAlignment WRITE setTextAlignment NOTIFY textAlignmentChanged FINAL) - Q_PROPERTY(QJSValue setTextAlignment READ getSetTextAlignment WRITE setSetTextAlignment NOTIFY setTextAlignmentChanged FINAL) - Q_PROPERTY(QJSValue background READ background WRITE setBackground NOTIFY backgroundChanged FINAL) - Q_PROPERTY(QJSValue setBackground READ getSetBackground WRITE setSetBackground NOTIFY setBackgroundChanged FINAL) - Q_PROPERTY(QJSValue foreground READ foreground WRITE setForeground NOTIFY foregroundChanged FINAL) - Q_PROPERTY(QJSValue setForeground READ getSetForeground WRITE setSetForeground NOTIFY setForegroundChanged FINAL) - Q_PROPERTY(QJSValue checkState READ checkState WRITE setCheckState NOTIFY checkStateChanged FINAL) - Q_PROPERTY(QJSValue setCheckState READ getSetCheckState WRITE setSetCheckState NOTIFY setCheckStateChanged FINAL) - - Q_PROPERTY(QJSValue accessibleText READ accessibleText WRITE setAccessibleText NOTIFY accessibleTextChanged FINAL) - Q_PROPERTY(QJSValue setAccessibleText READ getSetAccessibleText WRITE setSetAccessibleText NOTIFY setAccessibleTextChanged FINAL) - Q_PROPERTY(QJSValue accessibleDescription READ accessibleDescription - WRITE setAccessibleDescription NOTIFY accessibleDescriptionChanged FINAL) - Q_PROPERTY(QJSValue setAccessibleDescription READ getSetAccessibleDescription - WRITE setSetAccessibleDescription NOTIFY setAccessibleDescriptionChanged FINAL) - - Q_PROPERTY(QJSValue sizeHint READ sizeHint WRITE setSizeHint NOTIFY sizeHintChanged FINAL) - Q_PROPERTY(QJSValue setSizeHint READ getSetSizeHint WRITE setSetSizeHint NOTIFY setSizeHintChanged FINAL) - -public: - QQmlTableModelColumn(QObject *parent = nullptr); - ~QQmlTableModelColumn() override; - - QJSValue display() const; - void setDisplay(const QJSValue &stringOrFunction); - QJSValue getSetDisplay() const; - void setSetDisplay(const QJSValue &function); - - QJSValue decoration() const; - void setDecoration(const QJSValue &stringOrFunction); - QJSValue getSetDecoration() const; - void setSetDecoration(const QJSValue &function); - - QJSValue edit() const; - void setEdit(const QJSValue &stringOrFunction); - QJSValue getSetEdit() const; - void setSetEdit(const QJSValue &function); - - QJSValue toolTip() const; - void setToolTip(const QJSValue &stringOrFunction); - QJSValue getSetToolTip() const; - void setSetToolTip(const QJSValue &function); - - QJSValue statusTip() const; - void setStatusTip(const QJSValue &stringOrFunction); - QJSValue getSetStatusTip() const; - void setSetStatusTip(const QJSValue &function); - - QJSValue whatsThis() const; - void setWhatsThis(const QJSValue &stringOrFunction); - QJSValue getSetWhatsThis() const; - void setSetWhatsThis(const QJSValue &function); - - QJSValue font() const; - void setFont(const QJSValue &stringOrFunction); - QJSValue getSetFont() const; - void setSetFont(const QJSValue &function); - - QJSValue textAlignment() const; - void setTextAlignment(const QJSValue &stringOrFunction); - QJSValue getSetTextAlignment() const; - void setSetTextAlignment(const QJSValue &function); - - QJSValue background() const; - void setBackground(const QJSValue &stringOrFunction); - QJSValue getSetBackground() const; - void setSetBackground(const QJSValue &function); - - QJSValue foreground() const; - void setForeground(const QJSValue &stringOrFunction); - QJSValue getSetForeground() const; - void setSetForeground(const QJSValue &function); - - QJSValue checkState() const; - void setCheckState(const QJSValue &stringOrFunction); - QJSValue getSetCheckState() const; - void setSetCheckState(const QJSValue &function); - - QJSValue accessibleText() const; - void setAccessibleText(const QJSValue &stringOrFunction); - QJSValue getSetAccessibleText() const; - void setSetAccessibleText(const QJSValue &function); - - QJSValue accessibleDescription() const; - void setAccessibleDescription(const QJSValue &stringOrFunction); - QJSValue getSetAccessibleDescription() const; - void setSetAccessibleDescription(const QJSValue &function); - - QJSValue sizeHint() const; - void setSizeHint(const QJSValue &stringOrFunction); - QJSValue getSetSizeHint() const; - void setSetSizeHint(const QJSValue &function); - - QJSValue getterAtRole(const QString &roleName); - QJSValue setterAtRole(const QString &roleName); - - const QHash<QString, QJSValue> getters() const; - - static const QHash<int, QString> supportedRoleNames(); - -Q_SIGNALS: - void indexChanged(); - void displayChanged(); - void setDisplayChanged(); - void decorationChanged(); - void setDecorationChanged(); - void editChanged(); - void setEditChanged(); - void toolTipChanged(); - void setToolTipChanged(); - void statusTipChanged(); - void setStatusTipChanged(); - void whatsThisChanged(); - void setWhatsThisChanged(); - - void fontChanged(); - void setFontChanged(); - void textAlignmentChanged(); - void setTextAlignmentChanged(); - void backgroundChanged(); - void setBackgroundChanged(); - void foregroundChanged(); - void setForegroundChanged(); - void checkStateChanged(); - void setCheckStateChanged(); - - void accessibleTextChanged(); - void setAccessibleTextChanged(); - void accessibleDescriptionChanged(); - void setAccessibleDescriptionChanged(); - void sizeHintChanged(); - void setSizeHintChanged(); - -private: - int mIndex = -1; - - // We store these in hashes because QQuickTableModel needs string-based lookup in certain situations. - QHash<QString, QJSValue> mGetters; - QHash<QString, QJSValue> mSetters; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQmlTableModelColumn) - -#endif // QQMLTABLEMODELCOLUMN_P_H diff --git a/src/qml/types/qquickpackage.cpp b/src/qml/types/qquickpackage.cpp deleted file mode 100644 index 03539d8737..0000000000 --- a/src/qml/types/qquickpackage.cpp +++ /dev/null @@ -1,198 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qquickpackage_p.h" - -#include <private/qobject_p.h> -#include <private/qqmlguard_p.h> - -QT_BEGIN_NAMESPACE - -/*! - \qmltype Package - \instantiates QQuickPackage - \inqmlmodule QtQuick - \ingroup qtquick-views - \brief Specifies a collection of named items. - - The Package type is used in conjunction with - DelegateModel to enable delegates with a shared context - to be provided to multiple views. - - Any item within a Package may be assigned a name via the - \l{Package::name}{Package.name} attached property. - - The example below creates a Package containing two named items; - \e list and \e grid. The third item in the package (the \l Rectangle) is parented to whichever - delegate it should appear in. This allows an item to move - between views. - - \snippet package/Delegate.qml 0 - - These named items are used as the delegates by the two views who - reference the special \l{DelegateModel::parts} property to select - a model which provides the chosen delegate. - - \snippet package/view.qml 0 - - \sa {Qt Quick Examples - Views}, {Qt Quick Demo - Photo Viewer}, {Qt QML} -*/ - -/*! - \qmlattachedproperty string QtQuick::Package::name - This attached property holds the name of an item within a Package. -*/ - - -class QQuickPackagePrivate : public QObjectPrivate -{ -public: - QQuickPackagePrivate() {} - - struct DataGuard : public QQmlGuard<QObject> - { - DataGuard(QObject *obj, QList<DataGuard> *l) : list(l) { (QQmlGuard<QObject>&)*this = obj; } - QList<DataGuard> *list; - void objectDestroyed(QObject *) override { - // we assume priv will always be destroyed after objectDestroyed calls - list->removeOne(*this); - } - }; - - QList<DataGuard> dataList; - static void data_append(QQmlListProperty<QObject> *prop, QObject *o) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - list->append(DataGuard(o, list)); - } - static void data_clear(QQmlListProperty<QObject> *prop) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - list->clear(); - } - static QObject *data_at(QQmlListProperty<QObject> *prop, int index) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - return list->at(index); - } - static int data_count(QQmlListProperty<QObject> *prop) { - QList<DataGuard> *list = static_cast<QList<DataGuard> *>(prop->data); - return list->count(); - } -}; - -QHash<QObject *, QQuickPackageAttached *> QQuickPackageAttached::attached; - -QQuickPackageAttached::QQuickPackageAttached(QObject *parent) -: QObject(parent) -{ - attached.insert(parent, this); -} - -QQuickPackageAttached::~QQuickPackageAttached() -{ - attached.remove(parent()); -} - -QString QQuickPackageAttached::name() const -{ - return _name; -} - -void QQuickPackageAttached::setName(const QString &n) -{ - _name = n; -} - -QQuickPackage::QQuickPackage(QObject *parent) - : QObject(*(new QQuickPackagePrivate), parent) -{ -} - -QQuickPackage::~QQuickPackage() -{ -} - -QQmlListProperty<QObject> QQuickPackage::data() -{ - Q_D(QQuickPackage); - return QQmlListProperty<QObject>(this, &d->dataList, QQuickPackagePrivate::data_append, - QQuickPackagePrivate::data_count, - QQuickPackagePrivate::data_at, - QQuickPackagePrivate::data_clear); -} - -bool QQuickPackage::hasPart(const QString &name) -{ - Q_D(QQuickPackage); - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return true; - } - return false; -} - -QObject *QQuickPackage::part(const QString &name) -{ - Q_D(QQuickPackage); - if (name.isEmpty() && !d->dataList.isEmpty()) - return d->dataList.at(0); - - for (int ii = 0; ii < d->dataList.count(); ++ii) { - QObject *obj = d->dataList.at(ii); - QQuickPackageAttached *a = QQuickPackageAttached::attached.value(obj); - if (a && a->name() == name) - return obj; - } - - if (name == QLatin1String("default") && !d->dataList.isEmpty()) - return d->dataList.at(0); - - return nullptr; -} - -QQuickPackageAttached *QQuickPackage::qmlAttachedProperties(QObject *o) -{ - return new QQuickPackageAttached(o); -} - - - -QT_END_NAMESPACE - -#include "moc_qquickpackage_p.cpp" diff --git a/src/qml/types/qquickpackage_p.h b/src/qml/types/qquickpackage_p.h deleted file mode 100644 index 122c7fcb30..0000000000 --- a/src/qml/types/qquickpackage_p.h +++ /dev/null @@ -1,101 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQUICKPACKAGE_H -#define QQUICKPACKAGE_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <qqml.h> - -QT_BEGIN_NAMESPACE - -class QQuickPackagePrivate; -class QQuickPackageAttached; -class Q_AUTOTEST_EXPORT QQuickPackage : public QObject -{ - Q_OBJECT - Q_DECLARE_PRIVATE(QQuickPackage) - - Q_CLASSINFO("DefaultProperty", "data") - Q_PROPERTY(QQmlListProperty<QObject> data READ data) - -public: - QQuickPackage(QObject *parent=nullptr); - virtual ~QQuickPackage(); - - QQmlListProperty<QObject> data(); - - QObject *part(const QString & = QString()); - bool hasPart(const QString &); - - static QQuickPackageAttached *qmlAttachedProperties(QObject *); -}; - -class QQuickPackageAttached : public QObject -{ -Q_OBJECT -Q_PROPERTY(QString name READ name WRITE setName) -public: - QQuickPackageAttached(QObject *parent); - virtual ~QQuickPackageAttached(); - - QString name() const; - void setName(const QString &n); - - static QHash<QObject *, QQuickPackageAttached *> attached; -private: - QString _name; -}; - -QT_END_NAMESPACE - -QML_DECLARE_TYPE(QQuickPackage) -QML_DECLARE_TYPEINFO(QQuickPackage, QML_HAS_ATTACHED_PROPERTIES) - -#endif // QQUICKPACKAGE_H diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index 5a56208dc4..ba11271d66 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -1,27 +1,12 @@ SOURCES += \ $$PWD/qqmlbind.cpp \ $$PWD/qqmlconnections.cpp \ - $$PWD/qqmlmodelsmodule.cpp \ - $$PWD/qqmlmodelindexvaluetype.cpp \ - $$PWD/qqmlobjectmodel.cpp \ - $$PWD/qquickpackage.cpp \ - $$PWD/qqmlinstantiator.cpp \ - $$PWD/qqmltableinstancemodel.cpp \ - $$PWD/qqmltablemodel.cpp \ - $$PWD/qqmltablemodelcolumn.cpp + $$PWD/qqmlmodelindexvaluetype.cpp HEADERS += \ $$PWD/qqmlbind_p.h \ $$PWD/qqmlconnections_p.h \ - $$PWD/qqmlmodelsmodule_p.h \ - $$PWD/qqmlmodelindexvaluetype_p.h \ - $$PWD/qqmlobjectmodel_p.h \ - $$PWD/qquickpackage_p.h \ - $$PWD/qqmlinstantiator_p.h \ - $$PWD/qqmlinstantiator_p_p.h \ - $$PWD/qqmltableinstancemodel_p.h \ - $$PWD/qqmltablemodel_p.h \ - $$PWD/qqmltablemodelcolumn_p.h + $$PWD/qqmlmodelindexvaluetype_p.h qtConfig(qml-worker-script) { SOURCES += \ @@ -30,28 +15,6 @@ qtConfig(qml-worker-script) { $$PWD/qquickworkerscript_p.h } -qtConfig(qml-list-model) { - SOURCES += \ - $$PWD/qqmllistmodel.cpp \ - $$PWD/qqmllistmodelworkeragent.cpp - - HEADERS += \ - $$PWD/qqmllistmodel_p.h \ - $$PWD/qqmllistmodel_p_p.h \ - $$PWD/qqmllistmodelworkeragent_p.h -} - -qtConfig(qml-delegate-model) { - SOURCES += \ - $$PWD/qqmldelegatemodel.cpp \ - $$PWD/qqmldelegatecomponent.cpp - - HEADERS += \ - $$PWD/qqmldelegatemodel_p.h \ - $$PWD/qqmldelegatemodel_p_p.h \ - $$PWD/qqmldelegatecomponent_p.h -} - qtConfig(qml-animation) { SOURCES += \ $$PWD/qqmltimer.cpp diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp deleted file mode 100644 index f991ae0a69..0000000000 --- a/src/qml/util/qqmladaptormodel.cpp +++ /dev/null @@ -1,1037 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2018 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmladaptormodel_p.h" - -#include <private/qqmldelegatemodel_p_p.h> -#include <private/qmetaobjectbuilder_p.h> -#include <private/qqmlproperty_p.h> - -#include <private/qv4value_p.h> -#include <private/qv4functionobject_p.h> - -QT_BEGIN_NAMESPACE - -class QQmlAdaptorModelEngineData : public QV8Engine::Deletable -{ -public: - QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4); - ~QQmlAdaptorModelEngineData(); - - QV4::ExecutionEngine *v4; - QV4::PersistentValue listItemProto; -}; - -V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData) - -static QV4::ReturnedValue get_index(const QV4::FunctionObject *f, const QV4::Value *thisObject, const QV4::Value *, int) -{ - QV4::Scope scope(f); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"))); - - RETURN_RESULT(QV4::Encode(o->d()->item->index)); -} - -template <typename T, typename M> static void setModelDataType(QMetaObjectBuilder *builder, M *metaType) -{ - builder->setFlags(QMetaObjectBuilder::DynamicMetaObject); - builder->setClassName(T::staticMetaObject.className()); - builder->setSuperClass(&T::staticMetaObject); - metaType->propertyOffset = T::staticMetaObject.propertyCount(); - metaType->signalOffset = T::staticMetaObject.methodCount(); -} - -static void addProperty(QMetaObjectBuilder *builder, int propertyId, const QByteArray &propertyName, const QByteArray &propertyType) -{ - builder->addSignal("__" + QByteArray::number(propertyId) + "()"); - QMetaPropertyBuilder property = builder->addProperty( - propertyName, propertyType, propertyId); - property.setWritable(true); -} - -class VDMModelDelegateDataType; - -class QQmlDMCachedModelData : public QQmlDelegateModelItem -{ -public: - QQmlDMCachedModelData( - QQmlDelegateModelItemMetaType *metaType, - VDMModelDelegateDataType *dataType, - int index, int row, int column); - - int metaCall(QMetaObject::Call call, int id, void **arguments); - - virtual QVariant value(int role) const = 0; - virtual void setValue(int role, const QVariant &value) = 0; - - void setValue(const QString &role, const QVariant &value) override; - bool resolveIndex(const QQmlAdaptorModel &model, int idx) override; - - static QV4::ReturnedValue get_property(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - static QV4::ReturnedValue set_property(const QV4::FunctionObject *, const QV4::Value *thisObject, const QV4::Value *argv, int argc); - - VDMModelDelegateDataType *type; - QVector<QVariant> cachedData; -}; - -class VDMModelDelegateDataType - : public QQmlRefCount - , public QQmlAdaptorModel::Accessors - , public QAbstractDynamicMetaObject -{ -public: - VDMModelDelegateDataType(QQmlAdaptorModel *model) - : model(model) - , propertyOffset(0) - , signalOffset(0) - , hasModelData(false) - { - } - - bool notify( - const QQmlAdaptorModel &, - const QList<QQmlDelegateModelItem *> &items, - int index, - int count, - const QVector<int> &roles) const override - { - bool changed = roles.isEmpty() && !watchedRoles.isEmpty(); - if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) { - QList<int> roleIds; - for (const QByteArray &r : watchedRoles) { - QHash<QByteArray, int>::const_iterator it = roleNames.find(r); - if (it != roleNames.end()) - roleIds << it.value(); - } - const_cast<VDMModelDelegateDataType *>(this)->watchedRoleIds = roleIds; - } - - QVector<int> signalIndexes; - for (int i = 0; i < roles.count(); ++i) { - const int role = roles.at(i); - if (!changed && watchedRoleIds.contains(role)) - changed = true; - - int propertyId = propertyRoles.indexOf(role); - if (propertyId != -1) - signalIndexes.append(propertyId + signalOffset); - } - if (roles.isEmpty()) { - const int propertyRolesCount = propertyRoles.count(); - signalIndexes.reserve(propertyRolesCount); - for (int propertyId = 0; propertyId < propertyRolesCount; ++propertyId) - signalIndexes.append(propertyId + signalOffset); - } - - for (int i = 0, c = items.count(); i < c; ++i) { - QQmlDelegateModelItem *item = items.at(i); - const int idx = item->modelIndex(); - if (idx >= index && idx < index + count) { - for (int i = 0; i < signalIndexes.count(); ++i) - QMetaObject::activate(item, signalIndexes.at(i), nullptr); - } - } - return changed; - } - - void replaceWatchedRoles( - QQmlAdaptorModel &, - const QList<QByteArray> &oldRoles, - const QList<QByteArray> &newRoles) const override - { - VDMModelDelegateDataType *dataType = const_cast<VDMModelDelegateDataType *>(this); - - dataType->watchedRoleIds.clear(); - for (const QByteArray &oldRole : oldRoles) - dataType->watchedRoles.removeOne(oldRole); - dataType->watchedRoles += newRoles; - } - - static QV4::ReturnedValue get_hasModelChildren(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) - { - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object"))); - - const QQmlAdaptorModel *const model = static_cast<QQmlDMCachedModelData *>(o->d()->item)->type->model; - if (o->d()->item->index >= 0 && *model) { - const QAbstractItemModel * const aim = model->aim(); - RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex)))); - } else { - RETURN_RESULT(QV4::Encode(false)); - } - } - - - void initializeConstructor(QQmlAdaptorModelEngineData *const data) - { - QV4::ExecutionEngine *v4 = data->v4; - QV4::Scope scope(v4); - QV4::ScopedObject proto(scope, v4->newObject()); - proto->defineAccessorProperty(QStringLiteral("index"), get_index, nullptr); - proto->defineAccessorProperty(QStringLiteral("hasModelChildren"), get_hasModelChildren, nullptr); - QV4::ScopedProperty p(scope); - - typedef QHash<QByteArray, int>::const_iterator iterator; - for (iterator it = roleNames.constBegin(), end = roleNames.constEnd(); it != end; ++it) { - const int propertyId = propertyRoles.indexOf(it.value()); - const QByteArray &propertyName = it.key(); - - QV4::ScopedString name(scope, v4->newString(QString::fromUtf8(propertyName))); - QV4::ExecutionContext *global = v4->rootContext(); - QV4::ScopedFunctionObject g(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::get_property)); - QV4::ScopedFunctionObject s(scope, v4->memoryManager->allocate<QV4::IndexedBuiltinFunction>(global, propertyId, QQmlDMCachedModelData::set_property)); - p->setGetter(g); - p->setSetter(s); - proto->insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable|QV4::Attr_NotConfigurable); - } - prototype.set(v4, proto); - } - - // QAbstractDynamicMetaObject - - void objectDestroyed(QObject *) override - { - release(); - } - - int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override - { - return static_cast<QQmlDMCachedModelData *>(object)->metaCall(call, id, arguments); - } - - QV4::PersistentValue prototype; - QList<int> propertyRoles; - QList<int> watchedRoleIds; - QList<QByteArray> watchedRoles; - QHash<QByteArray, int> roleNames; - QQmlAdaptorModel *model; - int propertyOffset; - int signalOffset; - bool hasModelData; -}; - -QQmlDMCachedModelData::QQmlDMCachedModelData(QQmlDelegateModelItemMetaType *metaType, VDMModelDelegateDataType *dataType, int index, int row, int column) - : QQmlDelegateModelItem(metaType, dataType, index, row, column) - , type(dataType) -{ - if (index == -1) - cachedData.resize(type->hasModelData ? 1 : type->propertyRoles.count()); - - QObjectPrivate::get(this)->metaObject = type; - - type->addref(); -} - -int QQmlDMCachedModelData::metaCall(QMetaObject::Call call, int id, void **arguments) -{ - if (call == QMetaObject::ReadProperty && id >= type->propertyOffset) { - const int propertyIndex = id - type->propertyOffset; - if (index == -1) { - if (!cachedData.isEmpty()) { - *static_cast<QVariant *>(arguments[0]) = cachedData.at( - type->hasModelData ? 0 : propertyIndex); - } - } else if (*type->model) { - *static_cast<QVariant *>(arguments[0]) = value(type->propertyRoles.at(propertyIndex)); - } - return -1; - } else if (call == QMetaObject::WriteProperty && id >= type->propertyOffset) { - const int propertyIndex = id - type->propertyOffset; - if (index == -1) { - const QMetaObject *meta = metaObject(); - if (cachedData.count() > 1) { - cachedData[propertyIndex] = *static_cast<QVariant *>(arguments[0]); - QMetaObject::activate(this, meta, propertyIndex, nullptr); - } else if (cachedData.count() == 1) { - cachedData[0] = *static_cast<QVariant *>(arguments[0]); - QMetaObject::activate(this, meta, 0, nullptr); - QMetaObject::activate(this, meta, 1, nullptr); - } - } else if (*type->model) { - setValue(type->propertyRoles.at(propertyIndex), *static_cast<QVariant *>(arguments[0])); - } - return -1; - } else { - return qt_metacall(call, id, arguments); - } -} - -void QQmlDMCachedModelData::setValue(const QString &role, const QVariant &value) -{ - QHash<QByteArray, int>::iterator it = type->roleNames.find(role.toUtf8()); - if (it != type->roleNames.end()) { - for (int i = 0; i < type->propertyRoles.count(); ++i) { - if (type->propertyRoles.at(i) == *it) { - cachedData[i] = value; - return; - } - } - } -} - -bool QQmlDMCachedModelData::resolveIndex(const QQmlAdaptorModel &adaptorModel, int idx) -{ - if (index == -1) { - Q_ASSERT(idx >= 0); - cachedData.clear(); - setModelIndex(idx, adaptorModel.rowAt(idx), adaptorModel.columnAt(idx)); - const QMetaObject *meta = metaObject(); - const int propertyCount = type->propertyRoles.count(); - for (int i = 0; i < propertyCount; ++i) - QMetaObject::activate(this, meta, i, nullptr); - return true; - } else { - return false; - } -} - -QV4::ReturnedValue QQmlDMCachedModelData::get_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index; - - QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item); - if (o->d()->item->index == -1) { - if (!modelData->cachedData.isEmpty()) { - return scope.engine->fromVariant( - modelData->cachedData.at(modelData->type->hasModelData ? 0 : propertyId)); - } - } else if (*modelData->type->model) { - return scope.engine->fromVariant( - modelData->value(modelData->type->propertyRoles.at(propertyId))); - } - return QV4::Encode::undefined(); -} - -QV4::ReturnedValue QQmlDMCachedModelData::set_property(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) -{ - QV4::Scope scope(b); - QV4::Scoped<QQmlDelegateModelItemObject> o(scope, thisObject->as<QQmlDelegateModelItemObject>()); - if (!o) - return scope.engine->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - if (!argc) - return scope.engine->throwTypeError(); - - uint propertyId = static_cast<const QV4::IndexedBuiltinFunction *>(b)->d()->index; - - if (o->d()->item->index == -1) { - QQmlDMCachedModelData *modelData = static_cast<QQmlDMCachedModelData *>(o->d()->item); - if (!modelData->cachedData.isEmpty()) { - if (modelData->cachedData.count() > 1) { - modelData->cachedData[propertyId] = scope.engine->toVariant(argv[0], QVariant::Invalid); - QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), propertyId, nullptr); - } else if (modelData->cachedData.count() == 1) { - modelData->cachedData[0] = scope.engine->toVariant(argv[0], QVariant::Invalid); - QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 0, nullptr); - QMetaObject::activate(o->d()->item, o->d()->item->metaObject(), 1, nullptr); - } - } - } - return QV4::Encode::undefined(); -} - -//----------------------------------------------------------------- -// QAbstractItemModel -//----------------------------------------------------------------- - -class QQmlDMAbstractItemModelData : public QQmlDMCachedModelData -{ - Q_OBJECT - Q_PROPERTY(bool hasModelChildren READ hasModelChildren CONSTANT) - -public: - QQmlDMAbstractItemModelData( - QQmlDelegateModelItemMetaType *metaType, - VDMModelDelegateDataType *dataType, - int index, int row, int column) - : QQmlDMCachedModelData(metaType, dataType, index, row, column) - { - } - - bool hasModelChildren() const - { - if (index >= 0 && *type->model) { - const QAbstractItemModel * const model = type->model->aim(); - return model->hasChildren(model->index(row, column, type->model->rootIndex)); - } else { - return false; - } - } - - QVariant value(int role) const override - { - return type->model->aim()->index(row, column, type->model->rootIndex).data(role); - } - - void setValue(int role, const QVariant &value) override - { - type->model->aim()->setData( - type->model->aim()->index(row, column, type->model->rootIndex), value, role); - } - - QV4::ReturnedValue get() override - { - if (type->prototype.isUndefined()) { - QQmlAdaptorModelEngineData * const data = engineData(v4); - type->initializeConstructor(data); - } - QV4::Scope scope(v4); - QV4::ScopedObject proto(scope, type->prototype.value()); - QV4::ScopedObject o(scope, proto->engine()->memoryManager->allocate<QQmlDelegateModelItemObject>(this)); - o->setPrototypeOf(proto); - ++scriptRef; - return o.asReturnedValue(); - } -}; - -class VDMAbstractItemModelDataType : public VDMModelDelegateDataType -{ -public: - VDMAbstractItemModelDataType(QQmlAdaptorModel *model) - : VDMModelDelegateDataType(model) - { - } - - int rowCount(const QQmlAdaptorModel &model) const override - { - return model.aim()->rowCount(model.rootIndex); - } - - int columnCount(const QQmlAdaptorModel &model) const override - { - return model.aim()->columnCount(model.rootIndex); - } - - void cleanup(QQmlAdaptorModel &) const override - { - const_cast<VDMAbstractItemModelDataType *>(this)->release(); - } - - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override - { - QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8()); - if (it != roleNames.end()) { - return model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex).data(*it); - } else if (role == QLatin1String("hasModelChildren")) { - return QVariant(model.aim()->hasChildren(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex))); - } else { - return QVariant(); - } - } - - QVariant parentModelIndex(const QQmlAdaptorModel &model) const override - { - return model - ? QVariant::fromValue(model.aim()->parent(model.rootIndex)) - : QVariant(); - } - - QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override - { - return model - ? QVariant::fromValue(model.aim()->index(model.rowAt(index), model.columnAt(index), model.rootIndex)) - : QVariant(); - } - - bool canFetchMore(const QQmlAdaptorModel &model) const override - { - return model && model.aim()->canFetchMore(model.rootIndex); - } - - void fetchMore(QQmlAdaptorModel &model) const override - { - if (model) - model.aim()->fetchMore(model.rootIndex); - } - - QQmlDelegateModelItem *createItem( - QQmlAdaptorModel &model, - QQmlDelegateModelItemMetaType *metaType, - int index, int row, int column) const override - { - VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this); - if (!metaObject) - dataType->initializeMetaType(model); - return new QQmlDMAbstractItemModelData(metaType, dataType, index, row, column); - } - - void initializeMetaType(QQmlAdaptorModel &model) - { - QMetaObjectBuilder builder; - setModelDataType<QQmlDMAbstractItemModelData>(&builder, this); - - const QByteArray propertyType = QByteArrayLiteral("QVariant"); - const QHash<int, QByteArray> names = model.aim()->roleNames(); - for (QHash<int, QByteArray>::const_iterator it = names.begin(), cend = names.end(); it != cend; ++it) { - const int propertyId = propertyRoles.count(); - propertyRoles.append(it.key()); - roleNames.insert(it.value(), it.key()); - addProperty(&builder, propertyId, it.value(), propertyType); - } - if (propertyRoles.count() == 1) { - hasModelData = true; - const int role = names.begin().key(); - const QByteArray propertyName = QByteArrayLiteral("modelData"); - - propertyRoles.append(role); - roleNames.insert(propertyName, role); - addProperty(&builder, 1, propertyName, propertyType); - } - - metaObject.reset(builder.toMetaObject()); - *static_cast<QMetaObject *>(this) = *metaObject; - propertyCache.adopt(new QQmlPropertyCache(metaObject.data(), model.modelItemRevision)); - } -}; - -//----------------------------------------------------------------- -// QQmlListAccessor -//----------------------------------------------------------------- - -class QQmlDMListAccessorData : public QQmlDelegateModelItem -{ - Q_OBJECT - Q_PROPERTY(QVariant modelData READ modelData WRITE setModelData NOTIFY modelDataChanged) -public: - QQmlDMListAccessorData(QQmlDelegateModelItemMetaType *metaType, - QQmlAdaptorModel::Accessors *accessor, - int index, int row, int column, const QVariant &value) - : QQmlDelegateModelItem(metaType, accessor, index, row, column) - , cachedData(value) - { - } - - QVariant modelData() const - { - return cachedData; - } - - void setModelData(const QVariant &data) - { - if (data == cachedData) - return; - - cachedData = data; - emit modelDataChanged(); - } - - static QV4::ReturnedValue get_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *, int) - { - QV4::ExecutionEngine *v4 = b->engine(); - const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>(); - if (!o) - return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - - return v4->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData); - } - - static QV4::ReturnedValue set_modelData(const QV4::FunctionObject *b, const QV4::Value *thisObject, const QV4::Value *argv, int argc) - { - QV4::ExecutionEngine *v4 = b->engine(); - const QQmlDelegateModelItemObject *o = thisObject->as<QQmlDelegateModelItemObject>(); - if (!o) - return v4->throwTypeError(QStringLiteral("Not a valid DelegateModel object")); - if (!argc) - return v4->throwTypeError(); - - static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(v4->toVariant(argv[0], QVariant::Invalid)); - return QV4::Encode::undefined(); - } - - QV4::ReturnedValue get() override - { - QQmlAdaptorModelEngineData *data = engineData(v4); - QV4::Scope scope(v4); - QV4::ScopedObject o(scope, v4->memoryManager->allocate<QQmlDelegateModelItemObject>(this)); - QV4::ScopedObject p(scope, data->listItemProto.value()); - o->setPrototypeOf(p); - ++scriptRef; - return o.asReturnedValue(); - } - - void setValue(const QString &role, const QVariant &value) override - { - if (role == QLatin1String("modelData")) - cachedData = value; - } - - bool resolveIndex(const QQmlAdaptorModel &model, int idx) override - { - if (index == -1) { - index = idx; - cachedData = model.list.at(idx); - emit modelIndexChanged(); - emit modelDataChanged(); - return true; - } else { - return false; - } - } - - -Q_SIGNALS: - void modelDataChanged(); - -private: - QVariant cachedData; -}; - - -class VDMListDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors -{ -public: - VDMListDelegateDataType() - : QQmlRefCount() - , QQmlAdaptorModel::Accessors() - {} - - void cleanup(QQmlAdaptorModel &) const override - { - const_cast<VDMListDelegateDataType *>(this)->release(); - } - - int rowCount(const QQmlAdaptorModel &model) const override - { - return model.list.count(); - } - - int columnCount(const QQmlAdaptorModel &) const override - { - return 1; - } - - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override - { - return role == QLatin1String("modelData") - ? model.list.at(index) - : QVariant(); - } - - QQmlDelegateModelItem *createItem( - QQmlAdaptorModel &model, - QQmlDelegateModelItemMetaType *metaType, - int index, int row, int column) const override - { - VDMListDelegateDataType *dataType = const_cast<VDMListDelegateDataType *>(this); - if (!propertyCache) { - dataType->propertyCache.adopt(new QQmlPropertyCache( - &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision)); - } - - return new QQmlDMListAccessorData( - metaType, - dataType, - index, row, column, - index >= 0 && index < model.list.count() ? model.list.at(index) : QVariant()); - } - - bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override - { - for (auto modelItem : items) { - const int modelItemIndex = modelItem->index; - if (modelItemIndex < index || modelItemIndex >= index + count) - continue; - - auto listModelItem = static_cast<QQmlDMListAccessorData *>(modelItem); - QVariant updatedModelData = model.list.at(listModelItem->index); - listModelItem->setModelData(updatedModelData); - } - return true; - } -}; - -//----------------------------------------------------------------- -// QObject -//----------------------------------------------------------------- - -class VDMObjectDelegateDataType; -class QQmlDMObjectData : public QQmlDelegateModelItem, public QQmlAdaptorModelProxyInterface -{ - Q_OBJECT - Q_PROPERTY(QObject *modelData READ modelData NOTIFY modelDataChanged) - Q_INTERFACES(QQmlAdaptorModelProxyInterface) -public: - QQmlDMObjectData( - QQmlDelegateModelItemMetaType *metaType, - VDMObjectDelegateDataType *dataType, - int index, int row, int column, - QObject *object); - - void setModelData(QObject *modelData) - { - if (modelData == object) - return; - - object = modelData; - emit modelDataChanged(); - } - - QObject *modelData() const { return object; } - QObject *proxiedObject() override { return object; } - - QPointer<QObject> object; - -Q_SIGNALS: - void modelDataChanged(); -}; - -class VDMObjectDelegateDataType : public QQmlRefCount, public QQmlAdaptorModel::Accessors -{ -public: - int propertyOffset; - int signalOffset; - bool shared; - QMetaObjectBuilder builder; - - VDMObjectDelegateDataType() - : propertyOffset(0) - , signalOffset(0) - , shared(true) - { - } - - VDMObjectDelegateDataType(const VDMObjectDelegateDataType &type) - : QQmlRefCount() - , QQmlAdaptorModel::Accessors() - , propertyOffset(type.propertyOffset) - , signalOffset(type.signalOffset) - , shared(false) - , builder(type.metaObject.data(), QMetaObjectBuilder::Properties - | QMetaObjectBuilder::Signals - | QMetaObjectBuilder::SuperClass - | QMetaObjectBuilder::ClassName) - { - builder.setFlags(QMetaObjectBuilder::DynamicMetaObject); - } - - int rowCount(const QQmlAdaptorModel &model) const override - { - return model.list.count(); - } - - int columnCount(const QQmlAdaptorModel &) const override - { - return 1; - } - - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override - { - if (QObject *object = model.list.at(index).value<QObject *>()) - return object->property(role.toUtf8()); - return QVariant(); - } - - QQmlDelegateModelItem *createItem( - QQmlAdaptorModel &model, - QQmlDelegateModelItemMetaType *metaType, - int index, int row, int column) const override - { - VDMObjectDelegateDataType *dataType = const_cast<VDMObjectDelegateDataType *>(this); - if (!metaObject) - dataType->initializeMetaType(model); - return index >= 0 && index < model.list.count() - ? new QQmlDMObjectData(metaType, dataType, index, row, column, qvariant_cast<QObject *>(model.list.at(index))) - : nullptr; - } - - void initializeMetaType(QQmlAdaptorModel &model) - { - Q_UNUSED(model); - setModelDataType<QQmlDMObjectData>(&builder, this); - - metaObject.reset(builder.toMetaObject()); - // Note: ATM we cannot create a shared property cache for this class, since each model - // object can have different properties. And to make those properties available to the - // delegate, QQmlDMObjectData makes use of a QAbstractDynamicMetaObject subclass - // (QQmlDMObjectDataMetaObject), which we cannot represent in a QQmlPropertyCache. - // By not having a shared property cache, revisioned properties in QQmlDelegateModelItem - // will always be available to the delegate, regardless of the import version. - } - - void cleanup(QQmlAdaptorModel &) const override - { - const_cast<VDMObjectDelegateDataType *>(this)->release(); - } - - bool notify(const QQmlAdaptorModel &model, const QList<QQmlDelegateModelItem *> &items, int index, int count, const QVector<int> &) const override - { - for (auto modelItem : items) { - const int modelItemIndex = modelItem->index; - if (modelItemIndex < index || modelItemIndex >= index + count) - continue; - - auto objectModelItem = static_cast<QQmlDMObjectData *>(modelItem); - QObject *updatedModelData = qvariant_cast<QObject *>(model.list.at(objectModelItem->index)); - objectModelItem->setModelData(updatedModelData); - } - return true; - } -}; - -class QQmlDMObjectDataMetaObject : public QAbstractDynamicMetaObject -{ -public: - QQmlDMObjectDataMetaObject(QQmlDMObjectData *data, VDMObjectDelegateDataType *type) - : m_data(data) - , m_type(type) - { - QObjectPrivate *op = QObjectPrivate::get(m_data); - *static_cast<QMetaObject *>(this) = *type->metaObject; - op->metaObject = this; - m_type->addref(); - } - - ~QQmlDMObjectDataMetaObject() - { - m_type->release(); - } - - int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override - { - Q_ASSERT(o == m_data); - Q_UNUSED(o); - - static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); - if (id >= m_type->propertyOffset - && (call == QMetaObject::ReadProperty - || call == QMetaObject::WriteProperty - || call == QMetaObject::ResetProperty)) { - if (m_data->object) - QMetaObject::metacall(m_data->object, call, id - m_type->propertyOffset + objectPropertyOffset, arguments); - return -1; - } else if (id >= m_type->signalOffset && call == QMetaObject::InvokeMetaMethod) { - QMetaObject::activate(m_data, this, id - m_type->signalOffset, nullptr); - return -1; - } else { - return m_data->qt_metacall(call, id, arguments); - } - } - - int createProperty(const char *name, const char *) override - { - if (!m_data->object) - return -1; - const QMetaObject *metaObject = m_data->object->metaObject(); - static const int objectPropertyOffset = QObject::staticMetaObject.propertyCount(); - - const int previousPropertyCount = propertyCount() - propertyOffset(); - int propertyIndex = metaObject->indexOfProperty(name); - if (propertyIndex == -1) - return -1; - if (previousPropertyCount + objectPropertyOffset == metaObject->propertyCount()) - return propertyIndex + m_type->propertyOffset - objectPropertyOffset; - - if (m_type->shared) { - VDMObjectDelegateDataType *type = m_type; - m_type = new VDMObjectDelegateDataType(*m_type); - type->release(); - } - - const int previousMethodCount = methodCount(); - int notifierId = previousMethodCount - methodOffset(); - for (int propertyId = previousPropertyCount; propertyId < metaObject->propertyCount() - objectPropertyOffset; ++propertyId) { - QMetaProperty property = metaObject->property(propertyId + objectPropertyOffset); - QMetaPropertyBuilder propertyBuilder; - if (property.hasNotifySignal()) { - m_type->builder.addSignal("__" + QByteArray::number(propertyId) + "()"); - propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName(), notifierId); - ++notifierId; - } else { - propertyBuilder = m_type->builder.addProperty(property.name(), property.typeName()); - } - propertyBuilder.setWritable(property.isWritable()); - propertyBuilder.setResettable(property.isResettable()); - propertyBuilder.setConstant(property.isConstant()); - } - - m_type->metaObject.reset(m_type->builder.toMetaObject()); - *static_cast<QMetaObject *>(this) = *m_type->metaObject; - - notifierId = previousMethodCount; - for (int i = previousPropertyCount; i < metaObject->propertyCount() - objectPropertyOffset; ++i) { - QMetaProperty property = metaObject->property(i + objectPropertyOffset); - if (property.hasNotifySignal()) { - QQmlPropertyPrivate::connect( - m_data->object, property.notifySignalIndex(), m_data, notifierId); - ++notifierId; - } - } - return propertyIndex + m_type->propertyOffset - objectPropertyOffset; - } - - QQmlDMObjectData *m_data; - VDMObjectDelegateDataType *m_type; -}; - -QQmlDMObjectData::QQmlDMObjectData(QQmlDelegateModelItemMetaType *metaType, - VDMObjectDelegateDataType *dataType, - int index, int row, int column, - QObject *object) - : QQmlDelegateModelItem(metaType, dataType, index, row, column) - , object(object) -{ - new QQmlDMObjectDataMetaObject(this, dataType); -} - -//----------------------------------------------------------------- -// QQmlAdaptorModel -//----------------------------------------------------------------- - -static const QQmlAdaptorModel::Accessors qt_vdm_null_accessors; - -QQmlAdaptorModel::Accessors::~Accessors() -{ -} - -QQmlAdaptorModel::QQmlAdaptorModel() - : accessors(&qt_vdm_null_accessors) -{ -} - -QQmlAdaptorModel::~QQmlAdaptorModel() -{ - accessors->cleanup(*this); -} - -void QQmlAdaptorModel::setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine) -{ - accessors->cleanup(*this); - - list.setList(variant, engine); - - if (QObject *object = qvariant_cast<QObject *>(list.list())) { - setObject(object, parent); - if (qobject_cast<QAbstractItemModel *>(object)) - accessors = new VDMAbstractItemModelDataType(this); - else - accessors = new VDMObjectDelegateDataType; - } else if (list.type() == QQmlListAccessor::ListProperty) { - setObject(static_cast<const QQmlListReference *>(variant.constData())->object(), parent); - accessors = new VDMObjectDelegateDataType; - } else if (list.type() != QQmlListAccessor::Invalid - && list.type() != QQmlListAccessor::Instance) { // Null QObject - setObject(nullptr, parent); - accessors = new VDMListDelegateDataType; - } else { - setObject(nullptr, parent); - accessors = &qt_vdm_null_accessors; - } -} - -void QQmlAdaptorModel::invalidateModel() -{ - accessors->cleanup(*this); - accessors = &qt_vdm_null_accessors; - // Don't clear the model object as we still need the guard to clear the list variant if the - // object is destroyed. -} - -bool QQmlAdaptorModel::isValid() const -{ - return accessors != &qt_vdm_null_accessors; -} - -int QQmlAdaptorModel::count() const -{ - return rowCount() * columnCount(); -} - -int QQmlAdaptorModel::rowCount() const -{ - return qMax(0, accessors->rowCount(*this)); -} - -int QQmlAdaptorModel::columnCount() const -{ - return qMax(0, accessors->columnCount(*this)); -} - -int QQmlAdaptorModel::rowAt(int index) const -{ - int count = rowCount(); - return count <= 0 ? -1 : index % count; -} - -int QQmlAdaptorModel::columnAt(int index) const -{ - int count = rowCount(); - return count <= 0 ? -1 : index / count; -} - -int QQmlAdaptorModel::indexAt(int row, int column) const -{ - return column * rowCount() + row; -} - -void QQmlAdaptorModel::useImportVersion(int minorVersion) -{ - modelItemRevision = minorVersion; -} - -void QQmlAdaptorModel::objectDestroyed(QObject *) -{ - setModel(QVariant(), nullptr, nullptr); -} - -QQmlAdaptorModelEngineData::QQmlAdaptorModelEngineData(QV4::ExecutionEngine *v4) - : v4(v4) -{ - QV4::Scope scope(v4); - QV4::ScopedObject proto(scope, v4->newObject()); - proto->defineAccessorProperty(QStringLiteral("index"), get_index, nullptr); - proto->defineAccessorProperty(QStringLiteral("modelData"), - QQmlDMListAccessorData::get_modelData, QQmlDMListAccessorData::set_modelData); - listItemProto.set(v4, proto); -} - -QQmlAdaptorModelEngineData::~QQmlAdaptorModelEngineData() -{ -} - -QT_END_NAMESPACE - -#include <qqmladaptormodel.moc> diff --git a/src/qml/util/qqmladaptormodel_p.h b/src/qml/util/qqmladaptormodel_p.h deleted file mode 100644 index 8c18466ab5..0000000000 --- a/src/qml/util/qqmladaptormodel_p.h +++ /dev/null @@ -1,179 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLADAPTORMODEL_P_H -#define QQMLADAPTORMODEL_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qabstractitemmodel.h> - -#include "private/qqmllistaccessor_p.h" -#include <private/qqmlglobal_p.h> -#include <private/qqmlguard_p.h> -#include <private/qqmlnullablevalue_p.h> - -QT_REQUIRE_CONFIG(qml_delegate_model); - -QT_BEGIN_NAMESPACE - -class QQmlEngine; - -class QQmlDelegateModel; -class QQmlDelegateModelItem; -class QQmlDelegateModelItemMetaType; - -class Q_QML_PRIVATE_EXPORT QQmlAdaptorModel : public QQmlStrongJSQObjectReference<QObject> -{ -public: - class Accessors - { - public: - inline Accessors() {} - virtual ~Accessors(); - virtual int rowCount(const QQmlAdaptorModel &) const { return 0; } - virtual int columnCount(const QQmlAdaptorModel &) const { return 0; } - virtual void cleanup(QQmlAdaptorModel &) const {} - - virtual QVariant value(const QQmlAdaptorModel &, int, const QString &) const { - return QVariant(); } - - virtual QQmlDelegateModelItem *createItem( - QQmlAdaptorModel &, - QQmlDelegateModelItemMetaType *, - int, int, int) const { return nullptr; } - - virtual bool notify( - const QQmlAdaptorModel &, - const QList<QQmlDelegateModelItem *> &, - int, - int, - const QVector<int> &) const { return false; } - virtual void replaceWatchedRoles( - QQmlAdaptorModel &, - const QList<QByteArray> &, - const QList<QByteArray> &) const {} - virtual QVariant parentModelIndex(const QQmlAdaptorModel &) const { - return QVariant(); } - virtual QVariant modelIndex(const QQmlAdaptorModel &, int) const { - return QVariant(); } - virtual bool canFetchMore(const QQmlAdaptorModel &) const { return false; } - virtual void fetchMore(QQmlAdaptorModel &) const {} - - QScopedPointer<QMetaObject, QScopedPointerPodDeleter> metaObject; - QQmlRefPointer<QQmlPropertyCache> propertyCache; - }; - - const Accessors *accessors; - QPersistentModelIndex rootIndex; - QQmlListAccessor list; - - int modelItemRevision = 0; - - QQmlAdaptorModel(); - ~QQmlAdaptorModel(); - - inline QVariant model() const { return list.list(); } - void setModel(const QVariant &variant, QObject *parent, QQmlEngine *engine); - void invalidateModel(); - - bool isValid() const; - int count() const; - int rowCount() const; - int columnCount() const; - int rowAt(int index) const; - int columnAt(int index) const; - int indexAt(int row, int column) const; - - void useImportVersion(int minorVersion); - - inline bool adaptsAim() const { return qobject_cast<QAbstractItemModel *>(object()); } - inline QAbstractItemModel *aim() { return static_cast<QAbstractItemModel *>(object()); } - inline const QAbstractItemModel *aim() const { return static_cast<const QAbstractItemModel *>(object()); } - - inline QVariant value(int index, const QString &role) const { - return accessors->value(*this, index, role); } - inline QQmlDelegateModelItem *createItem(QQmlDelegateModelItemMetaType *metaType, int index) { - return accessors->createItem(*this, metaType, index, rowAt(index), columnAt(index)); } - inline bool hasProxyObject() const { - return list.type() == QQmlListAccessor::Instance || list.type() == QQmlListAccessor::ListProperty; } - - inline bool notify( - const QList<QQmlDelegateModelItem *> &items, - int index, - int count, - const QVector<int> &roles) const { - return accessors->notify(*this, items, index, count, roles); } - inline void replaceWatchedRoles( - const QList<QByteArray> &oldRoles, const QList<QByteArray> &newRoles) { - accessors->replaceWatchedRoles(*this, oldRoles, newRoles); } - - inline QVariant modelIndex(int index) const { return accessors->modelIndex(*this, index); } - inline QVariant parentModelIndex() const { return accessors->parentModelIndex(*this); } - inline bool canFetchMore() const { return accessors->canFetchMore(*this); } - inline void fetchMore() { return accessors->fetchMore(*this); } - -protected: - void objectDestroyed(QObject *) override; -}; - -class QQmlAdaptorModelProxyInterface -{ -public: - virtual ~QQmlAdaptorModelProxyInterface() {} - - virtual QObject *proxiedObject() = 0; -}; - -#define QQmlAdaptorModelProxyInterface_iid "org.qt-project.Qt.QQmlAdaptorModelProxyInterface" - -Q_DECLARE_INTERFACE(QQmlAdaptorModelProxyInterface, QQmlAdaptorModelProxyInterface_iid) - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/util/qqmlchangeset.cpp b/src/qml/util/qqmlchangeset.cpp deleted file mode 100644 index ba876b42e2..0000000000 --- a/src/qml/util/qqmlchangeset.cpp +++ /dev/null @@ -1,583 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmlchangeset_p.h" - -QT_BEGIN_NAMESPACE - - -/*! - \class QQmlChangeSet - \brief The QQmlChangeSet class stores an ordered list of notifications about - changes to a linear data set. - \internal - - QQmlChangeSet can be used to record a series of notifications about items in an indexed list - being inserted, removed, moved, and changed. Notifications in the set are re-ordered so that - all notifications of a single type are grouped together and sorted in order of ascending index, - with remove notifications preceding all others, followed by insert notification, and then - change notifications. - - Moves in a change set are represented by a remove notification paired with an insert - notification by way of a shared unique moveId. Re-ordering may result in one or both of the - paired notifications being divided, when this happens the offset member of the notification - will indicate the relative offset of the divided notification from the beginning of the - original. -*/ - -/*! - Constructs an empty change set. -*/ - -QQmlChangeSet::QQmlChangeSet() - : m_difference(0) -{ -} - -/*! - Constructs a copy of a \a changeSet. -*/ - -QQmlChangeSet::QQmlChangeSet(const QQmlChangeSet &changeSet) - : m_removes(changeSet.m_removes) - , m_inserts(changeSet.m_inserts) - , m_changes(changeSet.m_changes) - , m_difference(changeSet.m_difference) -{ -} - -/*! - Destroys a change set. -*/ - -QQmlChangeSet::~QQmlChangeSet() -{ -} - -/*! - Assigns the value of a \a changeSet to another. -*/ - -QQmlChangeSet &QQmlChangeSet::operator =(const QQmlChangeSet &changeSet) -{ - m_removes = changeSet.m_removes; - m_inserts = changeSet.m_inserts; - m_changes = changeSet.m_changes; - m_difference = changeSet.m_difference; - return *this; -} - -/*! - Appends a notification that \a count items were inserted at \a index. -*/ - -void QQmlChangeSet::insert(int index, int count) -{ - insert(QVector<Change>() << Change(index, count)); -} - -/*! - Appends a notification that \a count items were removed at \a index. -*/ - -void QQmlChangeSet::remove(int index, int count) -{ - QVector<Change> removes; - removes.append(Change(index, count)); - remove(&removes, nullptr); -} - -/*! - Appends a notification that \a count items were moved \a from one index \a to another. - - The \a moveId must be unique across the lifetime of the change set and any related - change sets. -*/ - -void QQmlChangeSet::move(int from, int to, int count, int moveId) -{ - QVector<Change> removes; - removes.append(Change(from, count, moveId)); - QVector<Change> inserts; - inserts.append(Change(to, count, moveId)); - remove(&removes, &inserts); - insert(inserts); -} - -/*! - Appends a notification that \a count items were changed at \a index. -*/ - -void QQmlChangeSet::change(int index, int count) -{ - QVector<Change> changes; - changes.append(Change(index, count)); - change(changes); -} - -/*! - Applies the changes in a \a changeSet to another. -*/ - -void QQmlChangeSet::apply(const QQmlChangeSet &changeSet) -{ - QVector<Change> r = changeSet.m_removes; - QVector<Change> i = changeSet.m_inserts; - QVector<Change> c = changeSet.m_changes; - remove(&r, &i); - insert(i); - change(c); -} - -/*! - Applies a list of \a removes to a change set. - - If a remove contains a moveId then any intersecting insert in the set will replace the - corresponding intersection in the optional \a inserts list. -*/ - -void QQmlChangeSet::remove(const QVector<Change> &removes, QVector<Change> *inserts) -{ - QVector<Change> r = removes; - remove(&r, inserts); -} - -void QQmlChangeSet::remove(QVector<Change> *removes, QVector<Change> *inserts) -{ - int removeCount = 0; - int insertCount = 0; - QVector<Change>::iterator insert = m_inserts.begin(); - QVector<Change>::iterator change = m_changes.begin(); - QVector<Change>::iterator rit = removes->begin(); - for (; rit != removes->end(); ++rit) { - int index = rit->index + removeCount; - int count = rit->count; - - // Decrement the accumulated remove count from the indexes of any changes prior to the - // current remove. - for (; change != m_changes.end() && change->end() < rit->index; ++change) - change->index -= removeCount; - // Remove any portion of a change notification that intersects the current remove. - for (; change != m_changes.end() && change->index > rit->end(); ++change) { - change->count -= qMin(change->end(), rit->end()) - qMax(change->index, rit->index); - if (change->count == 0) { - change = m_changes.erase(change); - } else if (rit->index < change->index) { - change->index = rit->index; - } - } - - // Decrement the accumulated remove count from the indexes of any inserts prior to the - // current remove. - for (; insert != m_inserts.end() && insert->end() <= index; ++insert) { - insertCount += insert->count; - insert->index -= removeCount; - } - - rit->index -= insertCount; - - // Remove any portion of a insert notification that intersects the current remove. - while (insert != m_inserts.end() && insert->index < index + count) { - int offset = index - insert->index; - const int difference = qMin(insert->end(), index + count) - qMax(insert->index, index); - - // If part of the remove or insert that precedes the intersection has a moveId create - // a new delta for that portion and subtract the size of that delta from the current - // one. - if (offset < 0 && rit->moveId != -1) { - rit = removes->insert(rit, Change( - rit->index, -offset, rit->moveId, rit->offset)); - ++rit; - rit->count -= -offset; - rit->offset += -offset; - index += -offset; - count -= -offset; - removeCount += -offset; - offset = 0; - } else if (offset > 0 && insert->moveId != -1) { - insert = m_inserts.insert(insert, Change( - insert->index - removeCount, offset, insert->moveId, insert->offset)); - ++insert; - insert->index += offset; - insert->count -= offset; - insert->offset += offset; - rit->index -= offset; - insertCount += offset; - } - - // If the current remove has a move id, find any inserts with the same move id and - // replace the corresponding sections with the insert removed from the change set. - if (rit->moveId != -1 && difference > 0 && inserts) { - for (QVector<Change>::iterator iit = inserts->begin(); iit != inserts->end(); ++iit) { - if (iit->moveId != rit->moveId - || rit->offset > iit->offset + iit->count - || iit->offset > rit->offset + difference) { - continue; - } - // If the intersecting insert starts before the replacement one create - // a new insert for the portion prior to the replacement insert. - const int overlapOffset = rit->offset - iit->offset; - if (overlapOffset > 0) { - iit = inserts->insert(iit, Change( - iit->index, overlapOffset, iit->moveId, iit->offset)); - ++iit; - iit->index += overlapOffset; - iit->count -= overlapOffset; - iit->offset += overlapOffset; - } - if (iit->offset >= rit->offset - && iit->offset + iit->count <= rit->offset + difference) { - // If the replacement insert completely encapsulates the existing - // one just change the moveId. - iit->moveId = insert->moveId; - iit->offset = insert->offset + qMax(0, -overlapOffset); - } else { - // Create a new insertion before the intersecting one with the number of intersecting - // items and remove that number from that insert. - const int count - = qMin(iit->offset + iit->count, rit->offset + difference) - - qMax(iit->offset, rit->offset); - iit = inserts->insert(iit, Change( - iit->index, - count, - insert->moveId, - insert->offset + qMax(0, -overlapOffset))); - ++iit; - iit->index += count; - iit->count -= count; - iit->offset += count; - } - } - } - - // Subtract the number of intersecting items from the current remove and insert. - insert->count -= difference; - insert->offset += difference; - rit->count -= difference; - rit->offset += difference; - - index += difference; - count -= difference; - removeCount += difference; - - if (insert->count == 0) { - insert = m_inserts.erase(insert); - } else if (rit->count == -offset || rit->count == 0) { - insert->index += difference; - break; - } else { - insert->index -= removeCount - difference; - rit->index -= insert->count; - insertCount += insert->count; - ++insert; - } - } - removeCount += rit->count; - } - for (; insert != m_inserts.end(); ++insert) - insert->index -= removeCount; - - removeCount = 0; - QVector<Change>::iterator remove = m_removes.begin(); - for (rit = removes->begin(); rit != removes->end(); ++rit) { - if (rit->count == 0) - continue; - // Accumulate consecutive removes into a single delta before attempting to apply. - for (QVector<Change>::iterator next = rit + 1; next != removes->end() - && next->index == rit->index - && next->moveId == -1 - && rit->moveId == -1; ++next) { - next->count += rit->count; - rit = next; - } - int index = rit->index + removeCount; - // Decrement the accumulated remove count from the indexes of any inserts prior to the - // current remove. - for (; remove != m_removes.end() && index > remove->index; ++remove) - remove->index -= removeCount; - while (remove != m_removes.end() && index + rit->count >= remove->index) { - int count = 0; - const int offset = remove->index - index; - QVector<Change>::iterator rend = remove; - for (; rend != m_removes.end() - && rit->moveId == -1 - && rend->moveId == -1 - && index + rit->count >= rend->index; ++rend) { - count += rend->count; - } - if (remove != rend) { - // Accumulate all existing non-move removes that are encapsulated by or immediately - // follow the current remove into it. - int difference = 0; - if (rend == m_removes.end()) { - difference = rit->count; - } else if (rit->index + rit->count < rend->index - removeCount) { - difference = rit->count; - } else if (rend->moveId != -1) { - difference = rend->index - removeCount - rit->index; - index += difference; - } - count += difference; - - rit->count -= difference; - removeCount += difference; - remove->index = rit->index; - remove->count = count; - remove = m_removes.erase(++remove, rend); - } else { - // Insert a remove for the portion of the unmergable current remove prior to the - // point of intersection. - if (offset > 0) { - remove = m_removes.insert(remove, Change( - rit->index, offset, rit->moveId, rit->offset)); - ++remove; - rit->count -= offset; - rit->offset += offset; - removeCount += offset; - index += offset; - } - remove->index = rit->index; - - ++remove; - } - } - - if (rit->count > 0) { - remove = m_removes.insert(remove, *rit); - ++remove; - } - removeCount += rit->count; - } - for (; remove != m_removes.end(); ++remove) - remove->index -= removeCount; - m_difference -= removeCount; -} - -/*! - Applies a list of \a inserts to a change set. -*/ - -void QQmlChangeSet::insert(const QVector<Change> &inserts) -{ - int insertCount = 0; - QVector<Change>::iterator insert = m_inserts.begin(); - QVector<Change>::iterator change = m_changes.begin(); - for (QVector<Change>::const_iterator iit = inserts.begin(); iit != inserts.end(); ++iit) { - if (iit->count == 0) - continue; - int index = iit->index - insertCount; - - Change current = *iit; - // Accumulate consecutive inserts into a single delta before attempting to insert. - for (QVector<Change>::const_iterator next = iit + 1; next != inserts.end() - && next->index == iit->index + iit->count - && next->moveId == -1 - && iit->moveId == -1; ++next) { - current.count += next->count; - iit = next; - } - - // Increment the index of any changes before the current insert by the accumlated insert - // count. - for (; change != m_changes.end() && change->index >= index; ++change) - change->index += insertCount; - // If the current insert index is in the middle of a change split it in two at that - // point and increment the index of the latter half. - if (change != m_changes.end() && change->index < index + iit->count) { - int offset = index - change->index; - change = m_changes.insert(change, Change(change->index + insertCount, offset)); - ++change; - change->index += iit->count + offset; - change->count -= offset; - } - - // Increment the index of any inserts before the current insert by the accumlated insert - // count. - for (; insert != m_inserts.end() && index > insert->index + insert->count; ++insert) - insert->index += insertCount; - if (insert == m_inserts.end()) { - insert = m_inserts.insert(insert, current); - ++insert; - } else { - const int offset = index - insert->index; - - if (offset < 0) { - // If the current insert is before an existing insert and not adjacent just insert - // it into the list. - insert = m_inserts.insert(insert, current); - ++insert; - } else if (iit->moveId == -1 && insert->moveId == -1) { - // If neither the current nor existing insert has a moveId add the current insert - // to the existing one. - if (offset < insert->count) { - insert->index -= current.count; - insert->count += current.count; - } else { - insert->index += insertCount; - insert->count += current.count; - ++insert; - } - } else if (offset < insert->count) { - // If either insert has a moveId then split the existing insert and insert the - // current one in the middle. - if (offset > 0) { - insert = m_inserts.insert(insert, Change( - insert->index + insertCount, offset, insert->moveId, insert->offset)); - ++insert; - insert->index += offset; - insert->count -= offset; - insert->offset += offset; - } - insert = m_inserts.insert(insert, current); - ++insert; - } else { - insert->index += insertCount; - ++insert; - insert = m_inserts.insert(insert, current); - ++insert; - } - } - insertCount += current.count; - } - for (; insert != m_inserts.end(); ++insert) - insert->index += insertCount; - m_difference += insertCount; -} - -/*! - Applies a combined list of \a removes and \a inserts to a change set. This is equivalent - calling \l remove() followed by \l insert() with the same lists. -*/ - -void QQmlChangeSet::move(const QVector<Change> &removes, const QVector<Change> &inserts) -{ - QVector<Change> r = removes; - QVector<Change> i = inserts; - remove(&r, &i); - insert(i); -} - -/*! - Applies a list of \a changes to a change set. -*/ - -void QQmlChangeSet::change(const QVector<Change> &changes) -{ - QVector<Change> c = changes; - change(&c); -} - -void QQmlChangeSet::change(QVector<Change> *changes) -{ - QVector<Change>::iterator insert = m_inserts.begin(); - QVector<Change>::iterator change = m_changes.begin(); - for (QVector<Change>::iterator cit = changes->begin(); cit != changes->end(); ++cit) { - for (; insert != m_inserts.end() && insert->end() < cit->index; ++insert) {} - for (; insert != m_inserts.end() && insert->index < cit->end(); ++insert) { - const int offset = insert->index - cit->index; - const int count = cit->count + cit->index - insert->index - insert->count; - if (offset == 0) { - cit->index = insert->index + insert->count; - cit->count = count; - } else { - cit = changes->insert(++cit, Change(insert->index + insert->count, count)); - --cit; - cit->count = offset; - } - } - - for (; change != m_changes.end() && change->index + change->count < cit->index; ++change) {} - if (change == m_changes.end() || change->index > cit->index + cit->count) { - if (cit->count > 0) { - change = m_changes.insert(change, *cit); - ++change; - } - } else { - if (cit->index < change->index) { - change->count += change->index - cit->index; - change->index = cit->index; - } - - if (cit->index + cit->count > change->index + change->count) { - change->count = cit->index + cit->count - change->index; - QVector<Change>::iterator cbegin = change; - QVector<Change>::iterator cend = ++cbegin; - for (; cend != m_changes.end() && cend->index <= change->index + change->count; ++cend) { - if (cend->index + cend->count > change->index + change->count) - change->count = cend->index + cend->count - change->index; - } - if (cbegin != cend) { - change = m_changes.erase(cbegin, cend); - --change; - } - } - } - } -} - -/*! - Prints the contents of a change \a set to the \a debug stream. -*/ - -QDebug operator <<(QDebug debug, const QQmlChangeSet &set) -{ - debug.nospace() << "QQmlChangeSet("; - const QVector<QQmlChangeSet::Change> &removes = set.removes(); - for (const QQmlChangeSet::Change &remove : removes) - debug << remove; - const QVector<QQmlChangeSet::Change> &inserts = set.inserts(); - for (const QQmlChangeSet::Change &insert : inserts) - debug << insert; - const QVector<QQmlChangeSet::Change> &changes = set.changes(); - for (const QQmlChangeSet::Change &change : changes) - debug << change; - return debug.nospace() << ')'; -} - -/*! - Prints a \a change to the \a debug stream. -*/ - -QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change) -{ - return (debug.nospace() << "Change(" << change.index << ',' << change.count << ')').space(); -} - -QT_END_NAMESPACE - diff --git a/src/qml/util/qqmlchangeset_p.h b/src/qml/util/qqmlchangeset_p.h deleted file mode 100644 index 8347a3ff19..0000000000 --- a/src/qml/util/qqmlchangeset_p.h +++ /dev/null @@ -1,161 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLCHANGESET_P_H -#define QQMLCHANGESET_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qdebug.h> -#include <QtCore/qvector.h> -#include <QtQml/private/qtqmlglobal_p.h> - -QT_BEGIN_NAMESPACE - -class Q_QML_PRIVATE_EXPORT QQmlChangeSet -{ -public: - struct MoveKey - { - MoveKey() {} - MoveKey(int moveId, int offset) : moveId(moveId), offset(offset) {} - int moveId = -1; - int offset = 0; - }; - - // The storrage for Change (below). This struct is trivial, which it has to be in order to store - // it in a QV4::Heap::Base object. The Change struct doesn't add any storage fields, so it is - // safe to cast ChangeData to/from Change. - struct ChangeData - { - int index; - int count; - int moveId; - int offset; - }; - - struct Change: ChangeData - { - Change() { - index = 0; - count = 0; - moveId = -1; - offset = 0; - } - Change(int index, int count, int moveId = -1, int offset = 0) { - this->index = index; - this->count = count; - this->moveId = moveId; - this->offset = offset; - } - - bool isMove() const { return moveId >= 0; } - - MoveKey moveKey(int index) const { - return MoveKey(moveId, index - Change::index + offset); } - - int start() const { return index; } - int end() const { return index + count; } - }; - - QQmlChangeSet(); - QQmlChangeSet(const QQmlChangeSet &changeSet); - ~QQmlChangeSet(); - - QQmlChangeSet &operator =(const QQmlChangeSet &changeSet); - - const QVector<Change> &removes() const { return m_removes; } - const QVector<Change> &inserts() const { return m_inserts; } - const QVector<Change> &changes() const { return m_changes; } - - void insert(int index, int count); - void remove(int index, int count); - void move(int from, int to, int count, int moveId); - void change(int index, int count); - - void insert(const QVector<Change> &inserts); - void remove(const QVector<Change> &removes, QVector<Change> *inserts = nullptr); - void move(const QVector<Change> &removes, const QVector<Change> &inserts); - void change(const QVector<Change> &changes); - void apply(const QQmlChangeSet &changeSet); - - bool isEmpty() const { return m_removes.empty() && m_inserts.empty() && m_changes.isEmpty(); } - - void clear() - { - m_removes.clear(); - m_inserts.clear(); - m_changes.clear(); - m_difference = 0; - } - - int difference() const { return m_difference; } - -private: - void remove(QVector<Change> *removes, QVector<Change> *inserts); - void change(QVector<Change> *changes); - - QVector<Change> m_removes; - QVector<Change> m_inserts; - QVector<Change> m_changes; - int m_difference; -}; - -Q_DECLARE_TYPEINFO(QQmlChangeSet::Change, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQmlChangeSet::MoveKey, Q_PRIMITIVE_TYPE); - -inline uint qHash(const QQmlChangeSet::MoveKey &key) { return qHash(qMakePair(key.moveId, key.offset)); } -inline bool operator ==(const QQmlChangeSet::MoveKey &l, const QQmlChangeSet::MoveKey &r) { - return l.moveId == r.moveId && l.offset == r.offset; } - -Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet::Change &change); -Q_QML_PRIVATE_EXPORT QDebug operator <<(QDebug debug, const QQmlChangeSet &change); - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/util/qqmllistaccessor.cpp b/src/qml/util/qqmllistaccessor.cpp deleted file mode 100644 index 46a11e2bc2..0000000000 --- a/src/qml/util/qqmllistaccessor.cpp +++ /dev/null @@ -1,160 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmllistaccessor_p.h" - -#include <private/qqmlmetatype_p.h> - -#include <QtCore/qstringlist.h> -#include <QtCore/qdebug.h> - -// ### Remove me -#include <private/qqmlengine_p.h> - -QT_BEGIN_NAMESPACE - -QQmlListAccessor::QQmlListAccessor() -: m_type(Invalid) -{ -} - -QQmlListAccessor::~QQmlListAccessor() -{ -} - -QVariant QQmlListAccessor::list() const -{ - return d; -} - -void QQmlListAccessor::setList(const QVariant &v, QQmlEngine *engine) -{ - d = v; - - // An incoming JS array as model is treated as a variant list, so we need to - // convert it first with toVariant(). - if (d.userType() == qMetaTypeId<QJSValue>()) - d = d.value<QJSValue>().toVariant(); - - QQmlEnginePrivate *enginePrivate = engine?QQmlEnginePrivate::get(engine):nullptr; - - if (!d.isValid()) { - m_type = Invalid; - } else if (d.userType() == QVariant::StringList) { - m_type = StringList; - } else if (d.userType() == QMetaType::QVariantList) { - m_type = VariantList; - } else if (d.canConvert(QVariant::Int)) { - // Here we have to check for an upper limit, because down the line code might (well, will) - // allocate memory depending on the number of elements. The upper limit cannot be INT_MAX: - // QVector<QPointer<QQuickItem>> something; - // something.resize(count()); - // (See e.g. QQuickRepeater::regenerate()) - // This will allocate data along the lines of: - // sizeof(QPointer<QQuickItem>) * count() + QVector::headerSize - // So, doing an approximate round-down-to-nice-number, we get: - const int upperLimit = 100 * 1000 * 1000; - - int i = v.toInt(); - if (i < 0) { - qWarning("Model size of %d is less than 0", i); - m_type = Invalid; - } else if (i > upperLimit) { - qWarning("Model size of %d is bigger than the upper limit %d", i, upperLimit); - m_type = Invalid; - } else { - m_type = Integer; - } - } else if ((!enginePrivate && QQmlMetaType::isQObject(d.userType())) || - (enginePrivate && enginePrivate->isQObject(d.userType()))) { - QObject *data = enginePrivate?enginePrivate->toQObject(d):QQmlMetaType::toQObject(d); - d = QVariant::fromValue(data); - m_type = Instance; - } else if (d.userType() == qMetaTypeId<QQmlListReference>()) { - m_type = ListProperty; - } else { - m_type = Instance; - } -} - -int QQmlListAccessor::count() const -{ - switch(m_type) { - case StringList: - return qvariant_cast<QStringList>(d).count(); - case VariantList: - return qvariant_cast<QVariantList>(d).count(); - case ListProperty: - return ((const QQmlListReference *)d.constData())->count(); - case Instance: - return 1; - case Integer: - return d.toInt(); - default: - case Invalid: - return 0; - } -} - -QVariant QQmlListAccessor::at(int idx) const -{ - Q_ASSERT(idx >= 0 && idx < count()); - switch(m_type) { - case StringList: - return QVariant::fromValue(qvariant_cast<QStringList>(d).at(idx)); - case VariantList: - return qvariant_cast<QVariantList>(d).at(idx); - case ListProperty: - return QVariant::fromValue(((const QQmlListReference *)d.constData())->at(idx)); - case Instance: - return d; - case Integer: - return QVariant(idx); - default: - case Invalid: - return QVariant(); - } -} - -bool QQmlListAccessor::isValid() const -{ - return m_type != Invalid; -} - -QT_END_NAMESPACE diff --git a/src/qml/util/qqmllistaccessor_p.h b/src/qml/util/qqmllistaccessor_p.h deleted file mode 100644 index bcd079adef..0000000000 --- a/src/qml/util/qqmllistaccessor_p.h +++ /dev/null @@ -1,83 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLLISTACCESSOR_H -#define QQMLLISTACCESSOR_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/QVariant> - -QT_BEGIN_NAMESPACE - -class QQmlEngine; -class Q_AUTOTEST_EXPORT QQmlListAccessor -{ -public: - QQmlListAccessor(); - ~QQmlListAccessor(); - - QVariant list() const; - void setList(const QVariant &, QQmlEngine * = nullptr); - - bool isValid() const; - - int count() const; - QVariant at(int) const; - - enum Type { Invalid, StringList, VariantList, ListProperty, Instance, Integer }; - Type type() const { return m_type; } - -private: - Type m_type; - QVariant d; -}; - -QT_END_NAMESPACE - -#endif // QQMLLISTACCESSOR_H diff --git a/src/qml/util/qqmllistcompositor.cpp b/src/qml/util/qqmllistcompositor.cpp deleted file mode 100644 index 921e86f355..0000000000 --- a/src/qml/util/qqmllistcompositor.cpp +++ /dev/null @@ -1,1482 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qqmllistcompositor_p.h" - -#include <QtCore/qvarlengtharray.h> - -//#define QT_QML_VERIFY_MINIMAL -//#define QT_QML_VERIFY_INTEGRITY - -QT_BEGIN_NAMESPACE - -/*! - \class QQmlListCompositor - \brief The QQmlListCompositor class provides a lookup table for filtered, or re-ordered list - indexes. - \internal - - QQmlListCompositor is intended as an aid for developing proxy models. It doesn't however - directly proxy a list or model, instead a range of indexes from one or many lists can be - inserted into the compositor and then categorized and shuffled around and it will manage the - task of translating from an index in the combined space into an index in a particular list. - - Within a compositor indexes are categorized into groups where a group is a sub-set of the - total indexes referenced by the compositor, each with an address space ranging from 0 to - the number of indexes in the group - 1. Group memberships are independent of each other with - the one exception that items always retain the same order so if an index is moved within a - group, its position in other groups will change as well. - - The iterator classes encapsulate information about a specific position in a compositor group. - This includes a source list, the index of an item within that list and the groups that item - is a member of. The iterator for a specific position in a group can be retrieved with the - find() function and the addition and subtraction operators of the iterators can be used to - navigate to adjacent items in the same group. - - Items can be added to the compositor with the append() and insert() functions, group - membership can be changed with the setFlags() and clearFlags() functions, and the position - of items in the compositor can be changed with the move() function. Each of these functions - optionally returns a list of the changes made to indexes within each group which can then - be propagated to view so that it can correctly refresh its contents; e.g. 3 items - removed at index 6, and 5 items inserted at index 1. The notification changes are always - ordered from the start of the list to the end and accumulate, so if 5 items are removed at - index 4, one is skipped and then 3 move are removed, the changes returned are 5 items removed - at index 4, followed by 3 items removed at index 4. - - When the contents of a source list change, the mappings within the compositor can be updated - with the listItemsInserted(), listItemsRemoved(), listItemsMoved(), and listItemsChanged() - functions. Like the direct manipulation functions these too return a list of group indexes - affected by the change. If items are removed from a source list they are also removed from - any groups they belong to with the one exception being items belonging to the \l Cache group. - When items belonging to this group are removed the list, index, and other group membership - information are discarded but Cache membership is retained until explicitly removed. This - allows the cache index to be retained until cached resources for that item are actually - released. - - Internally the index mapping is stored as a list of Range objects, each has a list identifier, - a start index, a count, and a set of flags which represent group membership and some other - properties. The group index of a range is the sum of all preceding ranges that are members of - that group. To avoid the inefficiency of iterating over potentially all ranges when looking - for a specific index, each time a lookup is done the range and its indexes are cached and the - next lookup is done relative to this. This works out to near constant time in most relevant - use cases because successive index lookups are most frequently adjacent. The total number of - ranges is often quite small, which helps as well. If there is a need for faster random access - then a skip list like index may be an appropriate addition. - - \sa DelegateModel -*/ - -#ifdef QT_QML_VERIFY_MINIMAL -#define QT_QML_VERIFY_INTEGRITY -/* - Diagnostic to verify there are no consecutive ranges, or that the compositor contains the - most compact representation possible. - - Returns false and prints a warning if any range has a starting index equal to the end - (index + count) index of the previous range, and both ranges also have the same flags and list - property. - - If there are no consecutive ranges this will return true. -*/ - -static bool qt_verifyMinimal( - const QQmlListCompositor::iterator &begin, - const QQmlListCompositor::iterator &end) -{ - bool minimal = true; - int index = 0; - - for (const QQmlListCompositor::Range *range = begin->next; range != *end; range = range->next, ++index) { - if (range->previous->list == range->list - && range->previous->flags == (range->flags & ~QQmlListCompositor::AppendFlag) - && range->previous->end() == range->index) { - qWarning() << index << "Consecutive ranges"; - qWarning() << *range->previous; - qWarning() << *range; - minimal = false; - } - } - - return minimal; -} - -#endif - -#ifdef QT_QML_VERIFY_INTEGRITY -static bool qt_printInfo(const QQmlListCompositor &compositor) -{ - qWarning() << compositor; - return true; -} - -/* - Diagnostic to verify the integrity of a compositor. - - Per range this verifies there are no invalid range combinations, that non-append ranges have - positive non-zero counts, and that list ranges have non-negative indexes. - - Accumulatively this verifies that the cached total group counts match the sum of counts - of member ranges. -*/ - -static bool qt_verifyIntegrity( - const QQmlListCompositor::iterator &begin, - const QQmlListCompositor::iterator &end, - const QQmlListCompositor::iterator &cachedIt) -{ - bool valid = true; - - int index = 0; - QQmlListCompositor::iterator it; - for (it = begin; *it != *end; *it = it->next) { - if (it->count == 0 && !it->append()) { - qWarning() << index << "Empty non-append range"; - valid = false; - } - if (it->count < 0) { - qWarning() << index << "Negative count"; - valid = false; - } - if (it->list && it->flags != QQmlListCompositor::CacheFlag && it->index < 0) { - qWarning() << index <<"Negative index"; - valid = false; - } - if (it->previous->next != it.range) { - qWarning() << index << "broken list: it->previous->next != it.range"; - valid = false; - } - if (it->next->previous != it.range) { - qWarning() << index << "broken list: it->next->previous != it.range"; - valid = false; - } - if (*it == *cachedIt) { - for (int i = 0; i < end.groupCount; ++i) { - int groupIndex = it.index[i]; - if (cachedIt->flags & (1 << i)) - groupIndex += cachedIt.offset; - if (groupIndex != cachedIt.index[i]) { - qWarning() << index - << "invalid cached index" - << QQmlListCompositor::Group(i) - << "Expected:" - << groupIndex - << "Actual" - << cachedIt.index[i] - << cachedIt; - valid = false; - } - } - } - it.incrementIndexes(it->count); - ++index; - } - - for (int i = 0; i < end.groupCount; ++i) { - if (end.index[i] != it.index[i]) { - qWarning() << "Group" << i << "count invalid. Expected:" << end.index[i] << "Actual:" << it.index[i]; - valid = false; - } - } - return valid; -} -#endif - -#if defined(QT_QML_VERIFY_MINIMAL) -# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!(qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \ - && qt_verifyMinimal(iterator(m_ranges.next, 0, Default, m_groupCount), m_end)) \ - && qt_printInfo(*this))); -#elif defined(QT_QML_VERIFY_INTEGRITY) -# define QT_QML_VERIFY_LISTCOMPOSITOR Q_ASSERT(!(!qt_verifyIntegrity(iterator(m_ranges.next, 0, Default, m_groupCount), m_end, m_cacheIt) \ - && qt_printInfo(*this))); -#else -# define QT_QML_VERIFY_LISTCOMPOSITOR -#endif - -//#define QT_QML_TRACE_LISTCOMPOSITOR(args) qDebug() << m_end.index[1] << m_end.index[0] << Q_FUNC_INFO args; -#define QT_QML_TRACE_LISTCOMPOSITOR(args) - -QQmlListCompositor::iterator &QQmlListCompositor::iterator::operator +=(int difference) -{ - // Update all indexes to the start of the range. - decrementIndexes(offset); - - // If the iterator group isn't a member of the current range ignore the current offset. - if (!(range->flags & groupFlag)) - offset = 0; - - offset += difference; - - // Iterate backwards looking for a range with a positive offset. - while (offset <= 0 && range->previous->flags) { - range = range->previous; - if (range->flags & groupFlag) - offset += range->count; - decrementIndexes(range->count); - } - - // Iterate forwards looking for the first range which contains both the offset and the - // iterator group. - while (range->flags && (offset >= range->count || !(range->flags & groupFlag))) { - if (range->flags & groupFlag) - offset -= range->count; - incrementIndexes(range->count); - range = range->next; - } - - // Update all the indexes to inclue the remaining offset. - incrementIndexes(offset); - - return *this; -} - -QQmlListCompositor::insert_iterator &QQmlListCompositor::insert_iterator::operator +=(int difference) -{ - iterator::operator +=(difference); - - // If the previous range contains the append flag move the iterator to the tail of the previous - // range so that appended appear after the insert position. - if (offset == 0 && range->previous->append()) { - range = range->previous; - offset = range->inGroup() ? range->count : 0; - } - - return *this; -} - - -/*! - Constructs an empty list compositor. -*/ - -QQmlListCompositor::QQmlListCompositor() - : m_end(m_ranges.next, 0, Default, 2) - , m_cacheIt(m_end) - , m_groupCount(2) - , m_defaultFlags(PrependFlag | DefaultFlag) - , m_removeFlags(AppendFlag | PrependFlag | GroupMask) - , m_moveId(0) -{ -} - -/*! - Destroys a list compositor. -*/ - -QQmlListCompositor::~QQmlListCompositor() -{ - for (Range *next, *range = m_ranges.next; range != &m_ranges; range = next) { - next = range->next; - delete range; - } -} - -/*! - Inserts a range with the given source \a list, start \a index, \a count and \a flags, in front - of the existing range \a before. -*/ - -inline QQmlListCompositor::Range *QQmlListCompositor::insert( - Range *before, void *list, int index, int count, uint flags) -{ - return new Range(before, list, index, count, flags); -} - -/*! - Erases a \a range from the compositor. - - Returns a pointer to the next range in the compositor. -*/ - -inline QQmlListCompositor::Range *QQmlListCompositor::erase( - Range *range) -{ - Range *next = range->next; - next->previous = range->previous; - next->previous->next = range->next; - delete range; - return next; -} - -/*! - Sets the number (\a count) of possible groups that items may belong to in a compositor. -*/ - -void QQmlListCompositor::setGroupCount(int count) -{ - m_groupCount = count; - m_end = iterator(&m_ranges, 0, Default, m_groupCount); - m_cacheIt = m_end; -} - -/*! - Returns the number of items that belong to a \a group. -*/ - -int QQmlListCompositor::count(Group group) const -{ - return m_end.index[group]; -} - -/*! - Returns an iterator representing the item at \a index in a \a group. - - The index must be between 0 and count(group) - 1. -*/ - -QQmlListCompositor::iterator QQmlListCompositor::find(Group group, int index) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< group << index) - Q_ASSERT(index >=0 && index < count(group)); - if (m_cacheIt == m_end) { - m_cacheIt = iterator(m_ranges.next, 0, group, m_groupCount); - m_cacheIt += index; - } else { - const int offset = index - m_cacheIt.index[group]; - m_cacheIt.setGroup(group); - m_cacheIt += offset; - } - Q_ASSERT(m_cacheIt.index[group] == index); - Q_ASSERT(m_cacheIt->inGroup(group)); - QT_QML_VERIFY_LISTCOMPOSITOR - return m_cacheIt; -} - -/*! - Returns an iterator representing the item at \a index in a \a group. - - The index must be between 0 and count(group) - 1. -*/ - -QQmlListCompositor::iterator QQmlListCompositor::find(Group group, int index) const -{ - return const_cast<QQmlListCompositor *>(this)->find(group, index); -} - -/*! - Returns an iterator representing an insert position in front of the item at \a index in a - \a group. - - The iterator for an insert position can sometimes resolve to a different Range than a regular - iterator. This is because when items are inserted on a boundary between Ranges, if the first - range has the Append flag set then the items should be inserted into that range to ensure - that the append position for the existing range remains after the insert position. - - The index must be between 0 and count(group) - 1. -*/ - -QQmlListCompositor::insert_iterator QQmlListCompositor::findInsertPosition(Group group, int index) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< group << index) - Q_ASSERT(index >=0 && index <= count(group)); - insert_iterator it; - if (m_cacheIt == m_end) { - it = iterator(m_ranges.next, 0, group, m_groupCount); - it += index; - } else { - const int offset = index - m_cacheIt.index[group]; - it = m_cacheIt; - it.setGroup(group); - it += offset; - } - Q_ASSERT(it.index[group] == index); - return it; -} - -/*! - Appends a range of \a count indexes starting at \a index from a \a list into a compositor - with the given \a flags. - - If supplied the \a inserts list will be populated with the positions of the inserted items - in each group. -*/ - -void QQmlListCompositor::append( - void *list, int index, int count, uint flags, QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count << flags) - insert(m_end, list, index, count, flags, inserts); -} - -/*! - Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags - into a \a group at index \a before. - - If supplied the \a inserts list will be populated with the positions of items inserted into - each group. -*/ - -void QQmlListCompositor::insert( - Group group, int before, void *list, int index, int count, uint flags, QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< group << before << list << index << count << flags) - insert(findInsertPosition(group, before), list, index, count, flags, inserts); -} - -/*! - Inserts a range of \a count indexes starting at \a index from a \a list with the given \a flags - into a compositor at position \a before. - - If supplied the \a inserts list will be populated with the positions of items inserted into - each group. -*/ - -QQmlListCompositor::iterator QQmlListCompositor::insert( - iterator before, void *list, int index, int count, uint flags, QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< before << list << index << count << flags) - if (inserts) { - inserts->append(Insert(before, count, flags & GroupMask)); - } - if (before.offset > 0) { - // Inserting into the middle of a range. Split it two and update the iterator so it's - // positioned at the start of the second half. - *before = insert( - *before, before->list, before->index, before.offset, before->flags & ~AppendFlag)->next; - before->index += before.offset; - before->count -= before.offset; - before.offset = 0; - } - - - if (!(flags & AppendFlag) && *before != m_ranges.next - && before->previous->list == list - && before->previous->flags == flags - && (!list || before->previous->end() == index)) { - // The insert arguments represent a continuation of the previous range so increment - // its count instead of inserting a new range. - before->previous->count += count; - before.incrementIndexes(count, flags); - } else { - *before = insert(*before, list, index, count, flags); - before.offset = 0; - } - - if (!(flags & AppendFlag) && before->next != &m_ranges - && before->list == before->next->list - && before->flags == before->next->flags - && (!list || before->end() == before->next->index)) { - // The current range and the next are continuous so add their counts and delete one. - before->next->index = before->index; - before->next->count += before->count; - *before = erase(*before); - } - - m_end.incrementIndexes(count, flags); - m_cacheIt = before; - QT_QML_VERIFY_LISTCOMPOSITOR - return before; -} - -/*! - Sets the given flags \a flags on \a count items belonging to \a group starting at the position - identified by \a fromGroup and the index \a from. - - If supplied the \a inserts list will be populated with insert notifications for affected groups. -*/ - -void QQmlListCompositor::setFlags( - Group fromGroup, int from, int count, Group group, int flags, QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags) - setFlags(find(fromGroup, from), count, group, flags, inserts); -} - -/*! - Sets the given flags \a flags on \a count items belonging to \a group starting at the position - \a from. - - If supplied the \a inserts list will be populated with insert notifications for affected groups. -*/ - -void QQmlListCompositor::setFlags( - iterator from, int count, Group group, uint flags, QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags) - if (!flags || !count) - return; - - if (from != group) { - // Skip to the next full range if the start one is not a member of the target group. - from.incrementIndexes(from->count - from.offset); - from.offset = 0; - *from = from->next; - } else if (from.offset > 0) { - // If the start position is mid range split off the portion unaffected. - *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next; - from->index += from.offset; - from->count -= from.offset; - from.offset = 0; - } - - for (; count > 0; *from = from->next) { - if (from != from.group) { - // Skip ranges that are not members of the target group. - from.incrementIndexes(from->count); - continue; - } - // Find the number of items affected in the current range. - const int difference = qMin(count, from->count); - count -= difference; - - // Determine the actual changes made to the range and increment counts accordingly. - const uint insertFlags = ~from->flags & flags; - const uint setFlags = (from->flags | flags) & ~AppendFlag; - if (insertFlags && inserts) - inserts->append(Insert(from, difference, insertFlags | (from->flags & CacheFlag))); - m_end.incrementIndexes(difference, insertFlags); - from.incrementIndexes(difference, setFlags); - - if (from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || from->previous->end() == from->index) - && from->previous->flags == setFlags) { - // If the additional flags make the current range a continuation of the previous - // then move the affected items over to the previous range. - from->previous->count += difference; - from->index += difference; - from->count -= difference; - if (from->count == 0) { - // Delete the current range if it is now empty, preserving the append flag - // in the previous range. - if (from->append()) - from->previous->flags |= AppendFlag; - *from = erase(*from)->previous; - continue; - } else { - break; - } - } else if (!insertFlags) { - // No new flags, so roll onto the next range. - from.incrementIndexes(from->count - difference); - continue; - } else if (difference < from->count) { - // Create a new range with the updated flags, and remove the affected items - // from the current range. - *from = insert(*from, from->list, from->index, difference, setFlags)->next; - from->index += difference; - from->count -= difference; - } else { - // The whole range is affected so simply update the flags. - from->flags |= flags; - continue; - } - from.incrementIndexes(from->count); - } - - if (from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || from->previous->end() == from->index) - && from->previous->flags == (from->flags & ~AppendFlag)) { - // If the following range is now a continuation, merge it with its previous range. - from.offset = from->previous->count; - from->previous->count += from->count; - from->previous->flags = from->flags; - *from = erase(*from)->previous; - } - m_cacheIt = from; - QT_QML_VERIFY_LISTCOMPOSITOR -} - -/*! - Clears the given flags \a flags on \a count items belonging to \a group starting at the position - \a from. - - If supplied the \a removes list will be populated with remove notifications for affected groups. -*/ - -void QQmlListCompositor::clearFlags( - Group fromGroup, int from, int count, Group group, uint flags, QVector<Remove> *removes) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << count << group << flags) - clearFlags(find(fromGroup, from), count, group, flags, removes); -} - -/*! - Clears the given flags \a flags on \a count items belonging to \a group starting at the position - identified by \a fromGroup and the index \a from. - - If supplied the \a removes list will be populated with remove notifications for affected groups. -*/ - -void QQmlListCompositor::clearFlags( - iterator from, int count, Group group, uint flags, QVector<Remove> *removes) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< from << count << flags) - if (!flags || !count) - return; - - const bool clearCache = flags & CacheFlag; - - if (from != group) { - // Skip to the next full range if the start one is not a member of the target group. - from.incrementIndexes(from->count - from.offset); - from.offset = 0; - *from = from->next; - } else if (from.offset > 0) { - // If the start position is mid range split off the portion unaffected. - *from = insert(*from, from->list, from->index, from.offset, from->flags & ~AppendFlag)->next; - from->index += from.offset; - from->count -= from.offset; - from.offset = 0; - } - - for (; count > 0; *from = from->next) { - if (from != group) { - // Skip ranges that are not members of the target group. - from.incrementIndexes(from->count); - continue; - } - // Find the number of items affected in the current range. - const int difference = qMin(count, from->count); - count -= difference; - - - // Determine the actual changes made to the range and decrement counts accordingly. - const uint removeFlags = from->flags & flags & ~(AppendFlag | PrependFlag); - const uint clearedFlags = from->flags & ~(flags | AppendFlag | UnresolvedFlag); - if (removeFlags && removes) { - const int maskedFlags = clearCache - ? (removeFlags & ~CacheFlag) - : (removeFlags | (from->flags & CacheFlag)); - if (maskedFlags) - removes->append(Remove(from, difference, maskedFlags)); - } - m_end.decrementIndexes(difference, removeFlags); - from.incrementIndexes(difference, clearedFlags); - - if (from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || clearedFlags == CacheFlag || from->previous->end() == from->index) - && from->previous->flags == clearedFlags) { - // If the removed flags make the current range a continuation of the previous - // then move the affected items over to the previous range. - from->previous->count += difference; - from->index += difference; - from->count -= difference; - if (from->count == 0) { - // Delete the current range if it is now empty, preserving the append flag - if (from->append()) - from->previous->flags |= AppendFlag; - *from = erase(*from)->previous; - } else { - from.incrementIndexes(from->count); - } - } else if (difference < from->count) { - // Create a new range with the reduced flags, and remove the affected items from - // the current range. - if (clearedFlags) - *from = insert(*from, from->list, from->index, difference, clearedFlags)->next; - from->index += difference; - from->count -= difference; - from.incrementIndexes(from->count); - } else if (clearedFlags) { - // The whole range is affected so simply update the flags. - from->flags &= ~flags; - } else { - // All flags have been removed from the range so remove it. - *from = erase(*from)->previous; - } - } - - if (*from != &m_ranges && from->previous != &m_ranges - && from->previous->list == from->list - && (!from->list || from->previous->end() == from->index) - && from->previous->flags == (from->flags & ~AppendFlag)) { - // If the following range is now a continuation, merge it with its previous range. - from.offset = from->previous->count; - from->previous->count += from->count; - from->previous->flags = from->flags; - *from = erase(*from)->previous; - } - m_cacheIt = from; - QT_QML_VERIFY_LISTCOMPOSITOR -} - -bool QQmlListCompositor::verifyMoveTo( - Group fromGroup, int from, Group toGroup, int to, int count, Group group) const -{ - if (group != toGroup) { - // determine how many items from the destination group intersect with the source group. - iterator fromIt = find(fromGroup, from); - - int intersectingCount = 0; - - for (; count > 0; *fromIt = fromIt->next) { - if (*fromIt == &m_ranges) - return false; - if (!fromIt->inGroup(group)) - continue; - if (fromIt->inGroup(toGroup)) - intersectingCount += qMin(count, fromIt->count - fromIt.offset); - count -= fromIt->count - fromIt.offset; - fromIt.offset = 0; - } - count = intersectingCount; - } - - return to >= 0 && to + count <= m_end.index[toGroup]; -} - -/*! - \internal - - Moves \a count items belonging to \a moveGroup from the index \a from in \a fromGroup - to the index \a to in \a toGroup. - - If \a removes and \a inserts are not null they will be populated with per group notifications - of the items moved. - */ - -void QQmlListCompositor::move( - Group fromGroup, - int from, - Group toGroup, - int to, - int count, - Group moveGroup, - QVector<Remove> *removes, - QVector<Insert> *inserts) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< fromGroup << from << toGroup << to << count) - Q_ASSERT(count > 0); - Q_ASSERT(from >=0); - Q_ASSERT(verifyMoveTo(fromGroup, from, toGroup, to, count, moveGroup)); - - // Find the position of the first item to move. - iterator fromIt = find(fromGroup, from); - - if (fromIt != moveGroup) { - // If the range at the from index doesn't contain items from the move group; skip - // to the next range. - fromIt.incrementIndexes(fromIt->count - fromIt.offset); - fromIt.offset = 0; - *fromIt = fromIt->next; - } else if (fromIt.offset > 0) { - // If the range at the from index contains items from the move group and the index isn't - // at the start of the range; split the range at the index and move the iterator to start - // of the second range. - *fromIt = insert( - *fromIt, fromIt->list, fromIt->index, fromIt.offset, fromIt->flags & ~AppendFlag)->next; - fromIt->index += fromIt.offset; - fromIt->count -= fromIt.offset; - fromIt.offset = 0; - } - - // Remove count items belonging to the move group from the list. - Range movedFlags; - for (int moveId = m_moveId; count > 0;) { - if (fromIt != moveGroup) { - // Skip ranges not containing items from the move group. - fromIt.incrementIndexes(fromIt->count); - *fromIt = fromIt->next; - continue; - } - int difference = qMin(count, fromIt->count); - - // Create a new static range containing the moved items from an existing range. - new Range( - &movedFlags, - fromIt->list, - fromIt->index, - difference, - fromIt->flags & ~(PrependFlag | AppendFlag)); - // Remove moved items from the count, the existing range, and a remove notification. - if (removes) - removes->append(Remove(fromIt, difference, fromIt->flags, ++moveId)); - count -= difference; - fromIt->count -= difference; - - // If the existing range contains the prepend flag replace the removed items with - // a placeholder range for new items inserted into the source model. - int removeIndex = fromIt->index; - if (fromIt->prepend() - && fromIt->previous != &m_ranges - && fromIt->previous->flags == PrependFlag - && fromIt->previous->list == fromIt->list - && fromIt->previous->end() == fromIt->index) { - // Grow the previous range instead of creating a new one if possible. - fromIt->previous->count += difference; - } else if (fromIt->prepend()) { - *fromIt = insert(*fromIt, fromIt->list, removeIndex, difference, PrependFlag)->next; - } - fromIt->index += difference; - - if (fromIt->count == 0) { - // If the existing range has no items remaining; remove it from the list. - if (fromIt->append()) - fromIt->previous->flags |= AppendFlag; - *fromIt = erase(*fromIt); - - // If the ranges before and after the removed range can be joined, do so. - if (*fromIt != m_ranges.next && fromIt->flags == PrependFlag - && fromIt->previous != &m_ranges - && fromIt->previous->flags == PrependFlag - && fromIt->previous->list == fromIt->list - && fromIt->previous->end() == fromIt->index) { - fromIt.incrementIndexes(fromIt->count); - fromIt->previous->count += fromIt->count; - *fromIt = erase(*fromIt); - } - } else if (count > 0) { - *fromIt = fromIt->next; - } - } - - // Try and join the range following the removed items to the range preceding it. - if (*fromIt != m_ranges.next - && *fromIt != &m_ranges - && fromIt->previous->list == fromIt->list - && (!fromIt->list || fromIt->previous->end() == fromIt->index) - && fromIt->previous->flags == (fromIt->flags & ~AppendFlag)) { - if (fromIt == fromIt.group) - fromIt.offset = fromIt->previous->count; - fromIt.offset = fromIt->previous->count; - fromIt->previous->count += fromIt->count; - fromIt->previous->flags = fromIt->flags; - *fromIt = erase(*fromIt)->previous; - } - - // Find the destination position of the move. - insert_iterator toIt = fromIt; - toIt.setGroup(toGroup); - - const int difference = to - toIt.index[toGroup]; - toIt += difference; - - // If the insert position is part way through a range; split it and move the iterator to the - // start of the second range. - if (toIt.offset > 0) { - *toIt = insert(*toIt, toIt->list, toIt->index, toIt.offset, toIt->flags & ~AppendFlag)->next; - toIt->index += toIt.offset; - toIt->count -= toIt.offset; - toIt.offset = 0; - } - - // Insert the moved ranges before the insert iterator, growing the previous range if that - // is an option. - for (Range *range = movedFlags.previous; range != &movedFlags; range = range->previous) { - if (*toIt != &m_ranges - && range->list == toIt->list - && (!range->list || range->end() == toIt->index) - && range->flags == (toIt->flags & ~AppendFlag)) { - toIt->index -= range->count; - toIt->count += range->count; - } else { - *toIt = insert(*toIt, range->list, range->index, range->count, range->flags); - } - } - - // Try and join the range after the inserted ranges to the last range inserted. - if (*toIt != m_ranges.next - && toIt->previous->list == toIt->list - && (!toIt->list || (toIt->previous->end() == toIt->index && toIt->previous->flags == (toIt->flags & ~AppendFlag)))) { - toIt.offset = toIt->previous->count; - toIt->previous->count += toIt->count; - toIt->previous->flags = toIt->flags; - *toIt = erase(*toIt)->previous; - } - // Create insert notification for the ranges moved. - Insert insert(toIt, 0, 0, 0); - for (Range *next, *range = movedFlags.next; range != &movedFlags; range = next) { - insert.count = range->count; - insert.flags = range->flags; - if (inserts) { - insert.moveId = ++m_moveId; - inserts->append(insert); - } - for (int i = 0; i < m_groupCount; ++i) { - if (insert.inGroup(i)) - insert.index[i] += range->count; - } - - next = range->next; - delete range; - } - - m_cacheIt = toIt; - - QT_QML_VERIFY_LISTCOMPOSITOR -} - -/*! - Clears the contents of a compositor. -*/ - -void QQmlListCompositor::clear() -{ - QT_QML_TRACE_LISTCOMPOSITOR("") - for (Range *range = m_ranges.next; range != &m_ranges; range = erase(range)) {} - m_end = iterator(m_ranges.next, 0, Default, m_groupCount); - m_cacheIt = m_end; -} - -void QQmlListCompositor::listItemsInserted( - QVector<Insert> *translatedInsertions, - void *list, - const QVector<QQmlChangeSet::Change> &insertions, - const QVector<MovedFlags> *movedFlags) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << insertions) - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it->list != list || it->flags == CacheFlag) { - // Skip ranges that don't reference list. - it.incrementIndexes(it->count); - continue; - } else if (it->flags & MovedFlag) { - // Skip ranges that were already moved in listItemsRemoved. - it->flags &= ~MovedFlag; - it.incrementIndexes(it->count); - continue; - } - for (const QQmlChangeSet::Change &insertion : insertions) { - int offset = insertion.index - it->index; - if ((offset > 0 && offset < it->count) - || (offset == 0 && it->prepend()) - || (offset == it->count && it->append())) { - // The insert index is within the current range. - if (it->prepend()) { - // The range has the prepend flag set so we insert new items into the range. - uint flags = m_defaultFlags; - if (insertion.isMove()) { - // If the insert was part of a move replace the default flags with - // the flags from the source range. - for (QVector<MovedFlags>::const_iterator move = movedFlags->begin(); - move != movedFlags->end(); - ++move) { - if (move->moveId == insertion.moveId) { - flags = move->flags; - break; - } - } - } - if (flags & ~(AppendFlag | PrependFlag)) { - // If any items are added to groups append an insert notification. - Insert translatedInsert(it, insertion.count, flags, insertion.moveId); - for (int i = 0; i < m_groupCount; ++i) { - if (it->inGroup(i)) - translatedInsert.index[i] += offset; - } - translatedInsertions->append(translatedInsert); - } - if ((it->flags & ~AppendFlag) == flags) { - // Accumulate items on the current range it its flags are the same as - // the insert flags. - it->count += insertion.count; - } else if (offset == 0 - && it->previous != &m_ranges - && it->previous->list == list - && it->previous->end() == insertion.index - && it->previous->flags == flags) { - // Attempt to append to the previous range if the insert position is at - // the start of the current range. - it->previous->count += insertion.count; - it->index += insertion.count; - it.incrementIndexes(insertion.count); - } else { - if (offset > 0) { - // Divide the current range at the insert position. - it.incrementIndexes(offset); - *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; - } - // Insert a new range, and increment the start index of the current range. - *it = insert(*it, it->list, insertion.index, insertion.count, flags)->next; - it.incrementIndexes(insertion.count, flags); - it->index += offset + insertion.count; - it->count -= offset; - } - m_end.incrementIndexes(insertion.count, flags); - } else { - // The range doesn't have the prepend flag set so split the range into parts; - // one before the insert position and one after the last inserted item. - if (offset > 0) { - *it = insert(*it, it->list, it->index, offset, it->flags)->next; - it->index += offset; - it->count -= offset; - } - it->index += insertion.count; - } - } else if (offset <= 0) { - // The insert position was before the current range so increment the start index. - it->index += insertion.count; - } - } - it.incrementIndexes(it->count); - } - m_cacheIt = m_end; - QT_QML_VERIFY_LISTCOMPOSITOR -} - -/*! - Updates the contents of a compositor when \a count items are inserted into a \a list at - \a index. - - This corrects the indexes of each range for that list in the compositor, splitting the range - in two if the insert index is in the middle of that range. If a range at the insert position - has the Prepend flag set then a new range will be inserted at that position with the groups - specified in defaultGroups(). If the insert index corresponds to the end of a range with - the Append flag set a new range will be inserted before the end of the append range. - - The \a translatedInsertions list is populated with insert notifications for affected - groups. -*/ - -void QQmlListCompositor::listItemsInserted( - void *list, int index, int count, QVector<Insert> *translatedInsertions) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) - Q_ASSERT(count > 0); - - QVector<QQmlChangeSet::Change> insertions; - insertions.append(QQmlChangeSet::Change(index, count)); - - listItemsInserted(translatedInsertions, list, insertions); -} - -void QQmlListCompositor::listItemsRemoved( - QVector<Remove> *translatedRemovals, - void *list, - QVector<QQmlChangeSet::Change> *removals, - QVector<QQmlChangeSet::Change> *insertions, - QVector<MovedFlags> *movedFlags) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << *removals) - - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it->list != list || it->flags == CacheFlag) { - // Skip ranges that don't reference list. - it.incrementIndexes(it->count); - continue; - } - bool removed = false; - for (QVector<QQmlChangeSet::Change>::iterator removal = removals->begin(); - !removed && removal != removals->end(); - ++removal) { - int relativeIndex = removal->index - it->index; - int itemsRemoved = removal->count; - if (relativeIndex + removal->count > 0 && relativeIndex < it->count) { - // If the current range intersects the remove; remove the intersecting items. - const int offset = qMax(0, relativeIndex); - int removeCount = qMin(it->count, relativeIndex + removal->count) - offset; - it->count -= removeCount; - int removeFlags = it->flags & m_removeFlags; - Remove translatedRemoval(it, removeCount, it->flags); - for (int i = 0; i < m_groupCount; ++i) { - if (it->inGroup(i)) - translatedRemoval.index[i] += offset; - } - if (removal->isMove()) { - // If the removal was part of a move find the corresponding insert. - QVector<QQmlChangeSet::Change>::iterator insertion = insertions->begin(); - for (; insertion != insertions->end() && insertion->moveId != removal->moveId; - ++insertion) {} - Q_ASSERT(insertion != insertions->end()); - Q_ASSERT(insertion->count == removal->count); - - if (relativeIndex < 0) { - // If the remove started before the current range, split it and the - // corresponding insert so we're only working with intersecting part. - int splitMoveId = ++m_moveId; - removal = removals->insert(removal, QQmlChangeSet::Change( - removal->index, -relativeIndex, splitMoveId)); - ++removal; - removal->count -= -relativeIndex; - insertion = insertions->insert(insertion, QQmlChangeSet::Change( - insertion->index, -relativeIndex, splitMoveId)); - ++insertion; - insertion->index += -relativeIndex; - insertion->count -= -relativeIndex; - } - - if (it->prepend()) { - // If the current range has the prepend flag preserve its flags to transfer - // to its new location. - removeFlags |= it->flags & CacheFlag; - translatedRemoval.moveId = ++m_moveId; - movedFlags->append(MovedFlags(m_moveId, it->flags & ~AppendFlag)); - - if (removeCount < removal->count) { - // If the remove doesn't encompass all of the current range, - // split it and the corresponding insert. - removal = removals->insert(removal, QQmlChangeSet::Change( - removal->index, removeCount, translatedRemoval.moveId)); - ++removal; - insertion = insertions->insert(insertion, QQmlChangeSet::Change( - insertion->index, removeCount, translatedRemoval.moveId)); - ++insertion; - - removal->count -= removeCount; - insertion->index += removeCount; - insertion->count -= removeCount; - } else { - // If there's no need to split the move simply replace its moveId - // with that of the translated move. - removal->moveId = translatedRemoval.moveId; - insertion->moveId = translatedRemoval.moveId; - } - } else { - // If the current range doesn't have prepend flags then insert a new range - // with list indexes from the corresponding insert location. The MoveFlag - // is to notify listItemsInserted that it can skip evaluation of that range. - if (offset > 0) { - *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; - it->index += offset; - it->count -= offset; - it.incrementIndexes(offset); - } - if (it->previous != &m_ranges - && it->previous->list == it->list - && it->end() == insertion->index - && it->previous->flags == (it->flags | MovedFlag)) { - it->previous->count += removeCount; - } else { - *it = insert(*it, it->list, insertion->index, removeCount, it->flags | MovedFlag)->next; - } - // Clear the changed flags as the item hasn't been removed. - translatedRemoval.flags = 0; - removeFlags = 0; - } - } else if (it->inCache()) { - // If not moving and the current range has the cache flag, insert a new range - // with just the cache flag set to retain caching information for the removed - // range. - if (offset > 0) { - *it = insert(*it, it->list, it->index, offset, it->flags & ~AppendFlag)->next; - it->index += offset; - it->count -= offset; - it.incrementIndexes(offset); - } - if (it->previous != &m_ranges - && it->previous->list == it->list - && it->previous->flags == CacheFlag) { - it->previous->count += removeCount; - } else { - *it = insert(*it, it->list, -1, removeCount, CacheFlag)->next; - } - it.index[Cache] += removeCount; - } - if (removeFlags & GroupMask) - translatedRemovals->append(translatedRemoval); - m_end.decrementIndexes(removeCount, removeFlags); - if (it->count == 0 && !it->append()) { - // Erase empty non-append ranges. - *it = erase(*it)->previous; - removed = true; - } else if (relativeIndex <= 0) { - // If the remove started before the current range move the start index of - // the range to the remove index. - it->index = removal->index; - } - } else if (relativeIndex < 0) { - // If the remove was before the current range decrement the start index by the - // number of items removed. - it->index -= itemsRemoved; - - if (it->previous != &m_ranges - && it->previous->list == it->list - && it->previous->end() == it->index - && it->previous->flags == (it->flags & ~AppendFlag)) { - // Compress ranges made continuous by the removal of separating ranges. - it.decrementIndexes(it->previous->count); - it->previous->count += it->count; - it->previous->flags = it->flags; - *it = erase(*it)->previous; - } - } - } - if (it->flags == CacheFlag && it->next->flags == CacheFlag && it->next->list == it->list) { - // Compress consecutive cache only ranges. - it.index[Cache] += it->next->count; - it->count += it->next->count; - erase(it->next); - } else if (!removed) { - it.incrementIndexes(it->count); - } - } - m_cacheIt = m_end; - QT_QML_VERIFY_LISTCOMPOSITOR -} - - -/*! - Updates the contents of a compositor when \a count items are removed from a \a list at - \a index. - - Ranges that intersect the specified range are removed from the compositor and the indexes of - ranges after index + count are updated. - - The \a translatedRemovals list is populated with remove notifications for the affected - groups. -*/ - - -void QQmlListCompositor::listItemsRemoved( - void *list, int index, int count, QVector<Remove> *translatedRemovals) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) - Q_ASSERT(count >= 0); - - QVector<QQmlChangeSet::Change> removals; - removals.append(QQmlChangeSet::Change(index, count)); - listItemsRemoved(translatedRemovals, list, &removals, nullptr, nullptr); -} - -/*! - Updates the contents of a compositor when \a count items are removed from a \a list at - \a index. - - Ranges that intersect the specified range are removed from the compositor and the indexes of - ranges after index + count are updated. - - The \a translatedRemovals and translatedInserts lists are populated with move - notifications for the affected groups. -*/ - -void QQmlListCompositor::listItemsMoved( - void *list, - int from, - int to, - int count, - QVector<Remove> *translatedRemovals, - QVector<Insert> *translatedInsertions) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << from << to << count) - Q_ASSERT(count >= 0); - - QVector<QQmlChangeSet::Change> removals; - QVector<QQmlChangeSet::Change> insertions; - QVector<MovedFlags> movedFlags; - removals.append(QQmlChangeSet::Change(from, count, 0)); - insertions.append(QQmlChangeSet::Change(to, count, 0)); - - listItemsRemoved(translatedRemovals, list, &removals, &insertions, &movedFlags); - listItemsInserted(translatedInsertions, list, insertions, &movedFlags); -} - -void QQmlListCompositor::listItemsChanged( - QVector<Change> *translatedChanges, - void *list, - const QVector<QQmlChangeSet::Change> &changes) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << changes) - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it->list != list || it->flags == CacheFlag) { - it.incrementIndexes(it->count); - continue; - } else if (!it->inGroup()) { - continue; - } - for (const QQmlChangeSet::Change &change : changes) { - const int offset = change.index - it->index; - if (offset + change.count > 0 && offset < it->count) { - const int changeOffset = qMax(0, offset); - const int changeCount = qMin(it->count, offset + change.count) - changeOffset; - - Change translatedChange(it, changeCount, it->flags); - for (int i = 0; i < m_groupCount; ++i) { - if (it->inGroup(i)) - translatedChange.index[i] += changeOffset; - } - translatedChanges->append(translatedChange); - } - } - it.incrementIndexes(it->count); - } -} - - -/*! - Translates the positions of \a count changed items at \a index in a \a list. - - The \a translatedChanges list is populated with change notifications for the - affected groups. -*/ - -void QQmlListCompositor::listItemsChanged( - void *list, int index, int count, QVector<Change> *translatedChanges) -{ - QT_QML_TRACE_LISTCOMPOSITOR(<< list << index << count) - Q_ASSERT(count >= 0); - QVector<QQmlChangeSet::Change> changes; - changes.append(QQmlChangeSet::Change(index, count)); - listItemsChanged(translatedChanges, list, changes); -} - -void QQmlListCompositor::transition( - Group from, - Group to, - QVector<QQmlChangeSet::Change> *removes, - QVector<QQmlChangeSet::Change> *inserts) -{ - int removeCount = 0; - for (iterator it(m_ranges.next, 0, Default, m_groupCount); *it != &m_ranges; *it = it->next) { - if (it == from && it != to) { - removes->append(QQmlChangeSet::Change(it.index[from]- removeCount, it->count)); - removeCount += it->count; - } else if (it != from && it == to) { - inserts->append(QQmlChangeSet::Change(it.index[to], it->count)); - } - it.incrementIndexes(it->count); - } -} - -/*! - \internal - Writes the name of \a group to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::Group &group) -{ - switch (group) { - case QQmlListCompositor::Cache: return debug << "Cache"; - case QQmlListCompositor::Default: return debug << "Default"; - default: return (debug.nospace() << "Group" << int(group)).space(); - } - -} - -/*! - \internal - Writes the contents of \a range to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::Range &range) -{ - (debug.nospace() - << "Range(" - << range.list) << ' ' - << range.index << ' ' - << range.count << ' ' - << (range.isUnresolved() ? 'U' : '0') - << (range.append() ? 'A' : '0') - << (range.prepend() ? 'P' : '0'); - for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i) - debug << (range.inGroup(i) ? '1' : '0'); - return (debug - << (range.inGroup(QQmlListCompositor::Default) ? 'D' : '0') - << (range.inGroup(QQmlListCompositor::Cache) ? 'C' : '0')); -} - -static void qt_print_indexes(QDebug &debug, int count, const int *indexes) -{ - for (int i = count - 1; i >= 0; --i) - debug << indexes[i]; -} - -/*! - \internal - Writes the contents of \a it to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::iterator &it) -{ - (debug.nospace() << "iterator(" << it.group).space() << "offset:" << it.offset; - qt_print_indexes(debug, it.groupCount, it.index); - return ((debug << **it).nospace() << ')').space(); -} - -static QDebug qt_print_change(QDebug debug, const char *name, const QQmlListCompositor::Change &change) -{ - debug.nospace() << name << '(' << change.moveId << ' ' << change.count << ' '; - for (int i = QQmlListCompositor::MaximumGroupCount - 1; i >= 2; --i) - debug << (change.inGroup(i) ? '1' : '0'); - debug << (change.inGroup(QQmlListCompositor::Default) ? 'D' : '0') - << (change.inGroup(QQmlListCompositor::Cache) ? 'C' : '0'); - int i = QQmlListCompositor::MaximumGroupCount - 1; - for (; i >= 0 && !change.inGroup(i); --i) {} - for (; i >= 0; --i) - debug << ' ' << change.index[i]; - return (debug << ')').maybeSpace(); -} - -/*! - \internal - Writes the contents of \a change to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::Change &change) -{ - return qt_print_change(debug, "Change", change); -} - -/*! - \internal - Writes the contents of \a remove to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::Remove &remove) -{ - return qt_print_change(debug, "Remove", remove); -} - -/*! - \internal - Writes the contents of \a insert to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor::Insert &insert) -{ - return qt_print_change(debug, "Insert", insert); -} - -/*! - \internal - Writes the contents of \a list to \a debug. -*/ - -QDebug operator <<(QDebug debug, const QQmlListCompositor &list) -{ - int indexes[QQmlListCompositor::MaximumGroupCount]; - for (int i = 0; i < QQmlListCompositor::MaximumGroupCount; ++i) - indexes[i] = 0; - debug.nospace() << "QQmlListCompositor("; - qt_print_indexes(debug, list.m_groupCount, list.m_end.index); - for (QQmlListCompositor::Range *range = list.m_ranges.next; range != &list.m_ranges; range = range->next) { - (debug << '\n').space(); - qt_print_indexes(debug, list.m_groupCount, indexes); - debug << ' ' << *range; - - for (int i = 0; i < list.m_groupCount; ++i) { - if (range->inGroup(i)) - indexes[i] += range->count; - } - } - return (debug << ')').maybeSpace(); -} - -QT_END_NAMESPACE diff --git a/src/qml/util/qqmllistcompositor_p.h b/src/qml/util/qqmllistcompositor_p.h deleted file mode 100644 index 172040559c..0000000000 --- a/src/qml/util/qqmllistcompositor_p.h +++ /dev/null @@ -1,372 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the QtQml 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 The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/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 3 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL3 included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 3 requirements -** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 2.0 or (at your option) the GNU General -** Public license version 3 or any later version approved by the KDE Free -** Qt Foundation. The licenses are as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-2.0.html and -** https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QQMLLISTCOMPOSITOR_P_H -#define QQMLLISTCOMPOSITOR_P_H - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <QtCore/qglobal.h> -#include <QtCore/qvector.h> - -#include <private/qqmlchangeset_p.h> - -#include <QtCore/qdebug.h> - -QT_BEGIN_NAMESPACE - -class Q_AUTOTEST_EXPORT QQmlListCompositor -{ -public: - enum { MinimumGroupCount = 3, MaximumGroupCount = 11 }; - - enum Group - { - Cache = 0, - Default = 1, - Persisted = 2 - }; - - enum Flag - { - CacheFlag = 1 << Cache, - DefaultFlag = 1 << Default, - PersistedFlag = 1 << Persisted, - PrependFlag = 0x10000000, - AppendFlag = 0x20000000, - UnresolvedFlag = 0x40000000, - MovedFlag = 0x80000000, - GroupMask = ~(PrependFlag | AppendFlag | UnresolvedFlag | MovedFlag | CacheFlag) - }; - - class Range - { - public: - Range() : next(this), previous(this) {} - Range(Range *next, void *list, int index, int count, uint flags) - : next(next), previous(next->previous), list(list), index(index), count(count), flags(flags) { - next->previous = this; previous->next = this; } - - Range *next; - Range *previous; - void *list = nullptr; - int index = 0; - int count = 0; - uint flags = 0; - - inline int start() const { return index; } - inline int end() const { return index + count; } - - inline int groups() const { return flags & GroupMask; } - - inline bool inGroup() const { return flags & GroupMask; } - inline bool inCache() const { return flags & CacheFlag; } - inline bool inGroup(int group) const { return flags & (1 << group); } - inline bool isUnresolved() const { return flags & UnresolvedFlag; } - - inline bool prepend() const { return flags & PrependFlag; } - inline bool append() const { return flags & AppendFlag; } - }; - - class Q_AUTOTEST_EXPORT iterator - { - public: - inline iterator(); - inline iterator(const iterator &it); - inline iterator(Range *range, int offset, Group group, int groupCount); - inline ~iterator() {} - - bool operator ==(const iterator &it) const { return range == it.range && offset == it.offset; } - bool operator !=(const iterator &it) const { return range != it.range || offset != it.offset; } - - bool operator ==(Group group) const { return range->flags & (1 << group); } - bool operator !=(Group group) const { return !(range->flags & (1 << group)); } - - Range *&operator *() { return range; } - Range * const &operator *() const { return range; } - Range *operator ->() { return range; } - const Range *operator ->() const { return range; } - - iterator &operator +=(int difference); - - template<typename T> T *list() const { return static_cast<T *>(range->list); } - int modelIndex() const { return range->index + offset; } - - void incrementIndexes(int difference) { incrementIndexes(difference, range->flags); } - void decrementIndexes(int difference) { decrementIndexes(difference, range->flags); } - - inline void incrementIndexes(int difference, uint flags); - inline void decrementIndexes(int difference, uint flags); - - void setGroup(Group g) { group = g; groupFlag = 1 << g; } - - Range *range = nullptr; - int offset = 0; - Group group = Default; - int groupFlag; - int groupCount = 0; - union { - struct { - int cacheIndex; - }; - int index[MaximumGroupCount]; - }; - }; - - class Q_AUTOTEST_EXPORT insert_iterator : public iterator - { - public: - inline insert_iterator() {} - inline insert_iterator(const iterator &it) : iterator(it) {} - inline insert_iterator(Range *, int, Group, int); - inline ~insert_iterator() {} - - insert_iterator &operator +=(int difference); - }; - - struct Change - { - inline Change() {} - inline Change(const iterator &it, int count, uint flags, int moveId = -1); - int count; - uint flags; - int moveId; - union { - struct { - int cacheIndex; - }; - int index[MaximumGroupCount]; - }; - - inline bool isMove() const { return moveId >= 0; } - inline bool inCache() const { return flags & CacheFlag; } - inline bool inGroup() const { return flags & GroupMask; } - inline bool inGroup(int group) const { return flags & (CacheFlag << group); } - - inline int groups() const { return flags & GroupMask; } - }; - - struct Insert : public Change - { - Insert() {} - Insert(const iterator &it, int count, uint flags, int moveId = -1) - : Change(it, count, flags, moveId) {} - }; - - struct Remove : public Change - { - Remove() {} - Remove(const iterator &it, int count, uint flags, int moveId = -1) - : Change(it, count, flags, moveId) {} - }; - - QQmlListCompositor(); - ~QQmlListCompositor(); - - int defaultGroups() const { return m_defaultFlags & ~PrependFlag; } - void setDefaultGroups(int groups) { m_defaultFlags = groups | PrependFlag; } - void setDefaultGroup(Group group) { m_defaultFlags |= (1 << group); } - void clearDefaultGroup(Group group) { m_defaultFlags &= ~(1 << group); } - void setRemoveGroups(int groups) { m_removeFlags = PrependFlag | AppendFlag | groups; } - void setGroupCount(int count); - - int count(Group group) const; - iterator find(Group group, int index); - iterator find(Group group, int index) const; - insert_iterator findInsertPosition(Group group, int index); - - const iterator &end() { return m_end; } - - void append(void *list, int index, int count, uint flags, QVector<Insert> *inserts = nullptr); - void insert(Group group, int before, void *list, int index, int count, uint flags, QVector<Insert> *inserts = nullptr); - iterator insert(iterator before, void *list, int index, int count, uint flags, QVector<Insert> *inserts = nullptr); - - void setFlags(Group fromGroup, int from, int count, Group group, int flags, QVector<Insert> *inserts = nullptr); - void setFlags(iterator from, int count, Group group, uint flags, QVector<Insert> *inserts = nullptr); - void setFlags(Group fromGroup, int from, int count, uint flags, QVector<Insert> *inserts = nullptr) { - setFlags(fromGroup, from, count, fromGroup, flags, inserts); } - void setFlags(const iterator from, int count, uint flags, QVector<Insert> *inserts = nullptr) { - setFlags(from, count, from.group, flags, inserts); } - - void clearFlags(Group fromGroup, int from, int count, Group group, uint flags, QVector<Remove> *removals = nullptr); - void clearFlags(iterator from, int count, Group group, uint flags, QVector<Remove> *removals = nullptr); - void clearFlags(Group fromGroup, int from, int count, uint flags, QVector<Remove> *removals = nullptr) { - clearFlags(fromGroup, from, count, fromGroup, flags, removals); } - void clearFlags(const iterator &from, int count, uint flags, QVector<Remove> *removals = nullptr) { - clearFlags(from, count, from.group, flags, removals); } - - bool verifyMoveTo(Group fromGroup, int from, Group toGroup, int to, int count, Group group) const; - - void move( - Group fromGroup, - int from, - Group toGroup, - int to, - int count, - Group group, - QVector<Remove> *removals = nullptr, - QVector<Insert> *inserts = nullptr); - void clear(); - - void listItemsInserted(void *list, int index, int count, QVector<Insert> *inserts); - void listItemsRemoved(void *list, int index, int count, QVector<Remove> *removals); - void listItemsMoved(void *list, int from, int to, int count, QVector<Remove> *removals, QVector<Insert> *inserts); - void listItemsChanged(void *list, int index, int count, QVector<Change> *changes); - - void transition( - Group from, - Group to, - QVector<QQmlChangeSet::Change> *removes, - QVector<QQmlChangeSet::Change> *inserts); - -private: - Range m_ranges; - iterator m_end; - iterator m_cacheIt; - int m_groupCount; - int m_defaultFlags; - int m_removeFlags; - int m_moveId; - - inline Range *insert(Range *before, void *list, int index, int count, uint flags); - inline Range *erase(Range *range); - - struct MovedFlags - { - MovedFlags() {} - MovedFlags(int moveId, uint flags) : moveId(moveId), flags(flags) {} - - int moveId; - uint flags; - }; - - void listItemsRemoved( - QVector<Remove> *translatedRemovals, - void *list, - QVector<QQmlChangeSet::Change> *removals, - QVector<QQmlChangeSet::Change> *insertions = nullptr, - QVector<MovedFlags> *movedFlags = nullptr); - void listItemsInserted( - QVector<Insert> *translatedInsertions, - void *list, - const QVector<QQmlChangeSet::Change> &insertions, - const QVector<MovedFlags> *movedFlags = nullptr); - void listItemsChanged( - QVector<Change> *translatedChanges, - void *list, - const QVector<QQmlChangeSet::Change> &changes); - - friend Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor &list); -}; - -Q_DECLARE_TYPEINFO(QQmlListCompositor::Change, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQmlListCompositor::Remove, Q_PRIMITIVE_TYPE); -Q_DECLARE_TYPEINFO(QQmlListCompositor::Insert, Q_PRIMITIVE_TYPE); - -inline QQmlListCompositor::iterator::iterator() {} -inline QQmlListCompositor::iterator::iterator(const iterator &it) - : range(it.range) - , offset(it.offset) - , group(it.group) - , groupFlag(it.groupFlag) - , groupCount(it.groupCount) -{ - for (int i = 0; i < groupCount; ++i) - index[i] = it.index[i]; -} - -inline QQmlListCompositor::iterator::iterator( - Range *range, int offset, Group group, int groupCount) - : range(range) - , offset(offset) - , group(group) - , groupFlag(1 << group) - , groupCount(groupCount) -{ - for (int i = 0; i < groupCount; ++i) - index[i] = 0; -} - -inline void QQmlListCompositor::iterator::incrementIndexes(int difference, uint flags) -{ - for (int i = 0; i < groupCount; ++i) { - if (flags & (1 << i)) - index[i] += difference; - } -} - -inline void QQmlListCompositor::iterator::decrementIndexes(int difference, uint flags) -{ - for (int i = 0; i < groupCount; ++i) { - if (flags & (1 << i)) - index[i] -= difference; - } -} - -inline QQmlListCompositor::insert_iterator::insert_iterator( - Range *range, int offset, Group group, int groupCount) - : iterator(range, offset, group, groupCount) {} - -inline QQmlListCompositor::Change::Change(const iterator &it, int count, uint flags, int moveId) - : count(count), flags(flags), moveId(moveId) -{ - for (int i = 0; i < MaximumGroupCount; ++i) - index[i] = it.index[i]; -} - -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Group &group); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Range &range); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::iterator &it); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Change &change); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Remove &remove); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor::Insert &insert); -Q_AUTOTEST_EXPORT QDebug operator <<(QDebug debug, const QQmlListCompositor &list); - -QT_END_NAMESPACE - -#endif diff --git a/src/qml/util/util.pri b/src/qml/util/util.pri index bebb271f1b..3b121ba3cb 100644 --- a/src/qml/util/util.pri +++ b/src/qml/util/util.pri @@ -1,19 +1,5 @@ SOURCES += \ - $$PWD/qqmlchangeset.cpp \ - $$PWD/qqmllistaccessor.cpp \ - $$PWD/qqmllistcompositor.cpp \ $$PWD/qqmlpropertymap.cpp HEADERS += \ - $$PWD/qqmlchangeset_p.h \ - $$PWD/qqmllistaccessor_p.h \ - $$PWD/qqmllistcompositor_p.h \ $$PWD/qqmlpropertymap.h - -qtConfig(qml-delegate-model) { - SOURCES += \ - $$PWD/qqmladaptormodel.cpp - - HEADERS += \ - $$PWD/qqmladaptormodel_p.h -} |