diff options
Diffstat (limited to 'src')
95 files changed, 872 insertions, 328 deletions
diff --git a/src/3rdparty/masm/yarr/YarrInterpreter.cpp b/src/3rdparty/masm/yarr/YarrInterpreter.cpp index 6eb6750dc4..4d3652fcbc 100644 --- a/src/3rdparty/masm/yarr/YarrInterpreter.cpp +++ b/src/3rdparty/masm/yarr/YarrInterpreter.cpp @@ -2125,7 +2125,15 @@ public: auto outputTermIndexAndNest = [&](size_t index, unsigned termNesting) { for (unsigned nestingDepth = 0; nestingDepth < termIndexNest; nestingDepth++) out.print(" "); +#if defined(WIN32) && defined(__MINGW32__) +# if __SIZEOF_POINTER__ == 8 + out.printf("%4I64u", index); +# else + out.printf("%4I32u", index); +# endif +#else out.printf("%4zu", index); +#endif for (unsigned nestingDepth = 0; nestingDepth < termNesting; nestingDepth++) out.print(" "); }; diff --git a/src/imports/labsmodels/plugin.cpp b/src/imports/labsmodels/plugin.cpp index e18f08b70b..cebc1dc920 100644 --- a/src/imports/labsmodels/plugin.cpp +++ b/src/imports/labsmodels/plugin.cpp @@ -70,7 +70,6 @@ public: void registerTypes(const char *uri) override { Q_ASSERT(QLatin1String(uri) == QLatin1String("Qt.labs.qmlmodels")); - Q_UNUSED(uri); QQmlModelsModule::defineLabsModule(); qmlRegisterModule(uri, 1, 0); diff --git a/src/imports/layouts/plugin.cpp b/src/imports/layouts/plugin.cpp index 80799e8859..d28109c3cf 100644 --- a/src/imports/layouts/plugin.cpp +++ b/src/imports/layouts/plugin.cpp @@ -56,7 +56,6 @@ public: void registerTypes(const char *uri) override { Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Layouts")); - Q_UNUSED(uri); qmlRegisterType<QQuickRowLayout>(uri, 1, 0, "RowLayout"); qmlRegisterType<QQuickColumnLayout>(uri, 1, 0, "ColumnLayout"); diff --git a/src/imports/models/plugin.cpp b/src/imports/models/plugin.cpp index 2b19345f6b..9fe63412f3 100644 --- a/src/imports/models/plugin.cpp +++ b/src/imports/models/plugin.cpp @@ -75,7 +75,6 @@ public: void registerTypes(const char *uri) override { Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQml.Models")); - Q_UNUSED(uri); QQmlModelsModule::defineModule(); // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward diff --git a/src/imports/particles/plugin.cpp b/src/imports/particles/plugin.cpp index e2b8712599..26fd979133 100644 --- a/src/imports/particles/plugin.cpp +++ b/src/imports/particles/plugin.cpp @@ -54,7 +54,6 @@ public: void registerTypes(const char *uri) override { Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Particles")); - Q_UNUSED(uri); QQuickParticlesModule::defineModule(); // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index 535e29ee70..266e3111bb 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -1787,17 +1787,28 @@ Item { running = true // Check the run list to see if this class is mentioned. - var functionsToRun = qtest_results.functionsToRun - if (functionsToRun.length > 0) { + let checkNames = false + let testsToRun = {} // explicitly provided function names to run and their tags for data-driven tests + + if (qtest_results.functionsToRun.length > 0) { + checkNames = true var found = false - var list = [] + if (name.length > 0) { - var prefix = name + "::" - for (var index in functionsToRun) { - if (functionsToRun[index].indexOf(prefix) == 0) { - list.push(functionsToRun[index]) - found = true - } + for (var index in qtest_results.functionsToRun) { + let caseFuncName = qtest_results.functionsToRun[index] + if (caseFuncName.indexOf(name + "::") != 0) + continue + + found = true + let funcName = caseFuncName.substring(name.length + 2) + + if (!(funcName in testsToRun)) + testsToRun[funcName] = [] + + let tagName = qtest_results.tagsToRun[index] + if (tagName.length > 0) // empty tags mean run all rows + testsToRun[funcName].push(tagName) } } if (!found) { @@ -1809,7 +1820,6 @@ Item { qtest_results.testCaseName = "" return } - functionsToRun = list } // Run the initTestCase function. @@ -1834,17 +1844,15 @@ Item { } testList.sort() } - var checkNames = (functionsToRun.length > 0) + for (var index in testList) { var prop = testList[index] + + if (checkNames && !(prop in testsToRun)) + continue + var datafunc = prop + "_data" var isBenchmark = (prop.indexOf("benchmark_") == 0) - if (checkNames) { - var index = functionsToRun.indexOf(name + "::" + prop) - if (index < 0) - continue - functionsToRun.splice(index, 1) - } qtest_results.functionName = prop if (!(datafunc in testCase)) @@ -1854,18 +1862,29 @@ Item { if (qtest_runInternal(datafunc)) { var table = qtest_testCaseResult var haveData = false + + let checkTags = (checkNames && testsToRun[prop].length > 0) + qtest_results.initTestTable() for (var index in table) { haveData = true var row = table[index] if (!row.tag) row.tag = "row " + index // Must have something + if (checkTags) { + let tags = testsToRun[prop] + let tagIdx = tags.indexOf(row.tag) + if (tagIdx < 0) + continue + tags.splice(tagIdx, 1) + } qtest_results.dataTag = row.tag if (isBenchmark) qtest_runBenchmarkFunction(prop, row) else qtest_runFunction(prop, row) qtest_results.dataTag = "" + qtest_results.skipped = false } if (!haveData) { if (datafunc === "init_data") @@ -1883,6 +1902,9 @@ Item { } qtest_results.finishTestFunction() qtest_results.skipped = false + + if (checkNames && testsToRun[prop].length <= 0) + delete testsToRun[prop] } // Run the cleanupTestCase function. @@ -1891,8 +1913,21 @@ Item { qtest_runInternal("cleanupTestCase") // Complain about missing functions that we were supposed to run. - if (functionsToRun.length > 0) - qtest_results.fail("Could not find functions: " + functionsToRun, "", 0) + if (checkNames) { + let missingTests = [] + for (var func in testsToRun) { + let caseFuncName = name + '::' + func + let tags = testsToRun[func] + if (tags.length <= 0) + missingTests.push(caseFuncName) + else + for (var i in tags) + missingTests.push(caseFuncName + ':' + tags[i]) + } + missingTests.sort() + if (missingTests.length > 0) + qtest_results.fail("Could not find test functions: " + missingTests, "", 0) + } // Clean up and exit. running = false diff --git a/src/imports/testlib/plugins.qmltypes b/src/imports/testlib/plugins.qmltypes index 56b4ecf662..e51371d176 100644 --- a/src/imports/testlib/plugins.qmltypes +++ b/src/imports/testlib/plugins.qmltypes @@ -195,6 +195,7 @@ Module { Property { name: "failCount"; type: "int"; isReadonly: true } Property { name: "skipCount"; type: "int"; isReadonly: true } Property { name: "functionsToRun"; type: "QStringList"; isReadonly: true } + Property { name: "tagsToRun"; type: "QStringList"; isReadonly: true } Signal { name: "programNameChanged" } Method { name: "reset" } Method { name: "startLogging" } diff --git a/src/imports/window/plugin.cpp b/src/imports/window/plugin.cpp index 657b230fa2..dfe1dcf62e 100644 --- a/src/imports/window/plugin.cpp +++ b/src/imports/window/plugin.cpp @@ -69,7 +69,6 @@ public: void registerTypes(const char *uri) override { Q_ASSERT(QLatin1String(uri) == QLatin1String("QtQuick.Window")); - Q_UNUSED(uri); QQuickWindowModule::defineModule(); // Auto-increment the import to stay in sync with ALL future QtQuick minor versions from 5.11 onward diff --git a/src/particles/particles.pro b/src/particles/particles.pro index ab1c854253..6a3fb1bdc4 100644 --- a/src/particles/particles.pro +++ b/src/particles/particles.pro @@ -6,7 +6,7 @@ CONFIG += internal_module QT = core-private gui-private qml-private quick-private DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES -win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS +msvc:DEFINES *= _CRT_SECURE_NO_WARNINGS solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 exists("qqml_enable_gcov") { diff --git a/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp index 0d6cd45354..c4d7872162 100644 --- a/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp +++ b/src/plugins/qmltooling/qmldbg_inspector/highlight.cpp @@ -188,7 +188,7 @@ void SelectionHighlight::showName(const QPointF &displayPoint) { m_displayPoint = displayPoint; m_nameDisplayActive = true; - QTimer::singleShot(1500, this, SLOT(disableNameDisplay())); + QTimer::singleShot(1500, this, &SelectionHighlight::disableNameDisplay); update(); } diff --git a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp index e4f2f556fc..12c36f3dd6 100644 --- a/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp +++ b/src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp @@ -87,14 +87,17 @@ qint64 QV4ProfilerAdapter::appendMemoryEvents(qint64 until, QList<QByteArray> &m qint64 QV4ProfilerAdapter::finalizeMessages(qint64 until, QList<QByteArray> &messages, qint64 callNext, QQmlDebugPacket &d) { + qint64 memoryNext = -1; + if (callNext == -1) { m_functionLocations.clear(); m_functionCallData.clear(); m_functionCallPos = 0; + memoryNext = appendMemoryEvents(until, messages, d); + } else { + memoryNext = appendMemoryEvents(qMin(callNext, until), messages, d); } - qint64 memoryNext = appendMemoryEvents(until, messages, d); - if (memoryNext == -1) { m_memoryData.clear(); m_memoryPos = 0; diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index 9cf96d27f3..af86b70014 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -537,6 +537,9 @@ std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint COLLECTOR_BEGIN_INSTR(ThrowOnNullOrUndefined) COLLECTOR_END_INSTR(ThrowOnNullOrUndefined) + COLLECTOR_BEGIN_INSTR(GetTemplateObject) + COLLECTOR_END_INSTR(GetTemplateObject) + COLLECTOR_BEGIN_INSTR(LoadQmlContext) COLLECTOR_END_INSTR(LoadQmlContext) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 21fb03c1a4..f570af4819 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -383,6 +383,7 @@ void Codegen::accept(Node *node) void Codegen::statement(Statement *ast) { + RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); RegisterScope scope(this); bytecodeGenerator->setLocation(ast->firstSourceLocation()); @@ -395,11 +396,12 @@ void Codegen::statement(Statement *ast) void Codegen::statement(ExpressionNode *ast) { - RegisterScope scope(this); - if (! ast) { return; } else { + RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); + RegisterScope scope(this); + Result r(nx); qSwap(_expr, r); VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); @@ -426,6 +428,7 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift if (!ast) return; + RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); Result r(iftrue, iffalse, trueBlockFollowsCondition); qSwap(_expr, r); accept(ast); @@ -449,6 +452,7 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift Codegen::Reference Codegen::expression(ExpressionNode *ast) { + RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); Result r; if (ast) { qSwap(_expr, r); @@ -598,6 +602,10 @@ Codegen::Reference Codegen::targetForPatternElement(AST::PatternElement *p) Reference lhs = expression(p->bindingTarget); if (hasError) return lhs; + if (!lhs.isLValue()) { + throwReferenceError(p->bindingTarget->firstSourceLocation(), QStringLiteral("Binding target is not a reference.")); + return lhs; + } lhs = lhs.asLValue(); return lhs; } @@ -2145,8 +2153,10 @@ bool Codegen::visit(ConditionalExpression *ast) iffalse.link(); Reference ko = expression(ast->ko); - if (hasError) + if (hasError) { + jump_endif.link(); // dummy link, to prevent assert in Jump destructor from triggering return false; + } ko.loadInAccumulator(); jump_endif.link(); @@ -2311,59 +2321,35 @@ bool Codegen::visit(TaggedTemplate *ast) break; } - int arrayTemp = createTemplateArray(ast->templateLiteral); - Q_UNUSED(arrayTemp); + createTemplateObject(ast->templateLiteral); + int templateObjectTemp = Reference::fromAccumulator(this).storeOnStack().stackSlot(); + Q_UNUSED(templateObjectTemp); auto calldata = pushTemplateArgs(ast->templateLiteral); if (hasError) return false; ++calldata.argc; - Q_ASSERT(calldata.argv == arrayTemp + 1); + Q_ASSERT(calldata.argv == templateObjectTemp + 1); --calldata.argv; handleCall(base, calldata, functionObject, thisObject); return false; } -int Codegen::createTemplateArray(TemplateLiteral *t) +void Codegen::createTemplateObject(TemplateLiteral *t) { - int arrayTemp = bytecodeGenerator->newRegister(); - - RegisterScope scope(this); + TemplateObject obj; - int argc = 0; - int args = -1; - auto push = [this, &argc, &args](const QStringRef &arg) { - int temp = bytecodeGenerator->newRegister(); - if (args == -1) - args = temp; - Instruction::LoadRuntimeString instr; - instr.stringId = registerString(arg.toString()); - bytecodeGenerator->addInstruction(instr); - Instruction::StoreReg store; - store.reg = temp; - bytecodeGenerator->addInstruction(store); - - ++argc; - }; - - for (TemplateLiteral *it = t; it; it = it->next) - push(it->value); - - if (args == -1) { - Q_ASSERT(argc == 0); - args = 0; + for (TemplateLiteral *it = t; it; it = it->next) { + obj.strings.append(registerString(it->value.toString())); + obj.rawStrings.append(registerString(it->rawValue.toString())); } - Instruction::DefineArray call; - call.argc = argc; - call.args = Moth::StackSlot::createRegister(args); - bytecodeGenerator->addInstruction(call); - - Instruction::StoreReg store; - store.reg = arrayTemp; - bytecodeGenerator->addInstruction(store); + int index = _module->templateObjects.size(); + _module->templateObjects.append(obj); - return arrayTemp; + Instruction::GetTemplateObject getTemplateObject; + getTemplateObject.index = index; + bytecodeGenerator->addInstruction(getTemplateObject); } bool Codegen::visit(FunctionExpression *ast) diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 7630a1f71d..3f96afc7c2 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -682,7 +682,7 @@ public: void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject); Arguments pushTemplateArgs(AST::TemplateLiteral *args); - int createTemplateArray(AST::TemplateLiteral *t); + void createTemplateObject(AST::TemplateLiteral *t); void setUseFastLookups(bool b) { useFastLookups = b; } @@ -767,6 +767,31 @@ protected: bool _onoff; }; + class RecursionDepthCheck { + public: + RecursionDepthCheck(Codegen *cg, const AST::SourceLocation &loc) + : _cg(cg) + { +#ifdef QT_NO_DEBUG + const int depthLimit = 4000; // limit to ~1000 deep +#else + const int depthLimit = 1000; // limit to ~250 deep +#endif // QT_NO_DEBUG + + ++_cg->_recursionDepth; + if (_cg->_recursionDepth > depthLimit) + _cg->throwSyntaxError(loc, QStringLiteral("Maximum statement or expression depth exceeded")); + } + + ~RecursionDepthCheck() + { --_cg->_recursionDepth; } + + private: + Codegen *_cg; + }; + int _recursionDepth = 0; + friend class RecursionDepthCheck; + private: VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const; void handleConstruct(const Reference &base, AST::ArgumentList *args); diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 4b5d5dc96d..b8497937c1 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -225,6 +225,36 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) return nullptr; } +Heap::Object *CompilationUnit::templateObjectAt(int index) const +{ + Q_ASSERT(index < int(data->templateObjectTableSize)); + if (!templateObjects.size()) + templateObjects.resize(data->templateObjectTableSize); + Heap::Object *o = templateObjects.at(index); + if (o) + return o; + + // create the template object + Scope scope(engine); + const CompiledData::TemplateObject *t = data->templateObjectAt(index); + Scoped<ArrayObject> a(scope, engine->newArrayObject(t->size)); + Scoped<ArrayObject> raw(scope, engine->newArrayObject(t->size)); + ScopedValue s(scope); + for (uint i = 0; i < t->size; ++i) { + s = runtimeStrings[t->stringIndexAt(i)]; + a->arraySet(i, s); + s = runtimeStrings[t->rawStringIndexAt(i)]; + raw->arraySet(i, s); + } + + ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, raw, 1); + a->defineReadonlyProperty(QStringLiteral("raw"), raw); + ObjectPrototype::method_freeze(engine->functionCtor(), nullptr, a, 1); + + templateObjects[index] = a->objectValue()->d(); + return templateObjects.at(index); +} + void CompilationUnit::unlink() { if (engine) @@ -284,6 +314,10 @@ void CompilationUnit::markObjects(QV4::MarkStack *markStack) if (c) c->mark(markStack); + for (QV4::Heap::Object *o : qAsConst(templateObjects)) + if (o) + o->mark(markStack); + if (runtimeLookups) { for (uint i = 0; i < data->lookupTableSize; ++i) runtimeLookups[i].markObjects(markStack); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 5c03303029..7c26b0b67d 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -373,6 +373,34 @@ struct Class }; static_assert(sizeof(Class) == 24, "Class structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +struct TemplateObject +{ + quint32_le size; + + static int calculateSize(int size) { + int trailingData = 2 * size * sizeof(quint32_le); + size_t s = align(sizeof(TemplateObject) + trailingData); + Q_ASSERT(s < INT_MAX); + return int(s); + } + + static size_t align(size_t a) { + return (a + 7) & ~size_t(7); + } + + const quint32_le *stringTable() const { + return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this + 1)); + } + + uint stringIndexAt(uint i) const { + return stringTable()[i]; + } + uint rawStringIndexAt(uint i) const { + return stringTable()[size + i]; + } +}; +static_assert(sizeof(TemplateObject) == 4, "Template object structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); + struct ExportEntry { quint32_le exportName; @@ -841,6 +869,8 @@ struct Unit quint32_le offsetToFunctionTable; quint32_le classTableSize; quint32_le offsetToClassTable; + quint32_le templateObjectTableSize; + quint32_le offsetToTemplateObjectTable; quint32_le blockTableSize; quint32_le offsetToBlockTable; quint32_le lookupTableSize; @@ -908,6 +938,7 @@ struct Unit const quint32_le *functionOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToFunctionTable); } const quint32_le *classOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToClassTable); } + const quint32_le *templateObjectOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToTemplateObjectTable); } const quint32_le *blockOffsetTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToBlockTable); } const Function *functionAt(int idx) const { @@ -922,6 +953,12 @@ struct Unit return reinterpret_cast<const Class *>(reinterpret_cast<const char *>(this) + offset); } + const TemplateObject *templateObjectAt(int idx) const { + const quint32_le *offsetTable = templateObjectOffsetTable(); + const quint32_le offset = offsetTable[idx]; + return reinterpret_cast<const TemplateObject *>(reinterpret_cast<const char *>(this) + offset); + } + const Block *blockAt(int idx) const { const quint32_le *offsetTable = blockOffsetTable(); const quint32_le offset = offsetTable[idx]; @@ -957,7 +994,7 @@ struct Unit const quint32_le *moduleRequestTable() const { return reinterpret_cast<const quint32_le*>((reinterpret_cast<const char *>(this)) + offsetToModuleRequestTable); } }; -static_assert(sizeof(Unit) == 240, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Unit) == 248, "Unit structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct TypeReference { @@ -1110,6 +1147,7 @@ public: QV4::Lookup *runtimeLookups = nullptr; QVector<QV4::Function *> runtimeFunctions; QVector<QV4::Heap::InternalClass *> runtimeBlocks; + mutable QVector<QV4::Heap::Object *> templateObjects; mutable QQmlNullableValue<QUrl> m_url; mutable QQmlNullableValue<QUrl> m_finalUrl; @@ -1160,6 +1198,8 @@ public: return data->stringAtInternal(index); } + Heap::Object *templateObjectAt(int index) const; + struct FunctionIterator { FunctionIterator(const Unit *unit, const Object *object, int index) : unit(unit), object(object), index(index) {} diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 7636baa1e6..3076c6b526 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -269,7 +269,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO registerString(request); } - Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->blocks.size()) * sizeof(quint32_le)); + Q_ALLOCA_VAR(quint32_le, blockClassAndFunctionOffsets, (module->functions.size() + module->classes.size() + module->templateObjects.size() + module->blocks.size()) * sizeof(quint32_le)); uint jsClassDataOffset = 0; char *dataPtr; @@ -284,7 +284,8 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO memcpy(dataPtr + unit->offsetToFunctionTable, blockClassAndFunctionOffsets, unit->functionTableSize * sizeof(quint32_le)); memcpy(dataPtr + unit->offsetToClassTable, blockClassAndFunctionOffsets + unit->functionTableSize, unit->classTableSize * sizeof(quint32_le)); - memcpy(dataPtr + unit->offsetToBlockTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, unit->blockTableSize * sizeof(quint32_le)); + memcpy(dataPtr + unit->offsetToTemplateObjectTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize, unit->templateObjectTableSize * sizeof(quint32_le)); + memcpy(dataPtr + unit->offsetToBlockTable, blockClassAndFunctionOffsets + unit->functionTableSize + unit->classTableSize + unit->templateObjectTableSize, unit->blockTableSize * sizeof(quint32_le)); for (int i = 0; i < module->functions.size(); ++i) { Context *function = module->functions.at(i); @@ -300,10 +301,16 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO writeClass(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size()], c); } + for (int i = 0; i < module->templateObjects.size(); ++i) { + const TemplateObject &t = module->templateObjects.at(i); + + writeTemplateObject(dataPtr + blockClassAndFunctionOffsets[i + module->functions.size() + module->classes.size()], t); + } + for (int i = 0; i < module->blocks.size(); ++i) { Context *block = module->blocks.at(i); - writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->functions.size()], block); + writeBlock(dataPtr + blockClassAndFunctionOffsets[i + module->classes.size() + module->templateObjects.size() + module->functions.size()], block); } CompiledData::Lookup *lookupsToWrite = reinterpret_cast<CompiledData::Lookup*>(dataPtr + unit->offsetToLookupTable); @@ -532,6 +539,34 @@ void QV4::Compiler::JSUnitGenerator::writeClass(char *b, const QV4::Compiler::Cl } } +void QV4::Compiler::JSUnitGenerator::writeTemplateObject(char *b, const QV4::Compiler::TemplateObject &t) +{ + QV4::CompiledData::TemplateObject *tmpl = reinterpret_cast<QV4::CompiledData::TemplateObject *>(b); + tmpl->size = t.strings.size(); + + quint32 currentOffset = sizeof(QV4::CompiledData::TemplateObject); + + quint32_le *strings = reinterpret_cast<quint32_le *>(b + currentOffset); + + // write methods + for (int i = 0; i < t.strings.size(); ++i) + strings[i] = t.strings.at(i); + strings += t.strings.size(); + + for (int i = 0; i < t.rawStrings.size(); ++i) + strings[i] = t.rawStrings.at(i); + + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); + if (showCode) { + qDebug() << "=== TemplateObject size" << tmpl->size; + for (uint i = 0; i < tmpl->size; ++i) { + qDebug() << " " << i << stringForIndex(tmpl->stringIndexAt(i)); + qDebug() << " raw: " << stringForIndex(tmpl->rawStringIndexAt(i)); + } + qDebug(); + } +} + void QV4::Compiler::JSUnitGenerator::writeBlock(char *b, QV4::Compiler::Context *irBlock) const { QV4::CompiledData::Block *block = reinterpret_cast<QV4::CompiledData::Block *>(b); @@ -580,6 +615,10 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.offsetToClassTable = nextOffset; nextOffset += unit.classTableSize * sizeof(uint); + unit.templateObjectTableSize = module->templateObjects.size(); + unit.offsetToTemplateObjectTable = nextOffset; + nextOffset += unit.templateObjectTableSize * sizeof(uint); + unit.blockTableSize = module->blocks.size(); unit.offsetToBlockTable = nextOffset; nextOffset += unit.blockTableSize * sizeof(uint); @@ -658,6 +697,14 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp } blockAndFunctionOffsets += module->classes.size(); + for (int i = 0; i < module->templateObjects.size(); ++i) { + const TemplateObject &t = module->templateObjects.at(i); + blockAndFunctionOffsets[i] = nextOffset; + + nextOffset += QV4::CompiledData::TemplateObject::calculateSize(t.strings.size()); + } + blockAndFunctionOffsets += module->templateObjects.size(); + for (int i = 0; i < module->blocks.size(); ++i) { Context *c = module->blocks.at(i); blockAndFunctionOffsets[i] = nextOffset; diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index c4c886ffad..2f5889ab53 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -73,6 +73,7 @@ struct JSClassMember; namespace Compiler { struct Class; +struct TemplateObject; struct Q_QML_PRIVATE_EXPORT StringTableGenerator { StringTableGenerator(); @@ -137,6 +138,7 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { QV4::CompiledData::Unit *generateUnit(GeneratorOption option = GenerateWithStringTable); void writeFunction(char *f, Context *irFunction) const; void writeClass(char *f, const Class &c); + void writeTemplateObject(char *f, const TemplateObject &o); void writeBlock(char *f, Context *irBlock) const; StringTableGenerator stringTable; diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index ca4cbfc4fc..5772bff7bf 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -187,7 +187,7 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS } // ### can we relax the restrictions here? - if (contextType == ContextType::Eval || c->contextType == ContextType::Binding) + if (c->contextType == ContextType::Eval || c->contextType == ContextType::Binding) return result; result.type = ResolvedName::Global; diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index d36ef0f447..328715da07 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -99,6 +99,14 @@ struct Class { QVector<Method> methods; }; +struct TemplateObject { + QVector<uint> strings; + QVector<uint> rawStrings; + bool operator==(const TemplateObject &other) { + return strings == other.strings && rawStrings == other.rawStrings; + } +}; + struct ExportEntry { QString exportName; @@ -133,6 +141,7 @@ struct Module { QList<Context *> functions; QList<Context *> blocks; QVector<Class> classes; + QVector<TemplateObject> templateObjects; Context *rootContext; QString fileName; QString finalUrl; diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 2026e64929..fc3ac769ae 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -96,6 +96,25 @@ void ScanFunctions::leaveEnvironment() _context = _contextStack.isEmpty() ? nullptr : _contextStack.top(); } +bool ScanFunctions::preVisit(Node *ast) +{ + if (_cg->hasError) + return false; + ++_recursionDepth; + + if (_recursionDepth > 1000) { + _cg->throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded")); + return false; + } + + return true; +} + +void ScanFunctions::postVisit(Node *) +{ + --_recursionDepth; +} + void ScanFunctions::checkDirectivePrologue(StatementList *ast) { for (StatementList *it = ast; it; it = it->next) { diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h index bb07540ec9..4463a4f4f3 100644 --- a/src/qml/compiler/qv4compilerscanfunctions_p.h +++ b/src/qml/compiler/qv4compilerscanfunctions_p.h @@ -96,6 +96,9 @@ protected: using Visitor::visit; using Visitor::endVisit; + bool preVisit(AST::Node *ast) override; + void postVisit(AST::Node *) override; + void checkDirectivePrologue(AST::StatementList *ast); void checkName(const QStringRef &name, const AST::SourceLocation &loc); @@ -169,6 +172,8 @@ protected: bool _allowFuncDecls; ContextType defaultProgramType; + unsigned _recursionDepth = 0; + private: static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr; }; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index eed8ffe6b8..6edf5a4ae7 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -712,6 +712,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(ThrowOnNullOrUndefined) MOTH_END_INSTR(ThrowOnNullOrUndefined) + MOTH_BEGIN_INSTR(GetTemplateObject) + d << index; + MOTH_END_INSTR(GetTemplateObject) + MOTH_BEGIN_INSTR(LoadQmlContext) d << dumpRegister(result, nFormals); MOTH_END_INSTR(LoadQmlContext) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 2b1660ee58..2ca8f692b8 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -198,6 +198,7 @@ QT_BEGIN_NAMESPACE #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) +#define INSTR_GetTemplateObject(op) INSTRUCTION(op, GetTemplateObject, 1, index) #define INSTR_TailCall(op) INSTRUCTION(op, TailCall, 4, func, thisObject, argc, argv) #define FOR_EACH_MOTH_INSTR_ALL(F) \ @@ -339,6 +340,7 @@ QT_BEGIN_NAMESPACE F(PopScriptContext) \ F(InitializeBlockDeadTemporalZone) \ F(ThrowOnNullOrUndefined) \ + F(GetTemplateObject) \ F(TailCall) \ F(Debug) \ diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index 6159ffe20b..8ebbd28737 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -91,7 +91,7 @@ when passed from C++ to QML and vice-versa: \li QFont \li \l font \row - \li QDate + \li QDateTime \li \l date \row \li QPoint, QPointF diff --git a/src/qml/doc/src/qmlfunctions.qdoc b/src/qml/doc/src/qmlfunctions.qdoc index 62c0f5d81b..969dd51433 100644 --- a/src/qml/doc/src/qmlfunctions.qdoc +++ b/src/qml/doc/src/qmlfunctions.qdoc @@ -356,13 +356,36 @@ */ /*! - \fn int qmlRegisterInterface(const char *typeName) - \relates QQmlEngine + \fn int qmlRegisterInterface(const char *typeName) + \relates QQmlEngine - This template function registers the C++ type in the QML system - under the name \a typeName. + This template function registers the C++ type in the QML system + under the name \a typeName. - Returns the QML type id. + Types registered as an interface with the engine should also + declare themselves as an interface with the + \l {The Meta-Object System}{meta object system}. For example: + + \code + struct FooInterface + { + public: + virtual ~FooInterface(); + virtual void doSomething() = 0; + }; + + Q_DECLARE_INTERFACE(FooInterface, "org.foo.FooInterface") + \endcode + + When registered with the QML engine in this way, they can be used as + property types: + + Q_PROPERTY(FooInterface *foo READ foo WRITE setFoo) + + When you assign a \l QObject sub-class to this property, the QML engine does + the interface cast to \c FooInterface* automatically. + + Returns the QML type id. */ /*! diff --git a/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc b/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc index 7c1d65b095..01e81e7c19 100644 --- a/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc +++ b/src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc @@ -23,7 +23,7 @@ plugins. Library plugins should limit themselves to registering types, as any manipulation of the engine's root context may cause conflicts or other issues in the library user's code. -\section1 Plugin Example +\section1 TimeExample QML extension plugin Suppose there is a new \c TimeModel C++ class that should be made available as a new QML type. It provides the current time through \c hour and \c minute @@ -47,6 +47,8 @@ imported correctly by any QML components that use this plugin. The \l{Defining QML Types from C++} article has more information about registering C++ types into the runtime. +\section1 Project settings for the plugin + Additionally, the project file (\c .pro) defines the project as a plugin library, specifies it should be built into the \c imports/TimeExample directory, and registers the plugin target name and various other details: @@ -61,6 +63,8 @@ TARGET = qmlqtimeexampleplugin SOURCES += qexampleqmlplugin.cpp \endcode +\section1 Plugin definition in the qmldir + Finally, a \l{Module Definition qmldir Files}{qmldir file} is required in the \c imports/TimeExample directory to describe the plugin and the types that it exports. The plugin includes a \c Clock.qml file along with the \c qmlqtimeexampleplugin diff --git a/src/qml/doc/src/qmllanguageref/qmlreference.qdoc b/src/qml/doc/src/qmllanguageref/qmlreference.qdoc index 3c3999f324..901a4a57fe 100644 --- a/src/qml/doc/src/qmllanguageref/qmlreference.qdoc +++ b/src/qml/doc/src/qmllanguageref/qmlreference.qdoc @@ -59,6 +59,7 @@ modules. \li \l{qtqml-syntax-objectattributes.html#signal-attributes}{Signal Attributes} \li \l{qtqml-syntax-objectattributes.html#method-attributes}{Method Attributes} \li \l{qtqml-syntax-objectattributes.html#attached-properties-and-attached-signal-handlers}{Attached Properties and Attached Signal Handlers} + \li \l{qtqml-syntax-objectattributes.html#enumeration-attributes}{Enumeration Attributes} \endlist \li \l{qtqml-syntax-propertybinding.html}{Property Binding} diff --git a/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc index b25ed625b2..c4c1b61693 100644 --- a/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc +++ b/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc @@ -673,4 +673,5 @@ property is only invoked when the property is reassigned to a different object v \endqml \sa {QML Basic Types} + \sa {qtqml-syntax-objectattributes.html#enumeration-attributes}{Enumeration Attributes} */ diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc index 79bfdc7042..ae36ebbcc9 100644 --- a/src/qml/doc/src/qmltypereference.qdoc +++ b/src/qml/doc/src/qmltypereference.qdoc @@ -91,10 +91,17 @@ provided: The \c date type refers to a date value, including the time of the day. -To create a \c date value, specify it as a "YYYY-MM-DD" string: +To create a \c date value, specify it as a "YYYY-MM-DDThh:mm:ss.zzzZ" string. +(The T is literal, YYYY is a full year number, MM and DD are month and day +numbers, hh, mm and ss are hours, minutes and seconds, with .zzz as +milliseconds and Z as time-zone offset. The T and following time are optional. +If they are omitted, the date is handled as the start of UTC's day, which +falls on other dates in some time-zones. When T is included, the :ss.zzz or +just .zzz part can be omitted. With or without those, the zone offset can be +omitted, in which case local time is used.) For example: \qml -MyDatePicker { minDate: "2000-01-01"; maxDate: "2020-12-31" } +MyDatePicker { minDate: "2000-01-01 0:0"; maxDate: "2020-12-31 23:59" } \endqml To read a date value returned from a C++ extension class, use @@ -102,7 +109,13 @@ To read a date value returned from a C++ extension class, use When integrating with C++, note that any QDate or QDateTime value \l{qtqml-cppintegration-data.html}{passed into QML from C++} is automatically -converted into a \c date value, and vice-versa. +converted into a \c date value, and vice-versa. Note, however, that +converting a QDate will result in UTC's start of the day, which falls on +a different date in some other time-zones. It is usually more robust +to convert the QDate via a QDateTime explicitly, specifying local-time +or a relevant time-zone and selecting a time of day (such as noon) +that reliably exists (daylight-savings transitions skip an hour, near +one end or the other of a day). This basic type is provided by the QML language. It can be implicitly converted to a \l{QtQml::Date}{Date} object. diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 6bc1ca6e45..098bbfc6c6 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -992,6 +992,15 @@ void BaselineJIT::generate_ThrowOnNullOrUndefined() as->checkException(); } +void BaselineJIT::generate_GetTemplateObject(int index) +{ + STORE_ACC(); + as->prepareCallWithArgCount(2); + as->passInt32AsArg(index, 1); + as->passFunctionAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(RuntimeHelpers::getTemplateObject, CallResultDestination::InAccumulator); + as->checkException(); +} void BaselineJIT::startInstruction(Instr::Type /*instr*/) { diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index 47ad274d23..98d23f4517 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -215,6 +215,7 @@ public: void generate_LoadQmlImportedScripts(int result) override; void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; void generate_ThrowOnNullOrUndefined() override; + void generate_GetTemplateObject(int index) override; void startInstruction(Moth::Instr::Type instr) override; void endInstruction(Moth::Instr::Type instr) override; diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp index b3ae630a95..225a4443d9 100644 --- a/src/qml/jsapi/qjsvalue.cpp +++ b/src/qml/jsapi/qjsvalue.cpp @@ -80,7 +80,7 @@ used to test if a value is of a certain type. The methods named toT() (e.g. toBool(), toString()) can be used to convert a QJSValue to another type. You can also use the generic - QJSValue_cast() function. + qjsvalue_cast() function. Object values have zero or more properties which are themselves QJSValues. Use setProperty() to set a property of an object, and diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 2e7c994550..8637db3dfd 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -60,11 +60,13 @@ void Heap::ArrayCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Array")); } -ReturnedValue ArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue ArrayCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *v4 = static_cast<const ArrayCtor *>(f)->engine(); Scope scope(v4); ScopedArrayObject a(scope, v4->newArrayObject()); + if (newTarget) + a->setProtoFromNewTarget(newTarget); uint len; if (argc == 1 && argv[0].isNumber()) { bool ok; diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index 04ec7e1607..c959b71bc6 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -70,7 +70,7 @@ struct ArrayCtor: FunctionObject { V4_OBJECT2(ArrayCtor, FunctionObject) - static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *); + static ReturnedValue virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget); static ReturnedValue virtualCall(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index 075ee1657e..3e5f51c302 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -50,10 +50,18 @@ void Heap::BooleanCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Boolean")); } -ReturnedValue BooleanCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *) +ReturnedValue BooleanCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *newTarget) { + auto v4 = that->engine(); bool n = argc ? argv[0].toBoolean() : false; - return Encode(that->engine()->newBooleanObject(n)); + + ReturnedValue o = Encode(v4->newBooleanObject(n)); + if (!newTarget) + return o; + Scope scope(v4); + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } ReturnedValue BooleanCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index df3bb37e9c..a13fb37a52 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -755,16 +755,16 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Date")); } -ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *) +ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, const Value *argv, int argc, const Value *newTarget) { - ExecutionEngine *e = that->engine(); + ExecutionEngine *v4 = that->engine(); double t = 0; if (argc == 0) t = currentTime(); else if (argc == 1) { - Scope scope(e); + Scope scope(v4); ScopedValue arg(scope, argv[0]); if (DateObject *d = arg->as<DateObject>()) { t = d->date(); @@ -772,7 +772,7 @@ ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, con arg = RuntimeHelpers::toPrimitive(arg, PREFERREDTYPE_HINT); if (String *s = arg->stringValue()) - t = ParseString(s->toQString(), e->localTZA); + t = ParseString(s->toQString(), v4->localTZA); else t = TimeClip(arg->toNumber()); } @@ -789,10 +789,16 @@ ReturnedValue DateCtor::virtualCallAsConstructor(const FunctionObject *that, con if (year >= 0 && year <= 99) year += 1900; t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - t = TimeClip(UTC(t, e->localTZA)); + t = TimeClip(UTC(t, v4->localTZA)); } - return Encode(e->newDateObject(Value::fromDouble(t))); + ReturnedValue o = Encode(v4->newDateObject(Value::fromDouble(t))); + if (!newTarget) + return o; + Scope scope(v4); + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } ReturnedValue DateCtor::virtualCall(const FunctionObject *m, const Value *, const Value *, int) diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 93cc55f8ad..dfe9d35194 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -276,7 +276,7 @@ QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngin return cg.generateCompilationUnit(); } -ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *engine = f->engine(); @@ -286,7 +286,14 @@ ReturnedValue FunctionCtor::virtualCallAsConstructor(const FunctionObject *f, co Function *vmf = compilationUnit->linkToEngine(engine); ExecutionContext *global = engine->scriptContext(); - return Encode(FunctionObject::createScriptFunction(global, vmf)); + ReturnedValue o = Encode(FunctionObject::createScriptFunction(global, vmf)); + + if (!newTarget) + return o; + Scope scope(engine); + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } // 15.3.1: This is equivalent to new Function(...) diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp index da87127e08..566db6fd4e 100644 --- a/src/qml/jsruntime/qv4generatorobject.cpp +++ b/src/qml/jsruntime/qv4generatorobject.cpp @@ -54,7 +54,7 @@ void Heap::GeneratorFunctionCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("GeneratorFunction")); } -ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *engine = f->engine(); @@ -64,7 +64,14 @@ ReturnedValue GeneratorFunctionCtor::virtualCallAsConstructor(const FunctionObje Function *vmf = compilationUnit->linkToEngine(engine); ExecutionContext *global = engine->scriptContext(); - return Encode(GeneratorFunction::create(global, vmf)); + ReturnedValue o = Encode(GeneratorFunction::create(global, vmf)); + + if (!newTarget) + return o; + Scope scope(engine); + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } // 15.3.1: This is equivalent to new Function(...) diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index e456879d9c..36569b0a60 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -166,7 +166,7 @@ void QV4Include::finished() QmlIR::Document::removeScriptPragmas(code); QV4::Scoped<QV4::QmlContext> qml(scope, m_qmlContext.value()); - QV4::Script script(v4, qml, code, m_url.toString()); + QV4::Script script(v4, qml, /*parse as QML binding*/false, code, m_url.toString()); script.parse(); if (!scope.engine->hasException) diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp index 7d53b36fcd..68741e7677 100644 --- a/src/qml/jsruntime/qv4mapobject.cpp +++ b/src/qml/jsruntime/qv4mapobject.cpp @@ -59,11 +59,14 @@ void Heap::MapCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Map")); } -ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool weakMap) +ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget, bool weakMap) { Scope scope(f); Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>()); - if (weakMap) { + bool protoSet = false; + if (newTarget) + protoSet = a->setProtoFromNewTarget(newTarget); + if (!protoSet && weakMap) { a->setPrototypeOf(scope.engine->weakMapPrototype()); scope.engine->memoryManager->registerWeakMap(a->d()); } diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 11ec53ced5..d26e888069 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -78,10 +78,18 @@ void Heap::NumberCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Number")); } -ReturnedValue NumberCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue NumberCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { + auto v4 = f->engine(); double dbl = argc ? argv[0].toNumber() : 0.; - return Encode(f->engine()->newNumberObject(dbl)); + + ReturnedValue o = Encode(f->engine()->newNumberObject(dbl)); + if (!newTarget) + return o; + Scope scope(v4); + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } ReturnedValue NumberCtor::virtualCall(const FunctionObject *, const Value *, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index a7ede4627c..3d2d54f651 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -986,6 +986,21 @@ const FunctionObject *Object::speciesConstructor(Scope &scope, const FunctionObj return static_cast<const FunctionObject *>(f); } +bool Object::setProtoFromNewTarget(const Value *newTarget) +{ + if (!newTarget || newTarget->isUndefined()) + return false; + + Q_ASSERT(newTarget->isFunctionObject()); + Scope scope(this); + ScopedObject proto(scope, static_cast<const FunctionObject *>(newTarget)->protoProperty()); + if (proto) { + setPrototypeOf(proto); + return true; + } + return false; +} + DEFINE_OBJECT_VTABLE(ArrayObject); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 6753ebfcd4..ff47810994 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -373,6 +373,8 @@ public: bool isArray() const; const FunctionObject *speciesConstructor(Scope &scope, const FunctionObject *defaultConstructor) const; + bool setProtoFromNewTarget(const Value *newTarget); + protected: static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty); static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver); diff --git a/src/qml/jsruntime/qv4promiseobject.cpp b/src/qml/jsruntime/qv4promiseobject.cpp index a955e5eb6a..8450655334 100644 --- a/src/qml/jsruntime/qv4promiseobject.cpp +++ b/src/qml/jsruntime/qv4promiseobject.cpp @@ -364,7 +364,7 @@ void Heap::RejectWrapper::init() } -ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { Scope scope(f); @@ -396,6 +396,9 @@ ReturnedValue PromiseCtor::virtualCallAsConstructor(const FunctionObject *f, con a->d()->resolution.set(scope.engine, Value::fromReturnedValue(scope.engine->catchException())); } + if (newTarget) + a->setProtoFromNewTarget(newTarget); + return a->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 8cdec2f6ee..9344a231ff 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -2226,9 +2226,7 @@ void QmlSignalHandler::initProto(ExecutionEngine *engine) void MultiplyWrappedQObjectMap::insert(QObject *key, Heap::Object *value) { - QV4::WeakValue v; - v.set(value->internalClass->engine, value); - QHash<QObject*, QV4::WeakValue>::insert(key, v); + QHash<QObject*, QV4::WeakValue>::operator[](key).set(value->internalClass->engine, value); connect(key, SIGNAL(destroyed(QObject*)), this, SLOT(removeDestroyedObject(QObject*))); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index be46245d5a..6465ee0fa6 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -290,7 +290,14 @@ public: Iterator end() { return QHash<QObject*, QV4::WeakValue>::end(); } void insert(QObject *key, Heap::Object *value); - ReturnedValue value(QObject *key) const { return QHash<QObject*, QV4::WeakValue>::value(key).value(); } + ReturnedValue value(QObject *key) const + { + ConstIterator it = find(key); + return it == end() + ? QV4::WeakValue().value() + : it->value(); + } + Iterator erase(Iterator it); void remove(QObject *key); void mark(QObject *key, MarkStack *markStack); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 4ef4fa2c9e..9df286065d 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -332,7 +332,13 @@ ReturnedValue RegExpCtor::virtualCallAsConstructor(const FunctionObject *fo, con return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); } - return Encode(scope.engine->newRegExpObject(regexp)); + ReturnedValue o = Encode(scope.engine->newRegExpObject(regexp)); + + if (!newTarget) + return o; + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } ReturnedValue RegExpCtor::virtualCall(const FunctionObject *f, const Value *, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index b576bb4d87..13244fdd95 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -595,6 +595,12 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu return Encode(x + y); } +ReturnedValue RuntimeHelpers::getTemplateObject(Function *function, int index) +{ + return function->compilationUnit->templateObjectAt(index)->asReturnedValue(); +} + + void Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) { Scope scope(engine); diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 72af90d1dc..2be3ebf012 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -114,6 +114,8 @@ struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { static Bool strictEqual(const Value &x, const Value &y); static ReturnedValue addHelper(ExecutionEngine *engine, const Value &left, const Value &right); + + static ReturnedValue getTemplateObject(Function *function, int index); }; diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index c28a3ffa2d..e4aceef3ee 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -406,10 +406,7 @@ struct Scoped return getPointer(); } - bool operator!() const { - return !ptr->m(); - } - operator void *() const { + explicit operator bool() const { return ptr->m(); } diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 3d8c037910..7bbef3335e 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -240,24 +240,8 @@ Script *Script::createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlCo QString sourceCode = QString::fromUtf8(data); QmlIR::Document::removeScriptPragmas(sourceCode); - auto result = new QV4::Script(engine, qmlContext, sourceCode, originalUrl.toString()); + auto result = new QV4::Script(engine, qmlContext, /*parseAsBinding*/false, sourceCode, originalUrl.toString()); result->contextType = QV4::Compiler::ContextType::ScriptImportedByQML; result->parse(); return result; } - -QV4::ReturnedValue Script::evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext) -{ - QV4::Scope scope(engine); - QV4::Script qmlScript(engine, qmlContext, script, QString()); - - qmlScript.parse(); - QV4::ScopedValue result(scope); - if (!scope.engine->hasException) - result = qmlScript.run(); - if (scope.engine->hasException) { - scope.engine->catchException(); - return Encode::undefined(); - } - return result->asReturnedValue(); -} diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index c138e4a538..a1e9b83a8b 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -73,10 +73,10 @@ struct Q_QML_EXPORT Script { : sourceFile(source), line(line), column(column), sourceCode(sourceCode) , context(scope), strictMode(false), inheritContext(false), parsed(false), contextType(mode) , vmFunction(nullptr), parseAsBinding(false) {} - Script(ExecutionEngine *engine, QmlContext *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) + Script(ExecutionEngine *engine, QmlContext *qml, bool parseAsBinding, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) , context(engine->rootContext()), strictMode(false), inheritContext(true), parsed(false) - , vmFunction(nullptr), parseAsBinding(true) { + , vmFunction(nullptr), parseAsBinding(parseAsBinding) { if (qml) qmlContext.set(engine, *qml); } @@ -106,8 +106,6 @@ struct Q_QML_EXPORT Script { QList<QQmlError> *reportedErrors = nullptr, QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Global); static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error); - - static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext); }; } diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp index 3c9b5031d1..088ecbe30d 100644 --- a/src/qml/jsruntime/qv4setobject.cpp +++ b/src/qml/jsruntime/qv4setobject.cpp @@ -59,11 +59,14 @@ void Heap::SetCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Set")); } -ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *, bool isWeak) +ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget, bool isWeak) { Scope scope(f); Scoped<SetObject> a(scope, scope.engine->memoryManager->allocate<SetObject>()); - if (isWeak) + bool protoSet = false; + if (newTarget) + protoSet = a->setProtoFromNewTarget(newTarget); + if (!protoSet && isWeak) a->setPrototypeOf(scope.engine->weakSetPrototype()); a->d()->isWeakSet = isWeak; diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 03f351b9e4..d0f6aff9d9 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -170,7 +170,7 @@ void Heap::StringCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("String")); } -ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *) +ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) { ExecutionEngine *v4 = static_cast<const Object *>(f)->engine(); Scope scope(v4); @@ -180,7 +180,13 @@ ReturnedValue StringCtor::virtualCallAsConstructor(const FunctionObject *f, cons else value = v4->newString(); CHECK_EXCEPTION(); - return Encode(v4->newStringObject(value)); + ReturnedValue o = Encode(v4->newStringObject(value)); + + if (!newTarget) + return o; + ScopedObject obj(scope, o); + obj->setProtoFromNewTarget(newTarget); + return obj->asReturnedValue(); } ReturnedValue StringCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc) diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 7c895e3637..20a84beccd 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -226,7 +226,7 @@ struct Q_QML_PRIVATE_EXPORT Value }; // Used only by 64-bit encoding - static const quint64 NaNEncodeMask = 0xfffc000000000000ll; + static const quint64 NaNEncodeMask = 0xfffc000000000000ull; enum { IsDouble_Shift = 64-14, IsManagedOrUndefined_Shift = 64-15, diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 1cca50f6c1..937a535b83 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -550,6 +550,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) + STORE_IP(); QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; acc = l->globalGetter(l, engine); CHECK_EXCEPTION; @@ -1378,6 +1379,10 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, } MOTH_END_INSTR(ThrowOnNullOrUndefined) + MOTH_BEGIN_INSTR(GetTemplateObject) + acc = RuntimeHelpers::getTemplateObject(function, index); + MOTH_END_INSTR(GetTemplateObject) + MOTH_BEGIN_INSTR(Debug) #if QT_CONFIG(qml_debug) STORE_IP(); diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index a7ae664d72..860a4e999e 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -392,6 +392,9 @@ protected: inline QStringRef &stringRef(int index) { return string_stack [tos + index - 1]; } + inline QStringRef &rawStringRef(int index) + { return rawString_stack [tos + index - 1]; } + inline AST::SourceLocation &loc(int index) { return location_stack [tos + index - 1]; } @@ -416,6 +419,7 @@ protected: int *state_stack = nullptr; AST::SourceLocation *location_stack = nullptr; QVector<QStringRef> string_stack; + QVector<QStringRef> rawString_stack; AST::Node *program = nullptr; @@ -427,11 +431,13 @@ protected: double dval; AST::SourceLocation loc; QStringRef spell; + QStringRef raw; }; int yytoken = -1; double yylval = 0.; QStringRef yytokenspell; + QStringRef yytokenraw; AST::SourceLocation yylloc; AST::SourceLocation yyprevlloc; @@ -493,6 +499,7 @@ void Parser::reallocateStack() state_stack = reinterpret_cast<int*> (realloc(state_stack, stack_size * sizeof(int))); location_stack = reinterpret_cast<AST::SourceLocation*> (realloc(location_stack, stack_size * sizeof(AST::SourceLocation))); string_stack.resize(stack_size); + rawString_stack.resize(stack_size); } Parser::Parser(Engine *engine): @@ -555,6 +562,7 @@ void Parser::pushToken(int token) last_token->token = yytoken; last_token->dval = yylval; last_token->spell = yytokenspell; + last_token->raw = yytokenraw; last_token->loc = yylloc; ++last_token; yytoken = token; @@ -566,6 +574,7 @@ int Parser::lookaheadToken(Lexer *lexer) yytoken = lexer->lex(); yylval = lexer->tokenValue(); yytokenspell = lexer->tokenSpell(); + yytokenraw = lexer->rawString(); yylloc = location(lexer); } return yytoken; @@ -605,8 +614,16 @@ bool Parser::parse(int startToken) program = 0; do { - if (++tos == stack_size) + if (++tos == stack_size) { reallocateStack(); + if (stack_size > 10000) { + // We're now in some serious right-recursive stuff, which will probably result in + // an AST that's so deep that recursively visiting it will run out of stack space. + const QString msg = QCoreApplication::translate("QQmlParser", "Maximum statement or expression depth exceeded"); + diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg)); + return false; + } + } state_stack[tos] = action; @@ -618,11 +635,13 @@ bool Parser::parse(int startToken) yytoken = lexer->lex(); yylval = lexer->tokenValue(); yytokenspell = lexer->tokenSpell(); + yytokenraw = lexer->rawString(); yylloc = location(lexer); } else { yytoken = first_token->token; yylval = first_token->dval; yytokenspell = first_token->spell; + yytokenraw = first_token->raw; yylloc = first_token->loc; ++first_token; if (first_token == last_token) @@ -643,6 +662,7 @@ bool Parser::parse(int startToken) yytoken = -1; sym(1).dval = yylval; stringRef(1) = yytokenspell; + rawStringRef(1) = yytokenraw; loc(1) = yylloc; } else { --tos; @@ -1889,7 +1909,7 @@ TemplateLiteral: T_NO_SUBSTITUTION_TEMPLATE; TemplateSpans: T_TEMPLATE_TAIL; /. case $rule_number: { - AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), nullptr); + AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), rawStringRef(1), nullptr); node->literalToken = loc(1); sym(1).Node = node; } break; @@ -1901,7 +1921,7 @@ TemplateSpans: T_TEMPLATE_MIDDLE Expression TemplateSpans; TemplateLiteral: T_TEMPLATE_HEAD Expression TemplateSpans; /. case $rule_number: { - AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), sym(2).Expression); + AST::TemplateLiteral *node = new (pool) AST::TemplateLiteral(stringRef(1), rawStringRef(1), sym(2).Expression); node->next = sym(3).Template; node->literalToken = loc(1); sym(1).Node = node; @@ -4354,6 +4374,7 @@ ExportSpecifier: IdentifierName T_AS IdentifierName; tk.token = yytoken; tk.dval = yylval; tk.spell = yytokenspell; + tk.raw = yytokenraw; tk.loc = yylloc; yylloc = yyprevlloc; @@ -4380,11 +4401,13 @@ ExportSpecifier: IdentifierName T_AS IdentifierName; token_buffer[0].token = yytoken; token_buffer[0].dval = yylval; token_buffer[0].spell = yytokenspell; + token_buffer[0].raw = yytokenraw; token_buffer[0].loc = yylloc; token_buffer[1].token = yytoken = lexer->lex(); token_buffer[1].dval = yylval = lexer->tokenValue(); token_buffer[1].spell = yytokenspell = lexer->tokenSpell(); + token_buffer[1].raw = yytokenraw = lexer->rawString(); token_buffer[1].loc = yylloc = location(lexer); if (t_action(errorState, yytoken)) { diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 72c47cbe32..43aeec6525 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -500,8 +500,8 @@ class QML_PARSER_EXPORT TemplateLiteral : public LeftHandSideExpression public: QQMLJS_DECLARE_AST_NODE(TemplateLiteral) - TemplateLiteral(const QStringRef &str, ExpressionNode *e) - : value(str), expression(e), next(nullptr) + TemplateLiteral(const QStringRef &str, const QStringRef &raw, ExpressionNode *e) + : value(str), rawValue(raw), expression(e), next(nullptr) { kind = K; } SourceLocation firstSourceLocation() const override @@ -513,6 +513,7 @@ public: void accept0(Visitor *visitor) override; QStringRef value; + QStringRef rawValue; ExpressionNode *expression; TemplateLiteral *next; SourceLocation literalToken; diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp index 71885c533d..c53b13f64d 100644 --- a/src/qml/parser/qqmljslexer.cpp +++ b/src/qml/parser/qqmljslexer.cpp @@ -130,6 +130,7 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode) _tokenText.reserve(1024); _errorMessage.clear(); _tokenSpell = QStringRef(); + _rawString = QStringRef(); _codePtr = code.unicode(); _endPtr = _codePtr + code.length(); @@ -163,13 +164,20 @@ void Lexer::setCode(const QString &code, int lineno, bool qmlMode) void Lexer::scanChar() { - unsigned sequenceLength = isLineTerminatorSequence(); + if (_skipLinefeed) { + Q_ASSERT(*_codePtr == QLatin1Char('\n')); + ++_codePtr; + _skipLinefeed = false; + } _char = *_codePtr++; - if (sequenceLength == 2) - _char = *_codePtr++; - ++_currentColumnNumber; + if (isLineTerminator()) { + if (_char == QLatin1Char('\r')) { + if (_codePtr < _endPtr && *_codePtr == QLatin1Char('\n')) + _skipLinefeed = true; + _char = QLatin1Char('\n'); + } ++_currentLineNumber; _currentColumnNumber = 0; } @@ -246,6 +254,7 @@ int Lexer::lex() again: _tokenSpell = QStringRef(); + _rawString = QStringRef(); _tokenKind = scanToken(); _tokenLength = _codePtr - _tokenStartPtr - 1; @@ -821,12 +830,15 @@ int Lexer::scanString(ScanStringMode mode) QChar quote = (mode == TemplateContinuation) ? QChar(TemplateHead) : QChar(mode); bool multilineStringLiteral = false; - const QChar *startCode = _codePtr; + const QChar *startCode = _codePtr - 1; + // in case we just parsed a \r, we need to reset this flag to get things working + // correctly in the loop below and afterwards + _skipLinefeed = false; if (_engine) { while (_codePtr <= _endPtr) { - if (isLineTerminator() && quote != QLatin1Char('`')) { - if (qmlMode()) + if (isLineTerminator()) { + if ((quote == QLatin1Char('`') || qmlMode())) break; _errorCode = IllegalCharacter; _errorMessage = QCoreApplication::translate("QQmlParser", "Stray newline in string literal"); @@ -836,7 +848,8 @@ int Lexer::scanString(ScanStringMode mode) } else if (_char == '$' && quote == QLatin1Char('`')) { break; } else if (_char == quote) { - _tokenSpell = _engine->midRef(startCode - _code.unicode() - 1, _codePtr - startCode); + _tokenSpell = _engine->midRef(startCode - _code.unicode(), _codePtr - startCode - 1); + _rawString = _tokenSpell; scanChar(); if (quote == QLatin1Char('`')) @@ -849,28 +862,36 @@ int Lexer::scanString(ScanStringMode mode) else return T_STRING_LITERAL; } - scanChar(); + // don't use scanChar() here, that would transform \r sequences and the midRef() call would create the wrong result + _char = *_codePtr++; + ++_currentColumnNumber; } } + // rewind by one char, so things gets scanned correctly + --_codePtr; + _validTokenText = true; - _tokenText.resize(0); - startCode--; - while (startCode != _codePtr - 1) - _tokenText += *startCode++; + _tokenText = QString(startCode, _codePtr - startCode); + + auto setRawString = [&](const QChar *end) { + QString raw(startCode, end - startCode - 1); + raw.replace(QLatin1String("\r\n"), QLatin1String("\n")); + raw.replace(QLatin1Char('\r'), QLatin1Char('\n')); + _rawString = _engine->newStringRef(raw); + }; + + scanChar(); while (_codePtr <= _endPtr) { - if (unsigned sequenceLength = isLineTerminatorSequence()) { - multilineStringLiteral = true; - _tokenText += _char; - if (sequenceLength == 2) - _tokenText += *_codePtr; - scanChar(); - } else if (_char == quote) { + if (_char == quote) { scanChar(); - if (_engine) + if (_engine) { _tokenSpell = _engine->newStringRef(_tokenText); + if (quote == QLatin1Char('`')) + setRawString(_codePtr - 1); + } if (quote == QLatin1Char('`')) _bracesCount = _outerTemplateBraceCount.pop(); @@ -885,8 +906,10 @@ int Lexer::scanString(ScanStringMode mode) scanChar(); scanChar(); _bracesCount = 1; - if (_engine) + if (_engine) { _tokenSpell = _engine->newStringRef(_tokenText); + setRawString(_codePtr - 2); + } return (mode == TemplateHead ? T_TEMPLATE_HEAD : T_TEMPLATE_MIDDLE); } else if (_char == QLatin1Char('\\')) { diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h index 644c5c09aa..03f33f6e06 100644 --- a/src/qml/parser/qqmljslexer_p.h +++ b/src/qml/parser/qqmljslexer_p.h @@ -161,6 +161,7 @@ public: int tokenStartColumn() const { return _tokenColumn; } inline QStringRef tokenSpell() const { return _tokenSpell; } + inline QStringRef rawString() const { return _rawString; } double tokenValue() const { return _tokenValue; } QString tokenText() const; @@ -213,6 +214,7 @@ private: QString _tokenText; QString _errorMessage; QStringRef _tokenSpell; + QStringRef _rawString; const QChar *_codePtr; const QChar *_endPtr; @@ -248,6 +250,7 @@ private: bool _followsClosingBrace; bool _delimited; bool _qmlMode; + bool _skipLinefeed = false; int _generatorLevel = 0; bool _staticIsKeyword = false; }; diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/parser/qqmljsmemorypool_p.h index 9a480f1224..afd0f809da 100644 --- a/src/qml/parser/qqmljsmemorypool_p.h +++ b/src/qml/parser/qqmljsmemorypool_p.h @@ -88,7 +88,7 @@ public: inline void *allocate(size_t size) { - size = (size + 7) & ~7; + size = (size + 7) & ~size_t(7); if (Q_LIKELY(_ptr && (_ptr + size < _end))) { void *addr = _ptr; _ptr += size; @@ -113,7 +113,9 @@ public: private: Q_NEVER_INLINE void *allocate_helper(size_t size) { - Q_ASSERT(size < BLOCK_SIZE); + size_t currentBlockSize = DEFAULT_BLOCK_SIZE; + while (Q_UNLIKELY(size >= currentBlockSize)) + currentBlockSize *= 2; if (++_blockCount == _allocatedBlocks) { if (! _allocatedBlocks) @@ -121,7 +123,7 @@ private: else _allocatedBlocks *= 2; - _blocks = (char **) realloc(_blocks, sizeof(char *) * _allocatedBlocks); + _blocks = reinterpret_cast<char **>(realloc(_blocks, sizeof(char *) * size_t(_allocatedBlocks))); Q_CHECK_PTR(_blocks); for (int index = _blockCount; index < _allocatedBlocks; ++index) @@ -131,12 +133,12 @@ private: char *&block = _blocks[_blockCount]; if (! block) { - block = (char *) malloc(BLOCK_SIZE); + block = reinterpret_cast<char *>(malloc(currentBlockSize)); Q_CHECK_PTR(block); } _ptr = block; - _end = _ptr + BLOCK_SIZE; + _end = _ptr + currentBlockSize; void *addr = _ptr; _ptr += size; @@ -153,7 +155,7 @@ private: enum { - BLOCK_SIZE = 8 * 1024, + DEFAULT_BLOCK_SIZE = 8 * 1024, DEFAULT_BLOCK_COUNT = 8 }; }; diff --git a/src/qml/qml.pro b/src/qml/qml.pro index a76a87b153..db59140f06 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -7,7 +7,7 @@ qtConfig(qml-network): \ DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES msvc:equals(QT_ARCH, i386): QMAKE_LFLAGS += /BASE:0x66000000 -win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS +msvc:DEFINES *= _CRT_SECURE_NO_WARNINGS win32:!winrt:LIBS += -lshell32 solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 @@ -47,7 +47,7 @@ exists("qqml_enable_gcov") { } # QTBUG-55238, disable new optimizer for MSVC 2015/Update 3. -release:win32-msvc*:equals(QT_CL_MAJOR_VERSION, 19):equals(QT_CL_MINOR_VERSION, 00): \ +release:msvc:equals(QT_CL_MAJOR_VERSION, 19):equals(QT_CL_MINOR_VERSION, 00): \ greaterThan(QT_CL_PATCH_VERSION, 24212):QMAKE_CXXFLAGS += -d2SSAOptimizer- QMAKE_DOCS = $$PWD/doc/qtqml.qdocconf diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index f288646ec7..c400e9239b 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -755,6 +755,9 @@ void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) d->context = nullptr; } + if (d->outerContext && d->outerContext->contextObject == o) + d->outerContext->contextObject = nullptr; + // Mark this object as in the process of deletion to // prevent it resolving in bindings QQmlData::markAsDeleted(o); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index bc53b98b5b..e379d416fd 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -511,6 +511,20 @@ QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSi findCompositeSingletons(set, compositeSingletons, baseUrl()); } + std::stable_sort(compositeSingletons.begin(), compositeSingletons.end(), + [](const QQmlImports::CompositeSingletonReference &lhs, + const QQmlImports::CompositeSingletonReference &rhs) { + if (lhs.prefix != rhs.prefix) + return lhs.prefix < rhs.prefix; + + if (lhs.typeName != rhs.typeName) + return lhs.typeName < rhs.typeName; + + return lhs.majorVersion != rhs.majorVersion + ? lhs.majorVersion < rhs.majorVersion + : lhs.minorVersion < rhs.minorVersion; + }); + return compositeSingletons; } @@ -744,8 +758,10 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt if (majversion >= 0 && minversion >= 0) { QQmlType t = QQmlMetaType::qmlType(type, uri, majversion, minversion); if (t.isValid()) { - if (vmajor) *vmajor = majversion; - if (vminor) *vminor = minversion; + if (vmajor) + *vmajor = majversion; + if (vminor) + *vminor = minversion; if (type_return) *type_return = t; return true; @@ -804,10 +820,13 @@ bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedSt if (candidate != end) { if (!base) // ensure we have a componentUrl componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName); - int major = vmajor ? *vmajor : -1; - int minor = vminor ? *vminor : -1; QQmlType returnType = fetchOrCreateTypeForUrl(componentUrl, type, isCompositeSingleton, - nullptr, major, minor); + nullptr, candidate->majorVersion, + candidate->minorVersion); + if (vmajor) + *vmajor = candidate->majorVersion; + if (vminor) + *vminor = candidate->minorVersion; if (type_return) *type_return = returnType; return returnType.isValid(); diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 9f2a96d5d9..380163202a 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -408,7 +408,7 @@ QQmlJavaScriptExpression::evalFunction(QQmlContextData *ctxt, QObject *scopeObje QV4::Scope scope(v4); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxt, scopeObject)); - QV4::Script script(v4, qmlContext, code, filename, line); + QV4::Script script(v4, qmlContext, /*parse as QML binding*/true, code, filename, line); QV4::ScopedValue result(scope); script.parse(); if (!v4->hasException) @@ -438,7 +438,7 @@ void QQmlJavaScriptExpression::createQmlBinding(QQmlContextData *ctxt, QObject * QV4::Scope scope(v4); QV4::Scoped<QV4::QmlContext> qmlContext(scope, QV4::QmlContext::create(v4->rootContext(), ctxt, qmlScope)); - QV4::Script script(v4, qmlContext, code, filename, line); + QV4::Script script(v4, qmlContext, /*parse as QML binding*/true, code, filename, line); script.parse(); if (v4->hasException) { QQmlDelayedError *error = delayedError(); diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index ba8d5831ad..11806a89a0 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -2244,6 +2244,9 @@ QQmlMetaType::TypeCategory QQmlMetaType::typeCategory(int userType) return Unknown; } +/*! + See qmlRegisterInterface() for information about when this will return true. +*/ bool QQmlMetaType::isInterface(int userType) { QMutexLocker lock(metaTypeDataLock()); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 0388215630..f91ba78932 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -889,28 +889,63 @@ struct StaticQtMetaObject : public QObject { return &staticQtMetaObject; } }; -static int EnumType(const QMetaObject *metaobj, const QByteArray &str, int type) +static bool isNamedEnumerator(const QMetaObject *metaObj, const QByteArray &scopedName) { QByteArray scope; QByteArray name; - int scopeIdx = str.lastIndexOf("::"); + int scopeIdx = scopedName.lastIndexOf("::"); if (scopeIdx != -1) { - scope = str.left(scopeIdx); - name = str.mid(scopeIdx + 2); + scope = scopedName.left(scopeIdx); + name = scopedName.mid(scopeIdx + 2); } else { - name = str; + name = scopedName; } const QMetaObject *meta; if (scope == "Qt") meta = StaticQtMetaObject::get(); else - meta = metaobj; + meta = metaObj; for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { QMetaEnum m = meta->enumerator(i); if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) - return QVariant::Int; + return true; } - return type; + return false; +} + +static bool passTypeAsInt(int type) +{ + // We should not encounter the unknown type here. + // In order to check that we need extra information. + Q_ASSERT(type != QMetaType::UnknownType); + + const QMetaType::TypeFlags flags = QMetaType::typeFlags(type); + + // Cast enumerations to int. + if (flags & QMetaType::IsEnumeration) + return true; + + // Qt builtins can be handled as they are. + if (type < int(QMetaType::User)) + return false; + + // Pointers to QObjects and QGadgets can be handled as they are. + if (flags & (QMetaType::PointerToQObject | QMetaType::PointerToGadget)) + return false; + + // If it wasn't declared as metatype, better don't touch it. + if (!(flags & QMetaType::WasDeclaredAsMetaType)) + return false; + + // If it needs construction or destruction (that is, it is a structured type), + // pass as original type. + if (flags & (QMetaType::NeedsConstruction | QMetaType::NeedsDestruction)) + return false; + + // A single value that's not a pointer to a QObject or QGadget, not a builtin type, was declared + // as meta type, but we don't know it as an enumeration (although it probably is one). + // Pass as int if it fits into an int. + return QMetaType::sizeOf(type) <= int(sizeof(int)); } QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names) @@ -1614,18 +1649,13 @@ int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *u propTypeName = m.typeName(); } - QMetaType::TypeFlags flags = QMetaType::typeFlags(type); - if (flags & QMetaType::IsEnumeration) { - type = QVariant::Int; - } else if (type == QMetaType::UnknownType || - (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) && - type != qMetaTypeId<QJSValue>())) { - //the UserType clause is to catch registered QFlags - type = EnumType(metaObject(), propTypeName, type); - } - if (type == QMetaType::UnknownType) { - if (unknownTypeError) *unknownTypeError = propTypeName; + if (isNamedEnumerator(metaObject(), propTypeName)) + type = QVariant::Int; + else if (unknownTypeError) + *unknownTypeError = propTypeName; + } else if (passTypeAsInt(type)) { + type = QVariant::Int; } return type; @@ -1665,20 +1695,18 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, for (int ii = 0; ii < argc; ++ii) { int type = m.parameterType(ii); - QMetaType::TypeFlags flags = QMetaType::typeFlags(type); - if (flags & QMetaType::IsEnumeration) - type = QVariant::Int; - else if (type == QMetaType::UnknownType || - (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) && - type != qMetaTypeId<QJSValue>())) { - //the UserType clause is to catch registered QFlags + if (type == QMetaType::UnknownType) { if (argTypeNames.isEmpty()) argTypeNames = m.parameterTypes(); - type = EnumType(metaObject, argTypeNames.at(ii), type); - } - if (type == QMetaType::UnknownType) { - if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); - return nullptr; + if (isNamedEnumerator(metaObject, argTypeNames.at(ii))) { + type = QVariant::Int; + } else { + if (unknownTypeError) + *unknownTypeError = argTypeNames.at(ii); + return nullptr; + } + } else if (passTypeAsInt(type)) { + type = QVariant::Int; } args->arguments[ii + 1] = type; } @@ -1704,20 +1732,18 @@ int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage * for (int ii = 0; ii < argc; ++ii) { int type = m.parameterType(ii); - QMetaType::TypeFlags flags = QMetaType::typeFlags(type); - if (flags & QMetaType::IsEnumeration) - type = QVariant::Int; - else if (type == QMetaType::UnknownType || - (type >= (int)QVariant::UserType && !(flags & QMetaType::PointerToQObject) && - type != qMetaTypeId<QJSValue>())) { - //the UserType clause is to catch registered QFlags) + if (type == QMetaType::UnknownType) { if (argTypeNames.isEmpty()) argTypeNames = m.parameterTypes(); - type = EnumType(_m.asT2(), argTypeNames.at(ii), type); - } - if (type == QMetaType::UnknownType) { - if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); - return nullptr; + if (isNamedEnumerator(_m.asT2(), argTypeNames.at(ii))) { + type = QVariant::Int; + } else { + if (unknownTypeError) + *unknownTypeError = argTypeNames.at(ii); + return nullptr; + } + } else if (passTypeAsInt(type)) { + type = QVariant::Int; } argStorage->operator[](ii + 1) = type; } diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 7480475ca7..9df502f778 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2723,10 +2723,6 @@ void QQmlTypeData::resolveTypes() } } - std::stable_sort(m_compositeSingletons.begin(), m_compositeSingletons.end(), [](const TypeReference &lhs, const TypeReference &rhs){ - return lhs.qualifiedName() < rhs.qualifiedName(); - }); - for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd(); unresolvedRef != end; ++unresolvedRef) { @@ -3289,14 +3285,7 @@ QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const if (hasInlineSourceCode) return QDateTime(); - QDateTime timeStamp = fileInfo.lastModified(); - if (timeStamp.isValid()) - return timeStamp; - - static QDateTime appTimeStamp; - if (!appTimeStamp.isValid()) - appTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); - return appTimeStamp; + return fileInfo.lastModified(); } bool QQmlDataBlob::SourceCodeData::exists() const diff --git a/src/qmldebug/qqmlprofilerclient.cpp b/src/qmldebug/qqmlprofilerclient.cpp index 73db2ad94d..5477af89d4 100644 --- a/src/qmldebug/qqmlprofilerclient.cpp +++ b/src/qmldebug/qqmlprofilerclient.cpp @@ -95,11 +95,16 @@ int QQmlProfilerClientPrivate::resolveStackTop() void QQmlProfilerClientPrivate::forwardEvents(const QQmlProfilerEvent &last) { + forwardDebugMessages(last.timestamp()); + eventReceiver->addEvent(last); +} + +void QQmlProfilerClientPrivate::forwardDebugMessages(qint64 untilTimestamp) +{ while (!pendingDebugMessages.isEmpty() - && pendingDebugMessages.front().timestamp() <= last.timestamp()) { + && pendingDebugMessages.front().timestamp() <= untilTimestamp) { eventReceiver->addEvent(pendingDebugMessages.dequeue()); } - eventReceiver->addEvent(last); } void QQmlProfilerClientPrivate::processCurrentEvent() @@ -142,7 +147,7 @@ void QQmlProfilerClientPrivate::processCurrentEvent() int typeIndex = resolveType(currentEvent); currentEvent.event.setTypeIndex(typeIndex); if (rangesInProgress.isEmpty()) - eventReceiver->addEvent(currentEvent.event); + forwardEvents(currentEvent.event); else pendingMessages.enqueue(currentEvent.event); break; @@ -228,8 +233,7 @@ void QQmlProfilerClientPrivate::finalize() currentEvent.event.setTimestamp(maximumTime); processCurrentEvent(); } - while (!pendingDebugMessages.isEmpty()) - eventReceiver->addEvent(pendingDebugMessages.dequeue()); + forwardDebugMessages(std::numeric_limits<qint64>::max()); } @@ -345,12 +349,14 @@ void QQmlProfilerClient::messageReceived(const QByteArray &data) && d->currentEvent.type.detailType() == StartTrace) { const QList<int> engineIds = d->currentEvent.event.numbers<QList<int>, qint32>(); d->trackedEngines.append(engineIds); + d->forwardDebugMessages(d->currentEvent.event.timestamp()); emit traceStarted(d->currentEvent.event.timestamp(), engineIds); } else if (d->currentEvent.type.message() == Event && d->currentEvent.type.detailType() == EndTrace) { const QList<int> engineIds = d->currentEvent.event.numbers<QList<int>, qint32>(); for (int engineId : engineIds) d->trackedEngines.removeAll(engineId); + d->forwardDebugMessages(d->currentEvent.event.timestamp()); emit traceFinished(d->currentEvent.event.timestamp(), engineIds); } else if (d->updateFeatures(d->currentEvent.type.feature())) { d->processCurrentEvent(); diff --git a/src/qmldebug/qqmlprofilerclient_p_p.h b/src/qmldebug/qqmlprofilerclient_p_p.h index df73209858..52d42eae82 100644 --- a/src/qmldebug/qqmlprofilerclient_p_p.h +++ b/src/qmldebug/qqmlprofilerclient_p_p.h @@ -86,6 +86,7 @@ public: int resolveType(const QQmlProfilerTypedEvent &type); int resolveStackTop(); void forwardEvents(const QQmlProfilerEvent &last); + void forwardDebugMessages(qint64 untilTimestamp); void processCurrentEvent(); void finalize(); diff --git a/src/qmltest/doc/src/qtquicktest-index.qdoc b/src/qmltest/doc/src/qtquicktest-index.qdoc index 0a89066d47..15ea33d06a 100644 --- a/src/qmltest/doc/src/qtquicktest-index.qdoc +++ b/src/qmltest/doc/src/qtquicktest-index.qdoc @@ -181,6 +181,8 @@ class Setup : public QObject { + Q_OBJECT + public: Setup() {} @@ -196,6 +198,15 @@ #include "tst_mytest.moc" \endcode + The \c .moc include is based on the file name of the \c .cpp file. + For example, in the example above, the \c .cpp file is named + \c tst_mytest.cpp. If the file was named \c MyTest.cpp, the include would + be: + + \code + #include "MyTest.moc" + \endcode + \section1 Licenses Qt Quick Tests is available under commercial licenses from \l{The Qt Company}. diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp index 3b854dfccd..3225dc95cd 100644 --- a/src/qmltest/quicktestresult.cpp +++ b/src/qmltest/quicktestresult.cpp @@ -381,6 +381,16 @@ QStringList QuickTestResult::functionsToRun() const } /*! + \qmlproperty list<string> TestResult::tagsToRun + + This property returns the list of test function's data tags to be run +*/ +QStringList QuickTestResult::tagsToRun() const +{ + return QTest::testTags; +} + +/*! \qmlmethod TestResult::reset() Resets all pass/fail/skip counters and prepare for testing. diff --git a/src/qmltest/quicktestresult_p.h b/src/qmltest/quicktestresult_p.h index f222cd3e87..b2eeefdfff 100644 --- a/src/qmltest/quicktestresult_p.h +++ b/src/qmltest/quicktestresult_p.h @@ -76,6 +76,7 @@ class Q_QUICK_TEST_EXPORT QuickTestResult : public QObject Q_PROPERTY(int failCount READ failCount) Q_PROPERTY(int skipCount READ skipCount) Q_PROPERTY(QStringList functionsToRun READ functionsToRun) + Q_PROPERTY(QStringList tagsToRun READ tagsToRun) public: QuickTestResult(QObject *parent = nullptr); ~QuickTestResult() override; @@ -107,6 +108,7 @@ public: int skipCount() const; QStringList functionsToRun() const; + QStringList tagsToRun() const; public Q_SLOTS: void reset(); diff --git a/src/quick/designer/qquickdesignersupport.cpp b/src/quick/designer/qquickdesignersupport.cpp index 1851c25a77..70b568800d 100644 --- a/src/quick/designer/qquickdesignersupport.cpp +++ b/src/quick/designer/qquickdesignersupport.cpp @@ -383,13 +383,20 @@ void QQuickDesignerSupport::resetAnchor(QQuickItem *item, const QString &name) } } -void QQuickDesignerSupport::emitComponentCompleteSignalForAttachedProperty(QQuickItem *item) +void QQuickDesignerSupport::emitComponentCompleteSignalForAttachedProperty(QObject *object) { - QQmlData *data = QQmlData::get(item); + if (!object) + return; + + QQmlData *data = QQmlData::get(object); if (data && data->context) { QQmlComponentAttached *componentAttached = data->context->componentAttached; - if (componentAttached) { - emit componentAttached->completed(); + while (componentAttached) { + if (componentAttached->parent()) + if (componentAttached->parent() == object) + emit componentAttached->completed(); + + componentAttached = componentAttached->next; } } } diff --git a/src/quick/designer/qquickdesignersupport_p.h b/src/quick/designer/qquickdesignersupport_p.h index 6628e404bd..fc46745e15 100644 --- a/src/quick/designer/qquickdesignersupport_p.h +++ b/src/quick/designer/qquickdesignersupport_p.h @@ -126,7 +126,7 @@ public: static QQuickItem *anchorCenterInTargetItem(QQuickItem *item); static QPair<QString, QObject*> anchorLineTarget(QQuickItem *item, const QString &name, QQmlContext *context); static void resetAnchor(QQuickItem *item, const QString &name); - static void emitComponentCompleteSignalForAttachedProperty(QQuickItem *item); + static void emitComponentCompleteSignalForAttachedProperty(QObject *item); static QList<QObject*> statesForItem(QQuickItem *item); diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index d1a8bbd901..e614b1bd6d 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -723,5 +723,6 @@ QML_DECLARE_TYPE(QQuickCloseEvent) QML_DECLARE_TYPE(QQuickPointerDevice) QML_DECLARE_TYPE(QPointingDeviceUniqueId) QML_DECLARE_TYPE(QQuickPointerEvent) +Q_DECLARE_METATYPE(QQuickEventPoint::GrabTransition) #endif // QQUICKEVENTS_P_P_H diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp index d51419a275..f32b32491b 100644 --- a/src/quick/items/qquickopenglshadereffectnode.cpp +++ b/src/quick/items/qquickopenglshadereffectnode.cpp @@ -366,6 +366,7 @@ class QQuickOpenGLShaderEffectMaterialCache : public QObject public: static QQuickOpenGLShaderEffectMaterialCache *get(bool create = true) { QOpenGLContext *ctx = QOpenGLContext::currentContext(); + Q_ASSERT(ctx); QQuickOpenGLShaderEffectMaterialCache *me = ctx->findChild<QQuickOpenGLShaderEffectMaterialCache *>(QStringLiteral("__qt_ShaderEffectCache"), Qt::FindDirectChildrenOnly); if (!me && create) { me = new QQuickOpenGLShaderEffectMaterialCache(); diff --git a/src/quick/items/qquickpathview.cpp b/src/quick/items/qquickpathview.cpp index e7e19b041e..77ed8a659c 100644 --- a/src/quick/items/qquickpathview.cpp +++ b/src/quick/items/qquickpathview.cpp @@ -2403,7 +2403,11 @@ void QQuickPathViewPrivate::snapToIndex(int index, MovementReason reason) const int duration = highlightMoveDuration; - if (!duration) { + const qreal count = pathItems == -1 ? modelCount : qMin(pathItems, modelCount); + const qreal averageItemLength = path->path().length() / count; + const qreal threshold = 0.5 / averageItemLength; // if we are within .5 px, we want to immediately assign rather than animate + + if (!duration || qAbs(offset - targetOffset) < threshold || (qFuzzyIsNull(targetOffset) && qAbs(modelCount - offset) < threshold)) { tl.set(moveOffset, targetOffset); } else if (moveDirection == QQuickPathView::Positive || (moveDirection == QQuickPathView::Shortest && targetOffset - offset > modelCount/2.0)) { qreal distance = modelCount - targetOffset + offset; diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index 025acefec2..f6d4e7ed49 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -192,7 +192,8 @@ void QQuickRenderControlPrivate::windowDestroyed() QQuickWindowPrivate::get(window)->animationController = nullptr; #if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) - QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); + if (QOpenGLContext::currentContext()) + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); #endif window = nullptr; diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 44c16e2d7e..eaf0e4cf89 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -314,7 +314,7 @@ */ /*! - \qmlmethod real QtQuick::TableView::forceLayout + \qmlmethod QtQuick::TableView::forceLayout Responding to changes in the model are batched so that they are handled only once per frame. This means the TableView delays showing any changes @@ -1647,8 +1647,9 @@ void QQuickTableViewPrivate::connectToModel() QObjectPrivate::connect(model, &QQmlInstanceModel::initItem, this, &QQuickTableViewPrivate::initItemCallback); if (tableModel) { - QObjectPrivate::connect(tableModel, &QQmlTableInstanceModel::itemPooled, this, &QQuickTableViewPrivate::itemPooledCallback); - QObjectPrivate::connect(tableModel, &QQmlTableInstanceModel::itemReused, this, &QQuickTableViewPrivate::itemReusedCallback); + const auto tm = tableModel.data(); + QObjectPrivate::connect(tm, &QQmlTableInstanceModel::itemPooled, this, &QQuickTableViewPrivate::itemPooledCallback); + QObjectPrivate::connect(tm, &QQmlTableInstanceModel::itemReused, this, &QQuickTableViewPrivate::itemReusedCallback); } if (auto const aim = model->abstractItemModel()) { @@ -1678,8 +1679,9 @@ void QQuickTableViewPrivate::disconnectFromModel() QObjectPrivate::disconnect(model, &QQmlInstanceModel::initItem, this, &QQuickTableViewPrivate::initItemCallback); if (tableModel) { - QObjectPrivate::disconnect(tableModel, &QQmlTableInstanceModel::itemPooled, this, &QQuickTableViewPrivate::itemPooledCallback); - QObjectPrivate::disconnect(tableModel, &QQmlTableInstanceModel::itemReused, this, &QQuickTableViewPrivate::itemReusedCallback); + const auto tm = tableModel.data(); + QObjectPrivate::disconnect(tm, &QQmlTableInstanceModel::itemPooled, this, &QQuickTableViewPrivate::itemPooledCallback); + QObjectPrivate::disconnect(tm, &QQmlTableInstanceModel::itemReused, this, &QQuickTableViewPrivate::itemReusedCallback); } if (auto const aim = model->abstractItemModel()) { diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 3cce30aaf6..4d4540bc36 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -343,6 +343,19 @@ void QQuickTextPrivate::updateBaseline(qreal baseline, qreal dy) q->setBaselineOffset(baseline + yoff + q->topPadding()); } +void QQuickTextPrivate::signalSizeChange(const QSizeF &previousSize) +{ + Q_Q(QQuickText); + + if (layedOutTextRect.size() != previousSize) { + emit q->contentSizeChanged(); + if (layedOutTextRect.width() != previousSize.width()) + emit q->contentWidthChanged(layedOutTextRect.width()); + if (layedOutTextRect.height() != previousSize.height()) + emit q->contentHeightChanged(layedOutTextRect.height()); + } +} + void QQuickTextPrivate::updateSize() { Q_Q(QQuickText); @@ -363,6 +376,8 @@ void QQuickTextPrivate::updateSize() qreal hPadding = q->leftPadding() + q->rightPadding(); qreal vPadding = q->topPadding() + q->bottomPadding(); + const QSizeF previousSize = layedOutTextRect.size(); + if (text.isEmpty() && !isLineLaidOutConnected() && fontSizeMode() == QQuickText::FixedSize) { // How much more expensive is it to just do a full layout on an empty string here? // There may be subtle differences in the height and baseline calculations between @@ -379,14 +394,13 @@ void QQuickTextPrivate::updateSize() q->setImplicitSize(hPadding, fontHeight + vPadding); layedOutTextRect = QRectF(0, 0, 0, fontHeight); advance = QSizeF(); - emit q->contentSizeChanged(); + signalSizeChange(previousSize); updateType = UpdatePaintNode; q->update(); return; } QSizeF size(0, 0); - QSizeF previousSize = layedOutTextRect.size(); //setup instance of QTextLayout for all cases other than richtext if (!richText) { @@ -483,13 +497,7 @@ void QQuickTextPrivate::updateSize() } } - - if (layedOutTextRect.size() != previousSize) - emit q->contentSizeChanged(); - if (layedOutTextRect.width() != previousSize.width()) - emit q->contentWidthChanged(layedOutTextRect.width()); - if (layedOutTextRect.height() != previousSize.height()) - emit q->contentHeightChanged(layedOutTextRect.height()); + signalSizeChange(previousSize); updateType = UpdatePaintNode; q->update(); } diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index fd26d966c8..efa45e0958 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -75,6 +75,7 @@ public: void updateBaseline(qreal baseline, qreal dy); void updateSize(); + void signalSizeChange(const QSizeF &previousSize); void updateLayout(); bool determineHorizontalAlignment(); bool setHAlign(QQuickText::HAlignment, bool forceAlign = false); diff --git a/src/quick/items/qquicktextedit.cpp b/src/quick/items/qquicktextedit.cpp index bfc9f4c769..06a0fc396b 100644 --- a/src/quick/items/qquicktextedit.cpp +++ b/src/quick/items/qquicktextedit.cpp @@ -2044,11 +2044,22 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * int firstDirtyPos = 0; if (nodeIterator != d->textNodeMap.end()) { firstDirtyPos = nodeIterator->startPos(); + // ### this could be optimized if the first and last dirty nodes are not connected + // as the intermediate text nodes would usually only need to be transformed differently. + int lastDirtyPos = firstDirtyPos; + auto it = d->textNodeMap.constEnd(); + while (it != nodeIterator) { + --it; + if (it->dirty()) { + lastDirtyPos = it->startPos(); + break; + } + } do { rootNode->removeChildNode(nodeIterator->textNode()); delete nodeIterator->textNode(); nodeIterator = d->textNodeMap.erase(nodeIterator); - } while (nodeIterator != d->textNodeMap.end() && nodeIterator->dirty()); + } while (nodeIterator != d->textNodeMap.constEnd() && nodeIterator->startPos() <= lastDirtyPos); } // FIXME: the text decorations could probably be handled separately (only updated for affected textFrames) @@ -2090,7 +2101,7 @@ QSGNode *QQuickTextEdit::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData * QTextCharFormat format = a->formatAccessor(pos); QTextBlock block = textFrame->firstCursorPosition().block(); engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position())); - engine.addTextObject(QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document, + engine.addTextObject(block, QPointF(0, 0), format, QQuickTextNodeEngine::Unselected, d->document, pos, textFrame->frameFormat().position()); nodeStart = pos; } else { diff --git a/src/quick/items/qquicktextnode.cpp b/src/quick/items/qquicktextnode.cpp index 13a8219cbd..0dd12207b7 100644 --- a/src/quick/items/qquicktextnode.cpp +++ b/src/quick/items/qquicktextnode.cpp @@ -205,7 +205,7 @@ void QQuickTextNode::addTextDocument(const QPointF &position, QTextDocument *tex QTextBlock block = textFrame->firstCursorPosition().block(); engine.setCurrentLine(block.layout()->lineForTextPosition(pos - block.position())); - engine.addTextObject(rect.topLeft(), format, QQuickTextNodeEngine::Unselected, textDocument, + engine.addTextObject(block, rect.topLeft(), format, QQuickTextNodeEngine::Unselected, textDocument, pos, textFrame->frameFormat().position()); } else { QTextFrame::iterator it = textFrame->begin(); diff --git a/src/quick/items/qquicktextnodeengine.cpp b/src/quick/items/qquicktextnodeengine.cpp index a53ca2a2a4..504d629b3e 100644 --- a/src/quick/items/qquicktextnodeengine.cpp +++ b/src/quick/items/qquicktextnodeengine.cpp @@ -423,10 +423,11 @@ void QQuickTextNodeEngine::addImage(const QRectF &rect, const QImage &image, qre QRectF searchRect = rect; if (layoutPosition == QTextFrameFormat::InFlow) { if (m_currentLineTree.isEmpty()) { + qreal y = m_currentLine.ascent() - ascent; if (m_currentTextDirection == Qt::RightToLeft) - searchRect.moveTopRight(m_position + m_currentLine.rect().topRight() + QPointF(0, 1)); + searchRect.moveTopRight(m_position + m_currentLine.rect().topRight() + QPointF(0, y)); else - searchRect.moveTopLeft(m_position + m_currentLine.position() + QPointF(0,1)); + searchRect.moveTopLeft(m_position + m_currentLine.position() + QPointF(0, y)); } else { const BinaryTreeNode *lastNode = m_currentLineTree.data() + m_currentLineTree.size() - 1; if (lastNode->glyphRun.isRightToLeft()) { @@ -443,7 +444,7 @@ void QQuickTextNodeEngine::addImage(const QRectF &rect, const QImage &image, qre m_hasContents = true; } -void QQuickTextNodeEngine::addTextObject(const QPointF &position, const QTextCharFormat &format, +void QQuickTextNodeEngine::addTextObject(const QTextBlock &block, const QPointF &position, const QTextCharFormat &format, SelectionState selectionState, QTextDocument *textDocument, int pos, QTextFrameFormat::Position layoutPosition) @@ -476,17 +477,23 @@ void QQuickTextNodeEngine::addTextObject(const QPointF &position, const QTextCha } qreal ascent; - QFontMetrics m(format.font()); + QTextLine line = block.layout()->lineForTextPosition(pos); switch (format.verticalAlignment()) { - case QTextCharFormat::AlignMiddle: - ascent = size.height() / 2 - 1; + case QTextCharFormat::AlignTop: + ascent = line.ascent(); break; - case QTextCharFormat::AlignBaseline: - ascent = size.height() - m.descent() - 1; + case QTextCharFormat::AlignMiddle: { + QFontMetrics m(format.font()); + ascent = (size.height() - m.xHeight()) / 2; + break; + } + case QTextCharFormat::AlignBottom: + ascent = size.height() - line.descent(); break; + case QTextCharFormat::AlignBaseline: default: - ascent = size.height() - 1; + ascent = size.height(); } addImage(QRectF(position, size), image, ascent, selectionState, layoutPosition); @@ -1058,7 +1065,7 @@ void QQuickTextNodeEngine::addTextBlock(QTextDocument *textDocument, const QText ? QQuickTextNodeEngine::Selected : QQuickTextNodeEngine::Unselected; - addTextObject(QPointF(), charFormat, selectionState, textDocument, textPos); + addTextObject(block, QPointF(), charFormat, selectionState, textDocument, textPos); } textPos += text.length(); } else { diff --git a/src/quick/items/qquicktextnodeengine_p.h b/src/quick/items/qquicktextnodeengine_p.h index 18c624513a..49c1766045 100644 --- a/src/quick/items/qquicktextnodeengine_p.h +++ b/src/quick/items/qquicktextnodeengine_p.h @@ -179,7 +179,7 @@ public: const QVarLengthArray<QTextLayout::FormatRange> &colorChanges, int textPos, int fragmentEnd, int selectionStart, int selectionEnd); - void addTextObject(const QPointF &position, const QTextCharFormat &format, + void addTextObject(const QTextBlock &block, const QPointF &position, const QTextCharFormat &format, SelectionState selectionState, QTextDocument *textDocument, int pos, QTextFrameFormat::Position layoutPosition = QTextFrameFormat::InFlow); diff --git a/src/quick/quick.pro b/src/quick/quick.pro index e9a8b84b2a..0f5f5abca3 100644 --- a/src/quick/quick.pro +++ b/src/quick/quick.pro @@ -5,7 +5,7 @@ qtConfig(qml-network): \ QT_PRIVATE += network DEFINES += QT_NO_URL_CAST_FROM_STRING QT_NO_INTEGER_EVENT_COORDINATES -win32-msvc*:DEFINES *= _CRT_SECURE_NO_WARNINGS +msvc:DEFINES *= _CRT_SECURE_NO_WARNINGS solaris-cc*:QMAKE_CXXFLAGS_RELEASE -= -O2 win32:!winrt: LIBS += -luser32 diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp index 74426c5c4d..405e2ab100 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp @@ -99,13 +99,6 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin xTarget.resize(columns + 1); yTarget.resize(rows + 1); - bool oldAA = painter->testRenderHint(QPainter::Antialiasing); - if (painter->paintEngine()->type() != QPaintEngine::OpenGL - && painter->paintEngine()->type() != QPaintEngine::OpenGL2 - && oldAA && painter->combinedTransform().type() != QTransform::TxNone) { - painter->setRenderHint(QPainter::Antialiasing, false); - } - xTarget[0] = targetRect.left(); xTarget[1] = targetCenterLeft; xTarget[columns - 1] = targetCenterRight; @@ -311,9 +304,6 @@ void qDrawBorderPixmap(QPainter *painter, const QRect &targetRect, const QMargin painter->drawPixmapFragments(opaqueData.data(), opaqueData.size(), pixmap, QPainter::OpaqueHint); if (translucentData.size()) painter->drawPixmapFragments(translucentData.data(), translucentData.size(), pixmap); - - if (oldAA) - painter->setRenderHint(QPainter::Antialiasing, true); } } // QSGSoftwareHelpers namespace @@ -464,6 +454,8 @@ static Qt::TileRule getTileRule(qreal factor) void QSGSoftwareInternalImageNode::paint(QPainter *painter) { painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth); + // Disable antialiased clipping. It causes transformed tiles to have gaps. + painter->setRenderHint(QPainter::Antialiasing, false); const QPixmap &pm = m_mirror || m_textureIsLayer ? m_cachedMirroredPixmap : pixmap(); @@ -494,6 +486,7 @@ void QSGSoftwareInternalImageNode::paint(QPainter *painter) } } + QRectF QSGSoftwareInternalImageNode::rect() const { return m_targetRect; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp index 2c361e03e2..f50fa00b0b 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp @@ -121,7 +121,7 @@ void QSGSoftwareInternalRectangleNode::setGradientStops(const QGradientStops &st for (const QGradientStop &stop : qAsConst(stops)) { if (stop.first < 0.0 || stop.first > 1.0) { needsNormalization = true; - continue; + break; } } @@ -425,8 +425,11 @@ void QSGSoftwareInternalRectangleNode::generateCornerPixmap() { //Generate new corner Pixmap int radius = qFloor(qMin(qMin(m_rect.width(), m_rect.height()) * 0.5, m_radius)); + const auto width = qRound(radius * 2 * m_devicePixelRatio); + + if (m_cornerPixmap.width() != width) + m_cornerPixmap = QPixmap(width, width); - m_cornerPixmap = QPixmap(qRound(radius * 2 * m_devicePixelRatio), qRound(radius * 2 * m_devicePixelRatio)); m_cornerPixmap.setDevicePixelRatio(m_devicePixelRatio); m_cornerPixmap.fill(Qt::transparent); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp index 471624d3f8..bd0698be6c 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp @@ -99,6 +99,8 @@ void QSGSoftwareImageNode::paint(QPainter *painter) updateCachedMirroredPixmap(); painter->setRenderHint(QPainter::SmoothPixmapTransform, (m_filtering == QSGTexture::Linear)); + // Disable antialiased clipping. It causes transformed tiles to have gaps. + painter->setRenderHint(QPainter::Antialiasing, false); if (!m_cachedPixmap.isNull()) { painter->drawPixmap(m_rect, m_cachedPixmap, m_sourceRect); @@ -194,6 +196,9 @@ void QSGSoftwareNinePatchNode::update() void QSGSoftwareNinePatchNode::paint(QPainter *painter) { + // Disable antialiased clipping. It causes transformed tiles to have gaps. + painter->setRenderHint(QPainter::Antialiasing, false); + if (m_margins.isNull()) painter->drawPixmap(m_bounds, m_pixmap, QRectF(0, 0, m_pixmap.width(), m_pixmap.height())); else diff --git a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp index 03b48b4b8a..c434563c90 100644 --- a/src/quick/scenegraph/qsgbasicinternalimagenode.cpp +++ b/src/quick/scenegraph/qsgbasicinternalimagenode.cpp @@ -189,15 +189,30 @@ namespace { struct Y { float y, ty; }; } -static inline void appendQuad(quint16 **indices, quint16 topLeft, quint16 topRight, - quint16 bottomLeft, quint16 bottomRight) +static inline void appendQuad(int indexType, void **indexData, + int topLeft, int topRight, + int bottomLeft, int bottomRight) { - *(*indices)++ = topLeft; - *(*indices)++ = bottomLeft; - *(*indices)++ = bottomRight; - *(*indices)++ = bottomRight; - *(*indices)++ = topRight; - *(*indices)++ = topLeft; + if (indexType == QSGGeometry::UnsignedIntType) { + quint32 *indices = static_cast<quint32 *>(*indexData); + *indices++ = topLeft; + *indices++ = bottomLeft; + *indices++ = bottomRight; + *indices++ = bottomRight; + *indices++ = topRight; + *indices++ = topLeft; + *indexData = indices; + } else { + Q_ASSERT(indexType == QSGGeometry::UnsignedShortType); + quint16 *indices = static_cast<quint16 *>(*indexData); + *indices++ = topLeft; + *indices++ = bottomLeft; + *indices++ = bottomRight; + *indices++ = bottomRight; + *indices++ = topRight; + *indices++ = topLeft; + *indexData = indices; + } } QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, @@ -230,8 +245,6 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, ++vCells; if (innerTargetRect.bottom() != targetRect.bottom()) ++vCells; - if (hCells * vCells * 4 >= 0x10000) - qWarning("QTBUG-58924 - Too many tiles in QSGInternalImageNode, rendering will be partially missing."); QVarLengthArray<X, 32> xData(2 * hCells); QVarLengthArray<Y, 32> yData(2 * vCells); @@ -273,7 +286,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, float leftPlusRight = targetRect.left() + targetRect.right(); int count = xData.size(); xs = xData.data(); - for (int i = 0; i < count >> 1; ++i) + for (int i = 0; i < (count >> 1); ++i) qSwap(xs[i], xs[count - 1 - i]); for (int i = 0; i < count; ++i) xs[i].x = leftPlusRight - xs[i].x; @@ -311,16 +324,29 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, } Q_ASSERT(ys == yData.data() + yData.size()); + QSGGeometry::Type indexType = QSGGeometry::UnsignedShortType; + // We can handled up to 0xffff indices, but keep the limit lower here to + // merge better in the batch renderer. + if (hCells * vCells * 4 > 0x7fff) + indexType = QSGGeometry::UnsignedIntType; + if (antialiasing) { + if (!geometry || geometry->indexType() != indexType) { + geometry = new QSGGeometry(smoothAttributeSet(), + hCells * vCells * 4 + (hCells + vCells - 1) * 4, + hCells * vCells * 6 + (hCells + vCells) * 12, + indexType); + } else { + geometry->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, + hCells * vCells * 6 + (hCells + vCells) * 12); + } QSGGeometry *g = geometry; Q_ASSERT(g); - g->allocate(hCells * vCells * 4 + (hCells + vCells - 1) * 4, - hCells * vCells * 6 + (hCells + vCells) * 12); g->setDrawingMode(QSGGeometry::DrawTriangles); SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); memset(vertices, 0, g->vertexCount() * g->sizeOfVertex()); - quint16 *indices = g->indexDataAsUShort(); + void *indexData = g->indexData(); // The deltas are how much the fuzziness can reach into the image. // Only the border vertices are moved by the vertex shader, so the fuzziness @@ -348,7 +374,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, float delta = float(qAbs(targetRect.width()) < qAbs(targetRect.height()) ? targetRect.width() : targetRect.height()) * 0.5f; - quint16 index = 0; + int index = 0; ys = yData.data(); for (int j = 0; j < vCells; ++j, ys += 2) { xs = xData.data(); @@ -360,7 +386,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, SmoothVertex *v = vertices + index; - quint16 topLeft = index; + int topLeft = index; for (int k = (isTop || isLeft ? 2 : 1); k--; ++v, ++index) { v->x = xs[0].x; v->u = xs[0].tx; @@ -368,7 +394,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, v->v = ys[0].ty; } - quint16 topRight = index; + int topRight = index; for (int k = (isTop || isRight ? 2 : 1); k--; ++v, ++index) { v->x = xs[1].x; v->u = xs[1].tx; @@ -376,7 +402,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, v->v = ys[0].ty; } - quint16 bottomLeft = index; + int bottomLeft = index; for (int k = (isBottom || isLeft ? 2 : 1); k--; ++v, ++index) { v->x = xs[0].x; v->u = xs[0].tx; @@ -384,7 +410,7 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, v->v = ys[1].ty; } - quint16 bottomRight = index; + int bottomRight = index; for (int k = (isBottom || isRight ? 2 : 1); k--; ++v, ++index) { v->x = xs[1].x; v->u = xs[1].tx; @@ -392,45 +418,44 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, v->v = ys[1].ty; } - appendQuad(&indices, topLeft, topRight, bottomLeft, bottomRight); + appendQuad(g->indexType(), &indexData, topLeft, topRight, bottomLeft, bottomRight); if (isTop) { vertices[topLeft].dy = vertices[topRight].dy = topDy; vertices[topLeft].dv = vertices[topRight].dv = topDv; vertices[topLeft + 1].dy = vertices[topRight + 1].dy = -delta; - appendQuad(&indices, topLeft + 1, topRight + 1, topLeft, topRight); + appendQuad(g->indexType(), &indexData, topLeft + 1, topRight + 1, topLeft, topRight); } if (isBottom) { vertices[bottomLeft].dy = vertices[bottomRight].dy = -bottomDy; vertices[bottomLeft].dv = vertices[bottomRight].dv = -bottomDv; vertices[bottomLeft + 1].dy = vertices[bottomRight + 1].dy = delta; - appendQuad(&indices, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1); + appendQuad(g->indexType(), &indexData, bottomLeft, bottomRight, bottomLeft + 1, bottomRight + 1); } if (isLeft) { vertices[topLeft].dx = vertices[bottomLeft].dx = leftDx; vertices[topLeft].du = vertices[bottomLeft].du = leftDu; vertices[topLeft + 1].dx = vertices[bottomLeft + 1].dx = -delta; - appendQuad(&indices, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft); + appendQuad(g->indexType(), &indexData, topLeft + 1, topLeft, bottomLeft + 1, bottomLeft); } if (isRight) { vertices[topRight].dx = vertices[bottomRight].dx = -rightDx; vertices[topRight].du = vertices[bottomRight].du = -rightDu; vertices[topRight + 1].dx = vertices[bottomRight + 1].dx = delta; - appendQuad(&indices, topRight, topRight + 1, bottomRight, bottomRight + 1); + appendQuad(g->indexType(), &indexData, topRight, topRight + 1, bottomRight, bottomRight + 1); } } } Q_ASSERT(index == g->vertexCount()); - Q_ASSERT(indices - g->indexCount() == g->indexData()); } else { - if (!geometry) { + if (!geometry || geometry->indexType() != indexType) { geometry = new QSGGeometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), hCells * vCells * 4, hCells * vCells * 6, - QSGGeometry::UnsignedShortType); + indexType); } else { geometry->allocate(hCells * vCells * 4, hCells * vCells * 6); } @@ -453,10 +478,9 @@ QSGGeometry *QSGBasicInternalImageNode::updateGeometry(const QRectF &targetRect, vertices += 4; } } - - quint16 *indices = geometry->indexDataAsUShort(); + void *indexData = geometry->indexData(); for (int i = 0; i < 4 * vCells * hCells; i += 4) - appendQuad(&indices, i, i + 1, i + 2, i + 3); + appendQuad(geometry->indexType(), &indexData, i, i + 1, i + 2, i + 3); } return geometry; } @@ -515,6 +539,10 @@ void QSGBasicInternalImageNode::updateGeometry() if (m_antialiasing) { QSGGeometry *g = geometry(); Q_ASSERT(g != &m_geometry); + if (g->indexType() != QSGGeometry::UnsignedShortType) { + setGeometry(new QSGGeometry(smoothAttributeSet(), 0)); + g = geometry(); + } g->allocate(8, 14); g->setDrawingMode(QSGGeometry::DrawTriangleStrip); SmoothVertex *vertices = reinterpret_cast<SmoothVertex *>(g->vertexData()); @@ -549,10 +577,14 @@ void QSGBasicInternalImageNode::updateGeometry() QSGGeometry::updateTexturedRectGeometry(&m_geometry, m_targetRect, sr); } } else { - QSGGeometry *g = m_antialiasing ? geometry() : &m_geometry; - updateGeometry(m_targetRect, m_innerTargetRect, - sourceRect, innerSourceRect, m_subSourceRect, - g, m_mirror, m_antialiasing); + QSGGeometry *g = geometry(); + g = updateGeometry(m_targetRect, m_innerTargetRect, + sourceRect, innerSourceRect, m_subSourceRect, + g, m_mirror, m_antialiasing); + if (g != geometry()) { + setGeometry(g); + setFlag(OwnsGeometry, true); + } } } markDirty(DirtyGeometry); diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 3d579fde46..2e91bafa7c 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -334,7 +334,8 @@ void QSGGuiThreadRenderLoop::windowDestroyed(QQuickWindow *window) qCDebug(QSG_LOG_RENDERLOOP, "cleanup without an OpenGL context"); #if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) - QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); + if (current) + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); #endif d->cleanupNodesOnShutdown(); diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 8262708320..c18ba4226c 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -499,7 +499,8 @@ void QSGRenderThread::invalidateOpenGL(QQuickWindow *window, bool inDestructor, QQuickWindowPrivate *dd = QQuickWindowPrivate::get(window); #if QT_CONFIG(quick_shadereffect) - QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); + if (current) + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); #endif // The canvas nodes must be cleaned up regardless if we are in the destructor.. diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 3b2737b8e1..95df700a15 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -246,7 +246,8 @@ void QSGWindowsRenderLoop::windowDestroyed(QQuickWindow *window) RLDEBUG("cleanup without an OpenGL context"); #if QT_CONFIG(quick_shadereffect) && QT_CONFIG(opengl) - QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); + if (current) + QQuickOpenGLShaderEffectMaterial::cleanupMaterialCache(); #endif d->cleanupNodesOnShutdown(); |