aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/masm/yarr/YarrInterpreter.cpp8
-rw-r--r--src/imports/labsmodels/plugin.cpp1
-rw-r--r--src/imports/layouts/plugin.cpp1
-rw-r--r--src/imports/models/plugin.cpp1
-rw-r--r--src/imports/particles/plugin.cpp1
-rw-r--r--src/imports/testlib/TestCase.qml73
-rw-r--r--src/imports/testlib/plugins.qmltypes1
-rw-r--r--src/imports/window/plugin.cpp1
-rw-r--r--src/particles/particles.pro2
-rw-r--r--src/plugins/qmltooling/qmldbg_inspector/highlight.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qv4profileradapter.cpp7
-rw-r--r--src/qml/compiler/qv4bytecodehandler.cpp3
-rw-r--r--src/qml/compiler/qv4codegen.cpp68
-rw-r--r--src/qml/compiler/qv4codegen_p.h27
-rw-r--r--src/qml/compiler/qv4compileddata.cpp34
-rw-r--r--src/qml/compiler/qv4compileddata_p.h42
-rw-r--r--src/qml/compiler/qv4compiler.cpp53
-rw-r--r--src/qml/compiler/qv4compiler_p.h2
-rw-r--r--src/qml/compiler/qv4compilercontext.cpp2
-rw-r--r--src/qml/compiler/qv4compilercontext_p.h9
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp19
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h5
-rw-r--r--src/qml/compiler/qv4instr_moth.cpp4
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h2
-rw-r--r--src/qml/doc/src/cppintegration/data.qdoc2
-rw-r--r--src/qml/doc/src/qmlfunctions.qdoc33
-rw-r--r--src/qml/doc/src/qmllanguageref/modules/qqmlextensionplugin.qdocinc6
-rw-r--r--src/qml/doc/src/qmllanguageref/qmlreference.qdoc1
-rw-r--r--src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc1
-rw-r--r--src/qml/doc/src/qmltypereference.qdoc19
-rw-r--r--src/qml/jit/qv4baselinejit.cpp9
-rw-r--r--src/qml/jit/qv4baselinejit_p.h1
-rw-r--r--src/qml/jsapi/qjsvalue.cpp2
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4arrayobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4booleanobject.cpp12
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp18
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp11
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp11
-rw-r--r--src/qml/jsruntime/qv4include.cpp2
-rw-r--r--src/qml/jsruntime/qv4mapobject.cpp7
-rw-r--r--src/qml/jsruntime/qv4numberobject.cpp12
-rw-r--r--src/qml/jsruntime/qv4object.cpp15
-rw-r--r--src/qml/jsruntime/qv4object_p.h2
-rw-r--r--src/qml/jsruntime/qv4promiseobject.cpp5
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp4
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h9
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp8
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp6
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h2
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h5
-rw-r--r--src/qml/jsruntime/qv4script.cpp18
-rw-r--r--src/qml/jsruntime/qv4script_p.h6
-rw-r--r--src/qml/jsruntime/qv4setobject.cpp7
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp10
-rw-r--r--src/qml/jsruntime/qv4value_p.h2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp5
-rw-r--r--src/qml/parser/qqmljs.g29
-rw-r--r--src/qml/parser/qqmljsast_p.h5
-rw-r--r--src/qml/parser/qqmljslexer.cpp67
-rw-r--r--src/qml/parser/qqmljslexer_p.h3
-rw-r--r--src/qml/parser/qqmljsmemorypool_p.h14
-rw-r--r--src/qml/qml.pro4
-rw-r--r--src/qml/qml/qqmlengine.cpp3
-rw-r--r--src/qml/qml/qqmlimport.cpp29
-rw-r--r--src/qml/qml/qqmljavascriptexpression.cpp4
-rw-r--r--src/qml/qml/qqmlmetatype.cpp3
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp112
-rw-r--r--src/qml/qml/qqmltypeloader.cpp13
-rw-r--r--src/qmldebug/qqmlprofilerclient.cpp16
-rw-r--r--src/qmldebug/qqmlprofilerclient_p_p.h1
-rw-r--r--src/qmltest/doc/src/qtquicktest-index.qdoc11
-rw-r--r--src/qmltest/quicktestresult.cpp10
-rw-r--r--src/qmltest/quicktestresult_p.h2
-rw-r--r--src/quick/designer/qquickdesignersupport.cpp15
-rw-r--r--src/quick/designer/qquickdesignersupport_p.h2
-rw-r--r--src/quick/items/qquickevents_p_p.h1
-rw-r--r--src/quick/items/qquickopenglshadereffectnode.cpp1
-rw-r--r--src/quick/items/qquickpathview.cpp6
-rw-r--r--src/quick/items/qquickrendercontrol.cpp3
-rw-r--r--src/quick/items/qquicktableview.cpp12
-rw-r--r--src/quick/items/qquicktext.cpp26
-rw-r--r--src/quick/items/qquicktext_p_p.h1
-rw-r--r--src/quick/items/qquicktextedit.cpp15
-rw-r--r--src/quick/items/qquicktextnode.cpp2
-rw-r--r--src/quick/items/qquicktextnodeengine.cpp27
-rw-r--r--src/quick/items/qquicktextnodeengine_p.h2
-rw-r--r--src/quick/quick.pro2
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalimagenode.cpp13
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwareinternalrectanglenode.cpp7
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp5
-rw-r--r--src/quick/scenegraph/qsgbasicinternalimagenode.cpp100
-rw-r--r--src/quick/scenegraph/qsgrenderloop.cpp3
-rw-r--r--src/quick/scenegraph/qsgthreadedrenderloop.cpp3
-rw-r--r--src/quick/scenegraph/qsgwindowsrenderloop.cpp3
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();