aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJan Arve Saether <jan-arve.saether@qt.io>2017-06-26 18:38:27 +0200
committerJan Arve Saether <jan-arve.saether@qt.io>2017-07-11 16:44:33 +0200
commitb6e6e737f1a4a7e48989a6a036e25c238304802f (patch)
tree8d4b5940b92ad1fc94e46c1742acd9355e19e1d4 /src
parentd5b3f5da9cfa90fc43f29f3bdeec01884a47d6ca (diff)
parent4beee1a6dcc1be57aa6fb2a175dadc6ff298545d (diff)
Merge remote-tracking branch 'origin/dev' into wip/pointerhandler
Conflicts: examples/quick/shared/LauncherList.qml src/quick/items/qquickevents.cpp src/quick/items/qquickevents_p_p.h src/quick/items/qquickwindow.cpp tests/auto/quick/touchmouse/tst_touchmouse.cpp Change-Id: Id692d291455093fc72db61f1b854f3fc9190267b
Diffstat (limited to 'src')
-rw-r--r--src/3rdparty/masm/assembler/ARMv7Assembler.h22
-rw-r--r--src/3rdparty/masm/assembler/MacroAssembler.h8
-rw-r--r--src/3rdparty/masm/assembler/MacroAssemblerARM64.h10
-rw-r--r--src/imports/builtins/builtins.qmltypes3
-rw-r--r--src/imports/imports.pro2
-rw-r--r--src/imports/localstorage/plugin.cpp97
-rw-r--r--src/imports/qtquick2/plugins.qmltypes35
-rw-r--r--src/imports/settings/qqmlsettings.cpp18
-rw-r--r--src/imports/shapes/plugin.cpp73
-rw-r--r--src/imports/shapes/plugins.qmltypes284
-rw-r--r--src/imports/shapes/qmldir4
-rw-r--r--src/imports/shapes/qquicknvprfunctions.cpp277
-rw-r--r--src/imports/shapes/qquicknvprfunctions_p.h400
-rw-r--r--src/imports/shapes/qquicknvprfunctions_p_p.h70
-rw-r--r--src/imports/shapes/qquickshape.cpp1331
-rw-r--r--src/imports/shapes/qquickshape_p.h287
-rw-r--r--src/imports/shapes/qquickshape_p_p.h281
-rw-r--r--src/imports/shapes/qquickshapegenericrenderer.cpp784
-rw-r--r--src/imports/shapes/qquickshapegenericrenderer_p.h308
-rw-r--r--src/imports/shapes/qquickshapenvprrenderer.cpp923
-rw-r--r--src/imports/shapes/qquickshapenvprrenderer_p.h237
-rw-r--r--src/imports/shapes/qquickshapesoftwarerenderer.cpp277
-rw-r--r--src/imports/shapes/qquickshapesoftwarerenderer_p.h136
-rw-r--r--src/imports/shapes/shapes.pro31
-rw-r--r--src/imports/testlib/TestCase.qml28
-rw-r--r--src/particles/qquickv4particledata.cpp107
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp6
-rw-r--r--src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp2
-rw-r--r--src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp2
-rw-r--r--src/qml/compiler/compiler.pri6
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp8
-rw-r--r--src/qml/compiler/qqmlpropertyvalidator_p.h4
-rw-r--r--src/qml/compiler/qv4codegen.cpp172
-rw-r--r--src/qml/compiler/qv4codegen_p.h12
-rw-r--r--src/qml/compiler/qv4compileddata.cpp6
-rw-r--r--src/qml/compiler/qv4instr_moth_p.h13
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp59
-rw-r--r--src/qml/compiler/qv4jsir_p.h18
-rw-r--r--src/qml/compiler/qv4ssa.cpp12
-rw-r--r--src/qml/compiler/qv4ssa_p.h2
-rw-r--r--src/qml/configure.json9
-rw-r--r--src/qml/doc/images/cppintegration-ex.pngbin0 -> 1777 bytes
-rw-r--r--src/qml/doc/snippets/code/backend/backend.cpp70
-rw-r--r--src/qml/doc/snippets/code/backend/backend.h75
-rw-r--r--src/qml/doc/snippets/code/backend/main.cpp66
-rw-r--r--src/qml/doc/snippets/code/backend/main.qml79
-rw-r--r--src/qml/doc/src/cppintegration/topic.qdoc63
-rw-r--r--src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc20
-rw-r--r--src/qml/jit/qv4isel_masm.cpp12
-rw-r--r--src/qml/jit/qv4unop.cpp9
-rw-r--r--src/qml/jsapi/qjsvalue.cpp4
-rw-r--r--src/qml/jsruntime/jsruntime.pri2
-rw-r--r--src/qml/jsruntime/qv4alloca_p.h53
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp21
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp2
-rw-r--r--src/qml/jsruntime/qv4arraydata_p.h14
-rw-r--r--src/qml/jsruntime/qv4booleanobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4booleanobject_p.h1
-rw-r--r--src/qml/jsruntime/qv4context.cpp124
-rw-r--r--src/qml/jsruntime/qv4context_p.h15
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4engine.cpp136
-rw-r--r--src/qml/jsruntime/qv4engine_p.h33
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h128
-rw-r--r--src/qml/jsruntime/qv4errorobject.cpp7
-rw-r--r--src/qml/jsruntime/qv4errorobject_p.h18
-rw-r--r--src/qml/jsruntime/qv4executableallocator_p.h2
-rw-r--r--src/qml/jsruntime/qv4function.cpp8
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp66
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h20
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp132
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h65
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp272
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h22
-rw-r--r--src/qml/jsruntime/qv4managed.cpp2
-rw-r--r--src/qml/jsruntime/qv4managed_p.h11
-rw-r--r--src/qml/jsruntime/qv4memberdata_p.h19
-rw-r--r--src/qml/jsruntime/qv4numberobject_p.h1
-rw-r--r--src/qml/jsruntime/qv4object.cpp133
-rw-r--r--src/qml/jsruntime/qv4object_p.h74
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp12
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp25
-rw-r--r--src/qml/jsruntime/qv4regexp_p.h1
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h2
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp114
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h2
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h1
-rw-r--r--src/qml/jsruntime/qv4string.cpp14
-rw-r--r--src/qml/jsruntime/qv4string_p.h13
-rw-r--r--src/qml/jsruntime/qv4stringobject_p.h3
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp4
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h1
-rw-r--r--src/qml/jsruntime/qv4variantobject_p.h1
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp74
-rw-r--r--src/qml/jsruntime/qv4vme_moth_p.h10
-rw-r--r--src/qml/memory/qv4heap_p.h12
-rw-r--r--src/qml/memory/qv4mm.cpp45
-rw-r--r--src/qml/memory/qv4mm_p.h48
-rw-r--r--src/qml/memory/qv4mmdefs_p.h33
-rw-r--r--src/qml/memory/qv4writebarrier_p.h15
-rw-r--r--src/qml/parser/qqmljslexer.cpp5
-rw-r--r--src/qml/qml/qqmlengine.cpp8
-rw-r--r--src/qml/qml/qqmlfile.cpp6
-rw-r--r--src/qml/qml/qqmlglobal.cpp2
-rw-r--r--src/qml/qml/qqmlglobal_p.h1
-rw-r--r--src/qml/qml/qqmlplatform.cpp6
-rw-r--r--src/qml/qml/qqmlplatform_p.h2
-rw-r--r--src/qml/qml/qqmlpropertycache.cpp5
-rw-r--r--src/qml/qml/qqmltypeloader.cpp41
-rw-r--r--src/qml/qml/qqmltypeloader_p.h12
-rw-r--r--src/qml/qml/qqmlvaluetypewrapper.cpp26
-rw-r--r--src/qml/qml/qqmlvmemetaobject.cpp2
-rw-r--r--src/qml/qml/qqmlxmlhttprequest.cpp4
-rw-r--r--src/qml/qtqmlglobal.h16
-rw-r--r--src/qml/qtqmlglobal_p.h46
-rw-r--r--src/qml/types/qqmlconnections.cpp1
-rw-r--r--src/qml/types/qqmllistmodel.cpp5
-rw-r--r--src/qml/util/qqmladaptormodel.cpp44
-rw-r--r--src/qmltest/quicktestresult.cpp46
-rw-r--r--src/quick/configure.json41
-rw-r--r--src/quick/designer/qquickdesignersupportitems.cpp8
-rw-r--r--src/quick/doc/images/declarative-arcrotation.pngbin0 -> 4315 bytes
-rw-r--r--src/quick/doc/images/pathitem-code-example.pngbin0 -> 5989 bytes
-rw-r--r--src/quick/doc/images/visualpath-code-example.pngbin0 -> 844 bytes
-rw-r--r--src/quick/doc/snippets/qml/layerblending.qml2
-rw-r--r--src/quick/doc/snippets/qml/opacitymask.qml2
-rw-r--r--src/quick/doc/snippets/qml/path/arcrotation.qml52
-rw-r--r--src/quick/doc/snippets/qml/regexp.qml46
-rw-r--r--src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc59
-rw-r--r--src/quick/doc/src/concepts/positioning/layouts.qdoc3
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc2
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc2
-rw-r--r--src/quick/doc/src/concepts/visualcanvas/topic.qdoc2
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp3
-rw-r--r--src/quick/items/items.qrc4
-rw-r--r--src/quick/items/qquickanimatedimage_p.h1
-rw-r--r--src/quick/items/qquickevents.cpp2
-rw-r--r--src/quick/items/qquickevents_p_p.h26
-rw-r--r--src/quick/items/qquickgridview.cpp4
-rw-r--r--src/quick/items/qquickimplicitsizeitem.cpp26
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p.h8
-rw-r--r--src/quick/items/qquickimplicitsizeitem_p_p.h3
-rw-r--r--src/quick/items/qquickitem.cpp91
-rw-r--r--src/quick/items/qquickitemsmodule.cpp6
-rw-r--r--src/quick/items/qquickitemview.cpp8
-rw-r--r--src/quick/items/qquickitemview_p_p.h9
-rw-r--r--src/quick/items/qquicklistview.cpp4
-rw-r--r--src/quick/items/qquickopenglshadereffect.cpp28
-rw-r--r--src/quick/items/qquickopenglshadereffectnode.cpp15
-rw-r--r--src/quick/items/qquickopenglshadereffectnode_p.h2
-rw-r--r--src/quick/items/qquickrectangle.cpp19
-rw-r--r--src/quick/items/qquickrectangle_p.h6
-rw-r--r--src/quick/items/qquickrectangle_p_p.h1
-rw-r--r--src/quick/items/qquickrepeater.cpp6
-rw-r--r--src/quick/items/qquickscreen.cpp48
-rw-r--r--src/quick/items/qquickscreen_p.h9
-rw-r--r--src/quick/items/qquicktext.cpp47
-rw-r--r--src/quick/items/qquicktext_p.h2
-rw-r--r--src/quick/items/qquicktext_p_p.h1
-rw-r--r--src/quick/items/qquickview.cpp1
-rw-r--r--src/quick/items/qquickwindow.cpp71
-rw-r--r--src/quick/items/qquickwindow_p.h4
-rw-r--r--src/quick/items/qquickwindowmodule.cpp1
-rw-r--r--src/quick/items/shaders/lineargradient.frag9
-rw-r--r--src/quick/items/shaders/lineargradient.vert15
-rw-r--r--src/quick/items/shaders/lineargradient_core.frag12
-rw-r--r--src/quick/items/shaders/lineargradient_core.vert17
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp8
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp3
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp92
-rw-r--r--src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h6
-rw-r--r--src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp4
-rw-r--r--src/quick/scenegraph/coreapi/qsgnode.cpp12
-rw-r--r--src/quick/scenegraph/coreapi/qsgrenderer.cpp4
-rw-r--r--src/quick/scenegraph/coreapi/qsgrendernode.cpp9
-rw-r--r--src/quick/scenegraph/qsgcontext.cpp8
-rw-r--r--src/quick/scenegraph/qsgcontextplugin.cpp11
-rw-r--r--src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp15
-rw-r--r--src/quick/scenegraph/qsgdefaultrendercontext.cpp2
-rw-r--r--src/quick/scenegraph/util/qsgtexture.cpp26
-rw-r--r--src/quick/scenegraph/util/qsgtexture.h3
-rw-r--r--src/quick/scenegraph/util/qsgtexture_p.h4
-rw-r--r--src/quick/scenegraph/util/qsgtexturematerial.cpp4
-rw-r--r--src/quick/util/qquickglobal.cpp5
-rw-r--r--src/quick/util/qquickpath.cpp184
-rw-r--r--src/quick/util/qquickpath_p.h19
-rw-r--r--src/quick/util/qquickpath_p_p.h2
-rw-r--r--src/quick/util/qquickpixmapcache.cpp2
-rw-r--r--src/quick/util/qquicksvgparser.cpp8
-rw-r--r--src/quick/util/qquicksvgparser_p.h7
-rw-r--r--src/quick/util/qquickvalidator.cpp13
-rw-r--r--src/quickwidgets/qquickwidget.cpp5
196 files changed, 8730 insertions, 1317 deletions
diff --git a/src/3rdparty/masm/assembler/ARMv7Assembler.h b/src/3rdparty/masm/assembler/ARMv7Assembler.h
index 615c72fc15..d57e5a7c78 100644
--- a/src/3rdparty/masm/assembler/ARMv7Assembler.h
+++ b/src/3rdparty/masm/assembler/ARMv7Assembler.h
@@ -2531,12 +2531,18 @@ private:
return (instruction[0] == OP_NOP_T2a) && (instruction[1] == OP_NOP_T2b);
}
+ static int32_t makeRelative(const void *target, const void *source)
+ {
+ intptr_t difference = reinterpret_cast<intptr_t>(target) - reinterpret_cast<intptr_t>(source);
+ return static_cast<int32_t>(difference);
+ }
+
static bool canBeJumpT1(const uint16_t* instruction, const void* target)
{
ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1));
ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
- intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
+ auto relative = makeRelative(target, instruction);
// It does not appear to be documented in the ARM ARM (big surprise), but
// for OP_B_T1 the branch displacement encoded in the instruction is 2
// less than the actual displacement.
@@ -2549,7 +2555,7 @@ private:
ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1));
ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
- intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
+ auto relative = makeRelative(target, instruction);
// It does not appear to be documented in the ARM ARM (big surprise), but
// for OP_B_T2 the branch displacement encoded in the instruction is 2
// less than the actual displacement.
@@ -2562,7 +2568,7 @@ private:
ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1));
ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
- intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
+ auto relative = makeRelative(target, instruction);
return ((relative << 11) >> 11) == relative;
}
@@ -2571,7 +2577,7 @@ private:
ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1));
ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
- intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
+ auto relative = makeRelative(target, instruction);
return ((relative << 7) >> 7) == relative;
}
@@ -2582,7 +2588,7 @@ private:
ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
ASSERT(canBeJumpT1(instruction, target));
- intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
+ auto relative = makeRelative(target, instruction);
// It does not appear to be documented in the ARM ARM (big surprise), but
// for OP_B_T1 the branch displacement encoded in the instruction is 2
// less than the actual displacement.
@@ -2600,7 +2606,7 @@ private:
ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
ASSERT(canBeJumpT2(instruction, target));
- intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
+ auto relative = makeRelative(target, instruction);
// It does not appear to be documented in the ARM ARM (big surprise), but
// for OP_B_T2 the branch displacement encoded in the instruction is 2
// less than the actual displacement.
@@ -2618,7 +2624,7 @@ private:
ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
ASSERT(canBeJumpT3(instruction, target));
- intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
+ auto relative = makeRelative(target, instruction);
// All branch offsets should be an even distance.
ASSERT(!(relative & 1));
@@ -2633,7 +2639,7 @@ private:
ASSERT(!(reinterpret_cast<intptr_t>(target) & 1));
ASSERT(canBeJumpT4(instruction, target));
- intptr_t relative = reinterpret_cast<intptr_t>(target) - (reinterpret_cast<intptr_t>(instruction));
+ auto relative = makeRelative(target, instruction);
// ARM encoding for the top two bits below the sign bit is 'peculiar'.
if (relative >= 0)
relative ^= 0xC00000;
diff --git a/src/3rdparty/masm/assembler/MacroAssembler.h b/src/3rdparty/masm/assembler/MacroAssembler.h
index f37861eb66..6e77a9ffb7 100644
--- a/src/3rdparty/masm/assembler/MacroAssembler.h
+++ b/src/3rdparty/masm/assembler/MacroAssembler.h
@@ -248,14 +248,6 @@ public:
}
#endif
-#if CPU(MIPS)
- void poke(FPRegisterID src, int index = 0)
- {
- ASSERT(!(index & 1));
- storeDouble(src, addressForPoke(index));
- }
-#endif
-
// Backwards banches, these are currently all implemented using existing forwards branch mechanisms.
void branchPtr(RelationalCondition cond, RegisterID op1, TrustedImmPtr imm, Label target)
{
diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
index 11f1672e15..d5f4acb3ca 100644
--- a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
+++ b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h
@@ -214,27 +214,27 @@ public:
#if defined(V4_BOOTSTRAP)
void loadPtr(ImplicitAddress address, RegisterID dest)
{
- load32(address, dest);
+ load64(address, dest);
}
void subPtr(TrustedImm32 imm, RegisterID dest)
{
- sub32(imm, dest);
+ sub64(imm, dest);
}
void addPtr(TrustedImm32 imm, RegisterID dest)
{
- add32(imm, dest);
+ add64(imm, dest);
}
void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest)
{
- add32(imm, src, dest);
+ add64(imm, src, dest);
}
void storePtr(RegisterID src, ImplicitAddress address)
{
- store32(src, address);
+ store64(src, address);
}
#endif
diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes
index ac95a8837b..c2f8f5b521 100644
--- a/src/imports/builtins/builtins.qmltypes
+++ b/src/imports/builtins/builtins.qmltypes
@@ -426,7 +426,8 @@ Module {
"WA_X11DoNotAcceptFocus": 126,
"WA_MacNoShadow": 127,
"WA_AlwaysStackOnTop": 128,
- "WA_AttributeCount": 129
+ "WA_TabletTracking": 129,
+ "WA_AttributeCount": 130
}
}
Enum {
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
index 71c05c4b89..5d7e434884 100644
--- a/src/imports/imports.pro
+++ b/src/imports/imports.pro
@@ -23,6 +23,8 @@ qtHaveModule(quick) {
qtConfig(systemsemaphore): SUBDIRS += sharedimage
qtConfig(quick-particles): \
SUBDIRS += particles
+
+ SUBDIRS += shapes
}
qtHaveModule(xmlpatterns) : SUBDIRS += xmllistmodel
diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp
index 40682dd6a8..a7d95cc295 100644
--- a/src/imports/localstorage/plugin.cpp
+++ b/src/imports/localstorage/plugin.cpp
@@ -73,8 +73,8 @@ QT_BEGIN_NAMESPACE
QV4::ScopedString v(scope, scope.engine->newString(desc)); \
QV4::ScopedObject ex(scope, scope.engine->newErrorObject(v)); \
ex->put(QV4::ScopedString(scope, scope.engine->newIdentifier(QStringLiteral("code"))).getPointer(), QV4::ScopedValue(scope, Primitive::fromInt32(error))); \
- ctx->engine()->throwError(ex); \
- return Encode::undefined(); \
+ scope.engine->throwError(ex); \
+ RETURN_UNDEFINED(); \
}
#define V4THROW_SQL2(error, desc) { \
@@ -87,8 +87,8 @@ QT_BEGIN_NAMESPACE
#define V4THROW_REFERENCE(string) { \
QV4::ScopedString v(scope, scope.engine->newString(string)); \
- ctx->engine()->throwReferenceError(v); \
- return Encode::undefined(); \
+ scope.engine->throwReferenceError(v); \
+ RETURN_UNDEFINED(); \
}
@@ -164,20 +164,18 @@ DEFINE_OBJECT_VTABLE(QV4::QQmlSqlDatabaseWrapper);
-static ReturnedValue qmlsqldatabase_version(CallContext *ctx)
+static void qmlsqldatabase_version(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, ctx->thisObject().as<QQmlSqlDatabaseWrapper>());
+ QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database)
V4THROW_REFERENCE("Not a SQLDatabase object");
- return Encode(scope.engine->newString(*r->d()->version));
+ RETURN_RESULT(Encode(scope.engine->newString(*r->d()->version)));
}
-static ReturnedValue qmlsqldatabase_rows_length(CallContext *ctx)
+static void qmlsqldatabase_rows_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, ctx->thisObject().as<QQmlSqlDatabaseWrapper>());
+ QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
V4THROW_REFERENCE("Not a SQLDatabase::Rows object");
@@ -190,29 +188,27 @@ static ReturnedValue qmlsqldatabase_rows_length(CallContext *ctx)
s = 0;
}
}
- return Encode(s);
+ RETURN_RESULT(Encode(s));
}
-static ReturnedValue qmlsqldatabase_rows_forwardOnly(CallContext *ctx)
+static void qmlsqldatabase_rows_forwardOnly(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, ctx->thisObject().as<QQmlSqlDatabaseWrapper>());
+ QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
V4THROW_REFERENCE("Not a SQLDatabase::Rows object");
- return Encode(r->d()->sqlQuery->isForwardOnly());
+ RETURN_RESULT(Encode(r->d()->sqlQuery->isForwardOnly()));
}
-static ReturnedValue qmlsqldatabase_rows_setForwardOnly(CallContext *ctx)
+static void qmlsqldatabase_rows_setForwardOnly(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, ctx->thisObject().as<QQmlSqlDatabaseWrapper>());
+ QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
V4THROW_REFERENCE("Not a SQLDatabase::Rows object");
- if (ctx->argc() < 1)
- return ctx->engine()->throwTypeError();
+ if (callData->argc < 1)
+ RETURN_RESULT(scope.engine->throwTypeError());
- r->d()->sqlQuery->setForwardOnly(ctx->args()[0].toBoolean());
- return Encode::undefined();
+ r->d()->sqlQuery->setForwardOnly(callData->args[0].toBoolean());
+ RETURN_UNDEFINED();
}
QQmlSqlDatabaseData::~QQmlSqlDatabaseData()
@@ -253,14 +249,13 @@ ReturnedValue QQmlSqlDatabaseWrapper::getIndexed(const Managed *m, uint index, b
return qmlsqldatabase_rows_index(r, r->engine(), index, hasProperty);
}
-static ReturnedValue qmlsqldatabase_rows_item(CallContext *ctx)
+static void qmlsqldatabase_rows_item(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, ctx->thisObject().as<QQmlSqlDatabaseWrapper>());
+ QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Rows)
V4THROW_REFERENCE("Not a SQLDatabase::Rows object");
- return qmlsqldatabase_rows_index(r, scope.engine, ctx->argc() ? ctx->args()[0].toUInt32() : 0);
+ RETURN_RESULT(qmlsqldatabase_rows_index(r, scope.engine, callData->argc ? callData->args[0].toUInt32() : 0));
}
static QVariant toSqlVariant(QV4::ExecutionEngine *engine, const QV4::ScopedValue &value)
@@ -272,10 +267,9 @@ static QVariant toSqlVariant(QV4::ExecutionEngine *engine, const QV4::ScopedValu
return engine->toVariant(value, /*typehint*/-1);
}
-static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx)
+static void qmlsqldatabase_executeSql(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, ctx->thisObject().as<QQmlSqlDatabaseWrapper>());
+ QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Query)
V4THROW_REFERENCE("Not a SQLDatabase::Query object");
@@ -284,7 +278,7 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx)
QSqlDatabase db = *r->d()->database;
- QString sql = ctx->argc() ? ctx->args()[0].toQString() : QString();
+ QString sql = callData->argc ? callData->args[0].toQString() : QString();
if (r->d()->readonly && !sql.startsWith(QLatin1String("SELECT"),Qt::CaseInsensitive)) {
V4THROW_SQL(SQLEXCEPTION_SYNTAX_ERR, QQmlEngine::tr("Read-only Transaction"));
@@ -296,8 +290,8 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx)
ScopedValue result(scope, Primitive::undefinedValue());
if (query.prepare(sql)) {
- if (ctx->argc() > 1) {
- ScopedValue values(scope, ctx->args()[1]);
+ if (callData->argc > 1) {
+ ScopedValue values(scope, callData->args[1]);
if (values->as<ArrayObject>()) {
ScopedArrayObject array(scope, values);
quint32 size = array->getLength();
@@ -351,7 +345,7 @@ static ReturnedValue qmlsqldatabase_executeSql(CallContext *ctx)
if (err)
V4THROW_SQL(SQLEXCEPTION_DATABASE_ERR,query.lastError().text());
- return result->asReturnedValue();
+ RETURN_RESULT(result->asReturnedValue());
}
struct TransactionRollback {
@@ -383,21 +377,19 @@ struct TransactionRollback {
};
-static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx)
+static void qmlsqldatabase_changeVersion(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- if (ctx->argc() < 2)
- return Encode::undefined();
-
- Scope scope(ctx);
+ if (callData->argc < 2)
+ RETURN_UNDEFINED();
- Scoped<QQmlSqlDatabaseWrapper> r(scope, ctx->thisObject());
+ Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject);
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database)
V4THROW_REFERENCE("Not a SQLDatabase object");
QSqlDatabase db = *r->d()->database;
- QString from_version = ctx->args()[0].toQString();
- QString to_version = ctx->args()[1].toQString();
- ScopedFunctionObject callback(scope, ctx->argument(2));
+ QString from_version = callData->args[0].toQString();
+ QString to_version = callData->args[1].toQString();
+ ScopedFunctionObject callback(scope, callData->argc > 2 ? callData->args[2] : Primitive::undefinedValue());
if (from_version != *r->d()->version)
V4THROW_SQL(SQLEXCEPTION_VERSION_ERR, QQmlEngine::tr("Version mismatch: expected %1, found %2").arg(from_version).arg(*r->d()->version));
@@ -438,17 +430,16 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx)
#endif
}
- return Encode::undefined();
+ RETURN_UNDEFINED();
}
-static ReturnedValue qmlsqldatabase_transaction_shared(CallContext *ctx, bool readOnly)
+static void qmlsqldatabase_transaction_shared(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData, bool readOnly)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, ctx->thisObject().as<QQmlSqlDatabaseWrapper>());
+ QV4::Scoped<QQmlSqlDatabaseWrapper> r(scope, callData->thisObject.as<QQmlSqlDatabaseWrapper>());
if (!r || r->d()->type != Heap::QQmlSqlDatabaseWrapper::Database)
V4THROW_REFERENCE("Not a SQLDatabase object");
- const FunctionObject *callback = ctx->argc() ? ctx->args()[0].as<FunctionObject>() : 0;
+ const FunctionObject *callback = callData->argc ? callData->args[0].as<FunctionObject>() : 0;
if (!callback)
V4THROW_SQL(SQLEXCEPTION_UNKNOWN_ERR, QQmlEngine::tr("transaction: missing callback"));
@@ -475,17 +466,17 @@ static ReturnedValue qmlsqldatabase_transaction_shared(CallContext *ctx, bool re
db.rollback();
}
- return Encode::undefined();
+ RETURN_UNDEFINED();
}
-static ReturnedValue qmlsqldatabase_transaction(CallContext *ctx)
+static void qmlsqldatabase_transaction(const QV4::BuiltinFunction *f, QV4::Scope &scope, QV4::CallData *callData)
{
- return qmlsqldatabase_transaction_shared(ctx, false);
+ qmlsqldatabase_transaction_shared(f, scope, callData, false);
}
-static ReturnedValue qmlsqldatabase_read_transaction(CallContext *ctx)
+static void qmlsqldatabase_read_transaction(const QV4::BuiltinFunction *f, QV4::Scope &scope, QV4::CallData *callData)
{
- return qmlsqldatabase_transaction_shared(ctx, true);
+ qmlsqldatabase_transaction_shared(f, scope, callData, true);
}
QQmlSqlDatabaseData::QQmlSqlDatabaseData(ExecutionEngine *v4)
diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes
index a9534b5ccc..73d6d8ec68 100644
--- a/src/imports/qtquick2/plugins.qmltypes
+++ b/src/imports/qtquick2/plugins.qmltypes
@@ -4,7 +4,7 @@ import QtQuick.tooling 1.2
// It is used for QML tooling purposes only.
//
// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick 2.9'
+// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick 2.10'
Module {
dependencies: []
@@ -1414,7 +1414,7 @@ Module {
Property { name: "source"; type: "QObject"; isPointer: true }
Property { name: "target"; type: "QObject"; isReadonly: true; isPointer: true }
Property { name: "hotSpot"; type: "QPointF" }
- Property { name: "imageSource"; revision: 8; type: "QUrl" }
+ Property { name: "imageSource"; type: "QUrl" }
Property { name: "keys"; type: "QStringList" }
Property { name: "mimeData"; type: "QVariantMap" }
Property { name: "supportedActions"; type: "Qt::DropActions" }
@@ -1517,8 +1517,12 @@ Module {
name: "QQuickFlickable"
defaultProperty: "flickableData"
prototype: "QQuickItem"
- exports: ["QtQuick/Flickable 2.0", "QtQuick/Flickable 2.9"]
- exportMetaObjectRevisions: [0, 9]
+ exports: [
+ "QtQuick/Flickable 2.0",
+ "QtQuick/Flickable 2.10",
+ "QtQuick/Flickable 2.9"
+ ]
+ exportMetaObjectRevisions: [0, 10, 9]
Enum {
name: "BoundsBehavior"
values: {
@@ -1529,6 +1533,12 @@ Module {
}
}
Enum {
+ name: "BoundsMovement"
+ values: {
+ "FollowBoundsBehavior": 1
+ }
+ }
+ Enum {
name: "FlickableDirection"
values: {
"AutoFlickDirection": 0,
@@ -1552,6 +1562,7 @@ Module {
Property { name: "horizontalVelocity"; type: "double"; isReadonly: true }
Property { name: "verticalVelocity"; type: "double"; isReadonly: true }
Property { name: "boundsBehavior"; type: "BoundsBehavior" }
+ Property { name: "boundsMovement"; revision: 10; type: "BoundsMovement" }
Property { name: "rebound"; type: "QQuickTransition"; isPointer: true }
Property { name: "maximumFlickVelocity"; type: "double" }
Property { name: "flickDeceleration"; type: "double" }
@@ -1583,6 +1594,7 @@ Module {
Property { name: "flickableData"; type: "QObject"; isList: true; isReadonly: true }
Property { name: "flickableChildren"; type: "QQuickItem"; isList: true; isReadonly: true }
Signal { name: "isAtBoundaryChanged" }
+ Signal { name: "boundsMovementChanged"; revision: 10 }
Signal { name: "movementStarted" }
Signal { name: "movementEnded" }
Signal { name: "flickStarted" }
@@ -1811,7 +1823,6 @@ Module {
exports: ["QtQuick/Gradient 2.0"]
exportMetaObjectRevisions: [0]
Property { name: "stops"; type: "QQuickGradientStop"; isList: true; isReadonly: true }
- Signal { name: "updated" }
}
Component {
name: "QQuickGradientStop"
@@ -2088,8 +2099,6 @@ Module {
prototype: "QQuickItem"
Property { name: "implicitWidth"; type: "double"; isReadonly: true }
Property { name: "implicitHeight"; type: "double"; isReadonly: true }
- Signal { name: "implicitWidthChanged2"; revision: 1 }
- Signal { name: "implicitHeightChanged2"; revision: 1 }
}
Component {
name: "QQuickIntValidator"
@@ -2300,6 +2309,7 @@ Module {
Property { name: "samplerName"; type: "QByteArray" }
Property { name: "effect"; type: "QQmlComponent"; isPointer: true }
Property { name: "textureMirroring"; type: "QQuickShaderEffectSource::TextureMirroring" }
+ Property { name: "samples"; type: "int" }
Signal {
name: "enabledChanged"
Parameter { name: "enabled"; type: "bool" }
@@ -2340,6 +2350,10 @@ Module {
name: "textureMirroringChanged"
Parameter { name: "mirroring"; type: "QQuickShaderEffectSource::TextureMirroring" }
}
+ Signal {
+ name: "samplesChanged"
+ Parameter { name: "count"; type: "int" }
+ }
}
Component {
name: "QQuickItemView"
@@ -3594,9 +3608,10 @@ Module {
prototype: "QQuickItem"
exports: [
"QtQuick/ShaderEffectSource 2.0",
- "QtQuick/ShaderEffectSource 2.6"
+ "QtQuick/ShaderEffectSource 2.6",
+ "QtQuick/ShaderEffectSource 2.9"
]
- exportMetaObjectRevisions: [0, 1]
+ exportMetaObjectRevisions: [0, 1, 2]
Enum {
name: "WrapMode"
values: {
@@ -3632,6 +3647,7 @@ Module {
Property { name: "mipmap"; type: "bool" }
Property { name: "recursive"; type: "bool" }
Property { name: "textureMirroring"; revision: 1; type: "TextureMirroring" }
+ Property { name: "samples"; revision: 2; type: "int" }
Signal { name: "scheduledUpdateCompleted" }
Method { name: "scheduleUpdate" }
}
@@ -4084,6 +4100,7 @@ Module {
Property { name: "rightPadding"; revision: 6; type: "double" }
Property { name: "bottomPadding"; revision: 6; type: "double" }
Property { name: "fontInfo"; revision: 9; type: "QJSValue"; isReadonly: true }
+ Property { name: "advance"; revision: 10; type: "QSizeF"; isReadonly: true }
Signal {
name: "textChanged"
Parameter { name: "text"; type: "string" }
diff --git a/src/imports/settings/qqmlsettings.cpp b/src/imports/settings/qqmlsettings.cpp
index cd6fcbc718..df67c04654 100644
--- a/src/imports/settings/qqmlsettings.cpp
+++ b/src/imports/settings/qqmlsettings.cpp
@@ -41,6 +41,7 @@
#include <qcoreevent.h>
#include <qsettings.h>
#include <qpointer.h>
+#include <qjsvalue.h>
#include <qdebug.h>
#include <qhash.h>
@@ -241,6 +242,7 @@ public:
void store();
void _q_propertyChanged();
+ QVariant readProperty(const QMetaProperty &property) const;
QQmlSettings *q_ptr;
int timerId;
@@ -295,7 +297,7 @@ void QQmlSettingsPrivate::load()
for (int i = offset; i < count; ++i) {
QMetaProperty property = mo->property(i);
- const QVariant previousValue = property.read(q);
+ const QVariant previousValue = readProperty(property);
const QVariant currentValue = instance()->value(property.name(), previousValue);
if (!currentValue.isNull() && (!previousValue.isValid()
@@ -340,9 +342,10 @@ void QQmlSettingsPrivate::_q_propertyChanged()
const int count = mo->propertyCount();
for (int i = offset; i < count; ++i) {
const QMetaProperty &property = mo->property(i);
- changedProperties.insert(property.name(), property.read(q));
+ const QVariant value = readProperty(property);
+ changedProperties.insert(property.name(), value);
#ifdef SETTINGS_DEBUG
- qDebug() << "QQmlSettings: cache" << property.name() << ":" << property.read(q);
+ qDebug() << "QQmlSettings: cache" << property.name() << ":" << value;
#endif
}
if (timerId != 0)
@@ -350,6 +353,15 @@ void QQmlSettingsPrivate::_q_propertyChanged()
timerId = q->startTimer(settingsWriteDelay);
}
+QVariant QQmlSettingsPrivate::readProperty(const QMetaProperty &property) const
+{
+ Q_Q(const QQmlSettings);
+ QVariant var = property.read(q);
+ if (var.userType() == qMetaTypeId<QJSValue>())
+ var = var.value<QJSValue>().toVariant();
+ return var;
+}
+
QQmlSettings::QQmlSettings(QObject *parent)
: QObject(parent), d_ptr(new QQmlSettingsPrivate)
{
diff --git a/src/imports/shapes/plugin.cpp b/src/imports/shapes/plugin.cpp
new file mode 100644
index 0000000000..ae0d02da93
--- /dev/null
+++ b/src/imports/shapes/plugin.cpp
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtQml/qqmlextensionplugin.h>
+#include <QtQml/qqml.h>
+
+#include "qquickshape_p.h"
+
+static void initResources()
+{
+#ifdef QT_STATIC
+ Q_INIT_RESOURCE(qmake_QtQuick_Shapes);
+#endif
+}
+
+QT_BEGIN_NAMESPACE
+
+class QmlShapesPlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
+
+public:
+ QmlShapesPlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); }
+ void registerTypes(const char *uri) Q_DECL_OVERRIDE
+ {
+ Q_ASSERT(QByteArray(uri) == QByteArray("QtQuick.Shapes"));
+ qmlRegisterType<QQuickShape>(uri, 1, 0, "Shape");
+ qmlRegisterType<QQuickShapePath>(uri, 1, 0, "ShapePath");
+ qmlRegisterUncreatableType<QQuickShapeGradient>(uri, 1, 0, "ShapeGradient", QQuickShapeGradient::tr("ShapeGradient is an abstract base class"));
+ qmlRegisterType<QQuickShapeLinearGradient>(uri, 1, 0, "LinearGradient");
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/shapes/plugins.qmltypes b/src/imports/shapes/plugins.qmltypes
new file mode 100644
index 0000000000..00d0050085
--- /dev/null
+++ b/src/imports/shapes/plugins.qmltypes
@@ -0,0 +1,284 @@
+import QtQuick.tooling 1.2
+
+// This file describes the plugin-supplied types contained in the library.
+// It is used for QML tooling purposes only.
+//
+// This file was auto-generated by:
+// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick.Shapes 1.0'
+
+Module {
+ dependencies: []
+ Component {
+ name: "QQuickItem"
+ defaultProperty: "data"
+ prototype: "QObject"
+ Enum {
+ name: "TransformOrigin"
+ values: {
+ "TopLeft": 0,
+ "Top": 1,
+ "TopRight": 2,
+ "Left": 3,
+ "Center": 4,
+ "Right": 5,
+ "BottomLeft": 6,
+ "Bottom": 7,
+ "BottomRight": 8
+ }
+ }
+ Property { name: "parent"; type: "QQuickItem"; isPointer: true }
+ Property { name: "data"; type: "QObject"; isList: true; isReadonly: true }
+ Property { name: "resources"; type: "QObject"; isList: true; isReadonly: true }
+ Property { name: "children"; type: "QQuickItem"; isList: true; isReadonly: true }
+ Property { name: "x"; type: "double" }
+ Property { name: "y"; type: "double" }
+ Property { name: "z"; type: "double" }
+ Property { name: "width"; type: "double" }
+ Property { name: "height"; type: "double" }
+ Property { name: "opacity"; type: "double" }
+ Property { name: "enabled"; type: "bool" }
+ Property { name: "visible"; type: "bool" }
+ Property { name: "visibleChildren"; type: "QQuickItem"; isList: true; isReadonly: true }
+ Property { name: "states"; type: "QQuickState"; isList: true; isReadonly: true }
+ Property { name: "transitions"; type: "QQuickTransition"; isList: true; isReadonly: true }
+ Property { name: "state"; type: "string" }
+ Property { name: "childrenRect"; type: "QRectF"; isReadonly: true }
+ Property { name: "anchors"; type: "QQuickAnchors"; isReadonly: true; isPointer: true }
+ Property { name: "left"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "right"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "horizontalCenter"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "top"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "bottom"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "verticalCenter"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "baseline"; type: "QQuickAnchorLine"; isReadonly: true }
+ Property { name: "baselineOffset"; type: "double" }
+ Property { name: "clip"; type: "bool" }
+ Property { name: "focus"; type: "bool" }
+ Property { name: "activeFocus"; type: "bool"; isReadonly: true }
+ Property { name: "activeFocusOnTab"; revision: 1; type: "bool" }
+ Property { name: "rotation"; type: "double" }
+ Property { name: "scale"; type: "double" }
+ Property { name: "transformOrigin"; type: "TransformOrigin" }
+ Property { name: "transformOriginPoint"; type: "QPointF"; isReadonly: true }
+ Property { name: "transform"; type: "QQuickTransform"; isList: true; isReadonly: true }
+ Property { name: "smooth"; type: "bool" }
+ Property { name: "antialiasing"; type: "bool" }
+ Property { name: "implicitWidth"; type: "double" }
+ Property { name: "implicitHeight"; type: "double" }
+ Property { name: "layer"; type: "QQuickItemLayer"; isReadonly: true; isPointer: true }
+ Signal {
+ name: "childrenRectChanged"
+ Parameter { type: "QRectF" }
+ }
+ Signal {
+ name: "baselineOffsetChanged"
+ Parameter { type: "double" }
+ }
+ Signal {
+ name: "stateChanged"
+ Parameter { type: "string" }
+ }
+ Signal {
+ name: "focusChanged"
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "activeFocusChanged"
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "activeFocusOnTabChanged"
+ revision: 1
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "parentChanged"
+ Parameter { type: "QQuickItem"; isPointer: true }
+ }
+ Signal {
+ name: "transformOriginChanged"
+ Parameter { type: "TransformOrigin" }
+ }
+ Signal {
+ name: "smoothChanged"
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "antialiasingChanged"
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "clipChanged"
+ Parameter { type: "bool" }
+ }
+ Signal {
+ name: "windowChanged"
+ revision: 1
+ Parameter { name: "window"; type: "QQuickWindow"; isPointer: true }
+ }
+ Method { name: "update" }
+ Method {
+ name: "grabToImage"
+ revision: 2
+ type: "bool"
+ Parameter { name: "callback"; type: "QJSValue" }
+ Parameter { name: "targetSize"; type: "QSize" }
+ }
+ Method {
+ name: "grabToImage"
+ revision: 2
+ type: "bool"
+ Parameter { name: "callback"; type: "QJSValue" }
+ }
+ Method {
+ name: "contains"
+ type: "bool"
+ Parameter { name: "point"; type: "QPointF" }
+ }
+ Method {
+ name: "mapFromItem"
+ Parameter { type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "mapToItem"
+ Parameter { type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "mapFromGlobal"
+ revision: 7
+ Parameter { type: "QQmlV4Function"; isPointer: true }
+ }
+ Method {
+ name: "mapToGlobal"
+ revision: 7
+ Parameter { type: "QQmlV4Function"; isPointer: true }
+ }
+ Method { name: "forceActiveFocus" }
+ Method {
+ name: "forceActiveFocus"
+ Parameter { name: "reason"; type: "Qt::FocusReason" }
+ }
+ Method {
+ name: "nextItemInFocusChain"
+ revision: 1
+ type: "QQuickItem*"
+ Parameter { name: "forward"; type: "bool" }
+ }
+ Method { name: "nextItemInFocusChain"; revision: 1; type: "QQuickItem*" }
+ Method {
+ name: "childAt"
+ type: "QQuickItem*"
+ Parameter { name: "x"; type: "double" }
+ Parameter { name: "y"; type: "double" }
+ }
+ }
+ Component {
+ name: "QQuickShapeGradient"
+ defaultProperty: "stops"
+ prototype: "QObject"
+ exports: ["QtQuick.Shapes/ShapeGradient 1.0"]
+ isCreatable: false
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "SpreadMode"
+ values: {
+ "PadSpread": 0,
+ "RepeatSpread": 1,
+ "ReflectSpread": 2
+ }
+ }
+ Property { name: "stops"; type: "QObject"; isList: true; isReadonly: true }
+ Property { name: "spread"; type: "SpreadMode" }
+ Signal { name: "updated" }
+ }
+ Component {
+ name: "QQuickShape"
+ defaultProperty: "elements"
+ prototype: "QQuickItem"
+ exports: ["QtQuick.Shapes/Shape 1.0"]
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "RendererType"
+ values: {
+ "UnknownRenderer": 0,
+ "GeometryRenderer": 1,
+ "NvprRenderer": 2,
+ "SoftwareRenderer": 3
+ }
+ }
+ Enum {
+ name: "Status"
+ values: {
+ "Null": 0,
+ "Ready": 1,
+ "Processing": 2
+ }
+ }
+ Property { name: "renderer"; type: "RendererType"; isReadonly: true }
+ Property { name: "asynchronous"; type: "bool" }
+ Property { name: "enableVendorExtensions"; type: "bool" }
+ Property { name: "status"; type: "Status"; isReadonly: true }
+ Property { name: "elements"; type: "QQuickShapePath"; isList: true; isReadonly: true }
+ }
+ Component {
+ name: "QQuickShapeLinearGradient"
+ defaultProperty: "stops"
+ prototype: "QQuickShapeGradient"
+ exports: ["QtQuick.Shapes/LinearGradient 1.0"]
+ exportMetaObjectRevisions: [0]
+ Property { name: "x1"; type: "double" }
+ Property { name: "y1"; type: "double" }
+ Property { name: "x2"; type: "double" }
+ Property { name: "y2"; type: "double" }
+ }
+ Component {
+ name: "QQuickShapePath"
+ defaultProperty: "path"
+ prototype: "QObject"
+ exports: ["QtQuick.Shapes/ShapePath 1.0"]
+ exportMetaObjectRevisions: [0]
+ Enum {
+ name: "FillRule"
+ values: {
+ "OddEvenFill": 0,
+ "WindingFill": 1
+ }
+ }
+ Enum {
+ name: "JoinStyle"
+ values: {
+ "MiterJoin": 0,
+ "BevelJoin": 64,
+ "RoundJoin": 128
+ }
+ }
+ Enum {
+ name: "CapStyle"
+ values: {
+ "FlatCap": 0,
+ "SquareCap": 16,
+ "RoundCap": 32
+ }
+ }
+ Enum {
+ name: "StrokeStyle"
+ values: {
+ "SolidLine": 1,
+ "DashLine": 2
+ }
+ }
+ Property { name: "path"; type: "QQuickPath"; isPointer: true }
+ Property { name: "strokeColor"; type: "QColor" }
+ Property { name: "strokeWidth"; type: "double" }
+ Property { name: "fillColor"; type: "QColor" }
+ Property { name: "fillRule"; type: "FillRule" }
+ Property { name: "joinStyle"; type: "JoinStyle" }
+ Property { name: "miterLimit"; type: "int" }
+ Property { name: "capStyle"; type: "CapStyle" }
+ Property { name: "strokeStyle"; type: "StrokeStyle" }
+ Property { name: "dashOffset"; type: "double" }
+ Property { name: "dashPattern"; type: "QVector<qreal>" }
+ Property { name: "fillGradient"; type: "QQuickShapeGradient"; isPointer: true }
+ Signal { name: "changed" }
+ }
+}
diff --git a/src/imports/shapes/qmldir b/src/imports/shapes/qmldir
new file mode 100644
index 0000000000..306ad1ecd7
--- /dev/null
+++ b/src/imports/shapes/qmldir
@@ -0,0 +1,4 @@
+module QtQuick.Shapes
+plugin qmlshapesplugin
+classname QmlShapesPlugin
+typeinfo plugins.qmltypes
diff --git a/src/imports/shapes/qquicknvprfunctions.cpp b/src/imports/shapes/qquicknvprfunctions.cpp
new file mode 100644
index 0000000000..0b92d4b4c0
--- /dev/null
+++ b/src/imports/shapes/qquicknvprfunctions.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquicknvprfunctions_p.h"
+
+#if QT_CONFIG(opengl)
+
+#include <QOpenGLContext>
+#include <QOffscreenSurface>
+#include <QOpenGLExtraFunctions>
+#include "qquicknvprfunctions_p_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QQuickNvprFunctions
+
+ \brief Function resolvers and other helpers for GL_NV_path_rendering
+ for both desktop (GL 4.3+) and mobile/embedded (GLES 3.1+) in a manner
+ that does not distract builds that do not have NVPR support either at
+ compile or run time.
+
+ \internal
+ */
+
+QQuickNvprFunctions::QQuickNvprFunctions()
+ : d(new QQuickNvprFunctionsPrivate(this))
+{
+}
+
+QQuickNvprFunctions::~QQuickNvprFunctions()
+{
+ delete d;
+}
+
+/*!
+ \return a recommended QSurfaceFormat suitable for GL_NV_path_rendering on top
+ of OpenGL 4.3 or OpenGL ES 3.1.
+ */
+QSurfaceFormat QQuickNvprFunctions::format()
+{
+ QSurfaceFormat fmt;
+ fmt.setDepthBufferSize(24);
+ fmt.setStencilBufferSize(8);
+ if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGL) {
+ fmt.setVersion(4, 3);
+ fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
+ } else if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
+ fmt.setVersion(3, 1);
+ }
+ return fmt;
+}
+
+/*!
+ \return true if GL_NV_path_rendering is supported with the current OpenGL
+ context.
+
+ When there is no current context, a temporary dummy one will be created and
+ made current.
+ */
+bool QQuickNvprFunctions::isSupported()
+{
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ QScopedPointer<QOpenGLContext> tempContext;
+ QScopedPointer<QOffscreenSurface> tempSurface;
+ if (!ctx) {
+ tempContext.reset(new QOpenGLContext);
+ if (!tempContext->create())
+ return false;
+ ctx = tempContext.data();
+ tempSurface.reset(new QOffscreenSurface);
+ tempSurface->setFormat(ctx->format());
+ tempSurface->create();
+ if (!ctx->makeCurrent(tempSurface.data()))
+ return false;
+ }
+
+ if (!ctx->hasExtension(QByteArrayLiteral("GL_NV_path_rendering")))
+ return false;
+
+ // Do not check for DSA as the string may not be exposed on ES
+ // drivers, yet the functions we need are resolvable.
+#if 0
+ if (!ctx->hasExtension(QByteArrayLiteral("GL_EXT_direct_state_access"))) {
+ qWarning("QtQuickPath/NVPR: GL_EXT_direct_state_access not supported");
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+/*!
+ Initializes using the current OpenGL context.
+
+ \return true when GL_NV_path_rendering is supported and initialization was
+ successful.
+ */
+bool QQuickNvprFunctions::create()
+{
+ return isSupported() && d->resolve();
+}
+
+/*!
+ Creates a program pipeline consisting of a separable fragment shader program.
+
+ This is essential for using NVPR with OpenGL ES 3.1+ since normal,
+ GLES2-style programs would not work without a vertex shader.
+
+ \note \a fragmentShaderSource should be a \c{version 310 es} shader since
+ this works both on desktop and embedded NVIDIA drivers, thus avoiding the
+ need to fight GLSL and GLSL ES differences.
+
+ The pipeline object is stored into \a pipeline, the fragment shader program
+ into \a program.
+
+ Use QOpenGLExtraFunctions to set uniforms, bind the pipeline, etc.
+
+ \return \c false on failure in which case the error log is printed on the
+ debug output. \c true on success.
+ */
+bool QQuickNvprFunctions::createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program)
+{
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+ if (!ctx)
+ return false;
+
+ QOpenGLExtraFunctions *f = ctx->extraFunctions();
+ *program = f->glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &fragmentShaderSource);
+ GLint status = 0;
+ f->glGetProgramiv(*program, GL_LINK_STATUS, &status);
+ if (!status) {
+ GLint len = 0;
+ f->glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &len);
+ if (len) {
+ QByteArray s;
+ s.resize(len);
+ f->glGetProgramInfoLog(*program, s.count(), nullptr, s.data());
+ qWarning("Failed to create separable shader program:\n%s", s.constData());
+ }
+ return false;
+ }
+
+ f->glGenProgramPipelines(1, pipeline);
+ f->glUseProgramStages(*pipeline, GL_FRAGMENT_SHADER_BIT, *program);
+ f->glActiveShaderProgram(*pipeline, *program);
+
+ f->glValidateProgramPipeline(*pipeline);
+ status = 0;
+ f->glGetProgramPipelineiv(*pipeline, GL_VALIDATE_STATUS, &status);
+ if (!status) {
+ GLint len = 0;
+ f->glGetProgramPipelineiv(*pipeline, GL_INFO_LOG_LENGTH, &len);
+ if (len) {
+ QByteArray s;
+ s.resize(len);
+ f->glGetProgramPipelineInfoLog(*pipeline, s.count(), nullptr, s.data());
+ qWarning("Program pipeline validation failed:\n%s", s.constData());
+ }
+ return false;
+ }
+
+ return true;
+}
+
+#define PROC(type, name) reinterpret_cast<type>(ctx->getProcAddress(#name))
+
+bool QQuickNvprFunctionsPrivate::resolve()
+{
+ QOpenGLContext *ctx = QOpenGLContext::currentContext();
+
+ q->genPaths = PROC(PFNGLGENPATHSNVPROC, glGenPathsNV);
+ q->deletePaths = PROC(PFNGLDELETEPATHSNVPROC, glDeletePathsNV);
+ q->isPath = PROC(PFNGLISPATHNVPROC, glIsPathNV);
+ q->pathCommands = PROC(PFNGLPATHCOMMANDSNVPROC, glPathCommandsNV);
+ q->pathCoords = PROC(PFNGLPATHCOORDSNVPROC, glPathCoordsNV);
+ q->pathSubCommands = PROC(PFNGLPATHSUBCOMMANDSNVPROC, glPathSubCommandsNV);
+ q->pathSubCoords = PROC(PFNGLPATHSUBCOORDSNVPROC, glPathSubCoordsNV);
+ q->pathString = PROC(PFNGLPATHSTRINGNVPROC, glPathStringNV);
+ q->pathGlyphs = PROC(PFNGLPATHGLYPHSNVPROC, glPathGlyphsNV);
+ q->pathGlyphRange = PROC(PFNGLPATHGLYPHRANGENVPROC, glPathGlyphRangeNV);
+ q->weightPaths = PROC(PFNGLWEIGHTPATHSNVPROC, glWeightPathsNV);
+ q->copyPath = PROC(PFNGLCOPYPATHNVPROC, glCopyPathNV);
+ q->interpolatePaths = PROC(PFNGLINTERPOLATEPATHSNVPROC, glInterpolatePathsNV);
+ q->transformPath = PROC(PFNGLTRANSFORMPATHNVPROC, glTransformPathNV);
+ q->pathParameteriv = PROC(PFNGLPATHPARAMETERIVNVPROC, glPathParameterivNV);
+ q->pathParameteri = PROC(PFNGLPATHPARAMETERINVPROC, glPathParameteriNV);
+ q->pathParameterfv = PROC(PFNGLPATHPARAMETERFVNVPROC, glPathParameterfvNV);
+ q->pathParameterf = PROC(PFNGLPATHPARAMETERFNVPROC, glPathParameterfNV);
+ q->pathDashArray = PROC(PFNGLPATHDASHARRAYNVPROC, glPathDashArrayNV);
+ q->pathStencilFunc = PROC(PFNGLPATHSTENCILFUNCNVPROC, glPathStencilFuncNV);
+ q->pathStencilDepthOffset = PROC(PFNGLPATHSTENCILDEPTHOFFSETNVPROC, glPathStencilDepthOffsetNV);
+ q->stencilFillPath = PROC(PFNGLSTENCILFILLPATHNVPROC, glStencilFillPathNV);
+ q->stencilStrokePath = PROC(PFNGLSTENCILSTROKEPATHNVPROC, glStencilStrokePathNV);
+ q->stencilFillPathInstanced = PROC(PFNGLSTENCILFILLPATHINSTANCEDNVPROC, glStencilFillPathInstancedNV);
+ q->stencilStrokePathInstanced = PROC(PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC, glStencilStrokePathInstancedNV);
+ q->pathCoverDepthFunc = PROC(PFNGLPATHCOVERDEPTHFUNCNVPROC, glPathCoverDepthFuncNV);
+ q->coverFillPath = PROC(PFNGLCOVERFILLPATHNVPROC, glCoverFillPathNV);
+ q->coverStrokePath = PROC(PFNGLCOVERSTROKEPATHNVPROC, glCoverStrokePathNV);
+ q->coverFillPathInstanced = PROC(PFNGLCOVERFILLPATHINSTANCEDNVPROC, glCoverFillPathInstancedNV);
+ q->coverStrokePathInstanced = PROC(PFNGLCOVERSTROKEPATHINSTANCEDNVPROC, glCoverStrokePathInstancedNV);
+ q->getPathParameteriv = PROC(PFNGLGETPATHPARAMETERIVNVPROC, glGetPathParameterivNV);
+ q->getPathParameterfv = PROC(PFNGLGETPATHPARAMETERFVNVPROC, glGetPathParameterfvNV);
+ q->getPathCommands = PROC(PFNGLGETPATHCOMMANDSNVPROC, glGetPathCommandsNV);
+ q->getPathCoords = PROC(PFNGLGETPATHCOORDSNVPROC, glGetPathCoordsNV);
+ q->getPathDashArray = PROC(PFNGLGETPATHDASHARRAYNVPROC, glGetPathDashArrayNV);
+ q->getPathMetrics = PROC(PFNGLGETPATHMETRICSNVPROC, glGetPathMetricsNV);
+ q->getPathMetricRange = PROC(PFNGLGETPATHMETRICRANGENVPROC, glGetPathMetricRangeNV);
+ q->getPathSpacing = PROC(PFNGLGETPATHSPACINGNVPROC, glGetPathSpacingNV);
+ q->isPointInFillPath = PROC(PFNGLISPOINTINFILLPATHNVPROC, glIsPointInFillPathNV);
+ q->isPointInStrokePath = PROC(PFNGLISPOINTINSTROKEPATHNVPROC, glIsPointInStrokePathNV);
+ q->getPathLength = PROC(PFNGLGETPATHLENGTHNVPROC, glGetPathLengthNV);
+ q->getPointAlongPath = PROC(PFNGLPOINTALONGPATHNVPROC, glPointAlongPathNV);
+ q->matrixLoad3x2f = PROC(PFNGLMATRIXLOAD3X2FNVPROC, glMatrixLoad3x2fNV);
+ q->matrixLoad3x3f = PROC(PFNGLMATRIXLOAD3X3FNVPROC, glMatrixLoad3x3fNV);
+ q->matrixLoadTranspose3x3f = PROC(PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC, glMatrixLoadTranspose3x3fNV);
+ q->matrixMult3x2f = PROC(PFNGLMATRIXMULT3X2FNVPROC, glMatrixMult3x2fNV);
+ q->matrixMult3x3f = PROC(PFNGLMATRIXMULT3X3FNVPROC, glMatrixMult3x3fNV);
+ q->matrixMultTranspose3x3f = PROC(PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC, glMatrixMultTranspose3x3fNV);
+ q->stencilThenCoverFillPath = PROC(PFNGLSTENCILTHENCOVERFILLPATHNVPROC, glStencilThenCoverFillPathNV);
+ q->stencilThenCoverStrokePath = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC, glStencilThenCoverStrokePathNV);
+ q->stencilThenCoverFillPathInstanced = PROC(PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC, glStencilThenCoverFillPathInstancedNV);
+ q->stencilThenCoverStrokePathInstanced = PROC(PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC, glStencilThenCoverStrokePathInstancedNV);
+ q->pathGlyphIndexRange = PROC(PFNGLPATHGLYPHINDEXRANGENVPROC, glPathGlyphIndexRangeNV);
+ q->pathGlyphIndexArray = PROC(PFNGLPATHGLYPHINDEXARRAYNVPROC, glPathGlyphIndexArrayNV);
+ q->pathMemoryGlyphIndexArray = PROC(PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC, glPathMemoryGlyphIndexArrayNV);
+ q->programPathFragmentInputGen = PROC(PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC, glProgramPathFragmentInputGenNV);
+ q->getProgramResourcefv = PROC(PFNGLGETPROGRAMRESOURCEFVNVPROC, glGetProgramResourcefvNV);
+
+ q->matrixLoadf = PROC(PFNGLMATRIXLOADFEXTPROC, glMatrixLoadfEXT);
+ q->matrixLoadIdentity = PROC(PFNGLMATRIXLOADIDENTITYEXTPROC, glMatrixLoadIdentityEXT);
+
+ return q->genPaths != nullptr // base path rendering ext
+ && q->programPathFragmentInputGen != nullptr // updated path rendering ext
+ && q->matrixLoadf != nullptr // direct state access ext
+ && q->matrixLoadIdentity != nullptr;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(opengl)
diff --git a/src/imports/shapes/qquicknvprfunctions_p.h b/src/imports/shapes/qquicknvprfunctions_p.h
new file mode 100644
index 0000000000..342e92cbc3
--- /dev/null
+++ b/src/imports/shapes/qquicknvprfunctions_p.h
@@ -0,0 +1,400 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKNVPRFUNCTIONS_P_H
+#define QQUICKNVPRFUNCTIONS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qopengl.h>
+#include <QtGui/qsurfaceformat.h>
+
+#if QT_CONFIG(opengl)
+
+QT_BEGIN_NAMESPACE
+
+// note: fixed pipeline specific functions are removed - modern ES ext
+// headers have all this, but not the fixed stuff
+
+#ifndef GL_NV_path_rendering
+#define GL_PATH_FORMAT_SVG_NV 0x9070
+#define GL_PATH_FORMAT_PS_NV 0x9071
+#define GL_STANDARD_FONT_NAME_NV 0x9072
+#define GL_SYSTEM_FONT_NAME_NV 0x9073
+#define GL_FILE_NAME_NV 0x9074
+#define GL_PATH_STROKE_WIDTH_NV 0x9075
+#define GL_PATH_END_CAPS_NV 0x9076
+#define GL_PATH_INITIAL_END_CAP_NV 0x9077
+#define GL_PATH_TERMINAL_END_CAP_NV 0x9078
+#define GL_PATH_JOIN_STYLE_NV 0x9079
+#define GL_PATH_MITER_LIMIT_NV 0x907A
+#define GL_PATH_DASH_CAPS_NV 0x907B
+#define GL_PATH_INITIAL_DASH_CAP_NV 0x907C
+#define GL_PATH_TERMINAL_DASH_CAP_NV 0x907D
+#define GL_PATH_DASH_OFFSET_NV 0x907E
+#define GL_PATH_CLIENT_LENGTH_NV 0x907F
+#define GL_PATH_FILL_MODE_NV 0x9080
+#define GL_PATH_FILL_MASK_NV 0x9081
+#define GL_PATH_FILL_COVER_MODE_NV 0x9082
+#define GL_PATH_STROKE_COVER_MODE_NV 0x9083
+#define GL_PATH_STROKE_MASK_NV 0x9084
+#define GL_COUNT_UP_NV 0x9088
+#define GL_COUNT_DOWN_NV 0x9089
+#define GL_PATH_OBJECT_BOUNDING_BOX_NV 0x908A
+#define GL_CONVEX_HULL_NV 0x908B
+#define GL_BOUNDING_BOX_NV 0x908D
+#define GL_TRANSLATE_X_NV 0x908E
+#define GL_TRANSLATE_Y_NV 0x908F
+#define GL_TRANSLATE_2D_NV 0x9090
+#define GL_TRANSLATE_3D_NV 0x9091
+#define GL_AFFINE_2D_NV 0x9092
+#define GL_AFFINE_3D_NV 0x9094
+#define GL_TRANSPOSE_AFFINE_2D_NV 0x9096
+#define GL_TRANSPOSE_AFFINE_3D_NV 0x9098
+#define GL_UTF8_NV 0x909A
+#define GL_UTF16_NV 0x909B
+#define GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV 0x909C
+#define GL_PATH_COMMAND_COUNT_NV 0x909D
+#define GL_PATH_COORD_COUNT_NV 0x909E
+#define GL_PATH_DASH_ARRAY_COUNT_NV 0x909F
+#define GL_PATH_COMPUTED_LENGTH_NV 0x90A0
+#define GL_PATH_FILL_BOUNDING_BOX_NV 0x90A1
+#define GL_PATH_STROKE_BOUNDING_BOX_NV 0x90A2
+#define GL_SQUARE_NV 0x90A3
+#define GL_ROUND_NV 0x90A4
+#define GL_TRIANGULAR_NV 0x90A5
+#define GL_BEVEL_NV 0x90A6
+#define GL_MITER_REVERT_NV 0x90A7
+#define GL_MITER_TRUNCATE_NV 0x90A8
+#define GL_SKIP_MISSING_GLYPH_NV 0x90A9
+#define GL_USE_MISSING_GLYPH_NV 0x90AA
+#define GL_PATH_ERROR_POSITION_NV 0x90AB
+#define GL_PATH_FOG_GEN_MODE_NV 0x90AC
+#define GL_ACCUM_ADJACENT_PAIRS_NV 0x90AD
+#define GL_ADJACENT_PAIRS_NV 0x90AE
+#define GL_FIRST_TO_REST_NV 0x90AF
+#define GL_PATH_GEN_MODE_NV 0x90B0
+#define GL_PATH_GEN_COEFF_NV 0x90B1
+#define GL_PATH_GEN_COLOR_FORMAT_NV 0x90B2
+#define GL_PATH_GEN_COMPONENTS_NV 0x90B3
+#define GL_PATH_STENCIL_FUNC_NV 0x90B7
+#define GL_PATH_STENCIL_REF_NV 0x90B8
+#define GL_PATH_STENCIL_VALUE_MASK_NV 0x90B9
+#define GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV 0x90BD
+#define GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV 0x90BE
+#define GL_PATH_COVER_DEPTH_FUNC_NV 0x90BF
+#define GL_PATH_DASH_OFFSET_RESET_NV 0x90B4
+#define GL_MOVE_TO_RESETS_NV 0x90B5
+#define GL_MOVE_TO_CONTINUES_NV 0x90B6
+#define GL_CLOSE_PATH_NV 0x00
+#define GL_MOVE_TO_NV 0x02
+#define GL_RELATIVE_MOVE_TO_NV 0x03
+#define GL_LINE_TO_NV 0x04
+#define GL_RELATIVE_LINE_TO_NV 0x05
+#define GL_HORIZONTAL_LINE_TO_NV 0x06
+#define GL_RELATIVE_HORIZONTAL_LINE_TO_NV 0x07
+#define GL_VERTICAL_LINE_TO_NV 0x08
+#define GL_RELATIVE_VERTICAL_LINE_TO_NV 0x09
+#define GL_QUADRATIC_CURVE_TO_NV 0x0A
+#define GL_RELATIVE_QUADRATIC_CURVE_TO_NV 0x0B
+#define GL_CUBIC_CURVE_TO_NV 0x0C
+#define GL_RELATIVE_CUBIC_CURVE_TO_NV 0x0D
+#define GL_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0E
+#define GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV 0x0F
+#define GL_SMOOTH_CUBIC_CURVE_TO_NV 0x10
+#define GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV 0x11
+#define GL_SMALL_CCW_ARC_TO_NV 0x12
+#define GL_RELATIVE_SMALL_CCW_ARC_TO_NV 0x13
+#define GL_SMALL_CW_ARC_TO_NV 0x14
+#define GL_RELATIVE_SMALL_CW_ARC_TO_NV 0x15
+#define GL_LARGE_CCW_ARC_TO_NV 0x16
+#define GL_RELATIVE_LARGE_CCW_ARC_TO_NV 0x17
+#define GL_LARGE_CW_ARC_TO_NV 0x18
+#define GL_RELATIVE_LARGE_CW_ARC_TO_NV 0x19
+#define GL_RESTART_PATH_NV 0xF0
+#define GL_DUP_FIRST_CUBIC_CURVE_TO_NV 0xF2
+#define GL_DUP_LAST_CUBIC_CURVE_TO_NV 0xF4
+#define GL_RECT_NV 0xF6
+#define GL_CIRCULAR_CCW_ARC_TO_NV 0xF8
+#define GL_CIRCULAR_CW_ARC_TO_NV 0xFA
+#define GL_CIRCULAR_TANGENT_ARC_TO_NV 0xFC
+#define GL_ARC_TO_NV 0xFE
+#define GL_RELATIVE_ARC_TO_NV 0xFF
+#define GL_BOLD_BIT_NV 0x01
+#define GL_ITALIC_BIT_NV 0x02
+#define GL_GLYPH_WIDTH_BIT_NV 0x01
+#define GL_GLYPH_HEIGHT_BIT_NV 0x02
+#define GL_GLYPH_HORIZONTAL_BEARING_X_BIT_NV 0x04
+#define GL_GLYPH_HORIZONTAL_BEARING_Y_BIT_NV 0x08
+#define GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT_NV 0x10
+#define GL_GLYPH_VERTICAL_BEARING_X_BIT_NV 0x20
+#define GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV 0x40
+#define GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV 0x80
+#define GL_GLYPH_HAS_KERNING_BIT_NV 0x100
+#define GL_FONT_X_MIN_BOUNDS_BIT_NV 0x00010000
+#define GL_FONT_Y_MIN_BOUNDS_BIT_NV 0x00020000
+#define GL_FONT_X_MAX_BOUNDS_BIT_NV 0x00040000
+#define GL_FONT_Y_MAX_BOUNDS_BIT_NV 0x00080000
+#define GL_FONT_UNITS_PER_EM_BIT_NV 0x00100000
+#define GL_FONT_ASCENDER_BIT_NV 0x00200000
+#define GL_FONT_DESCENDER_BIT_NV 0x00400000
+#define GL_FONT_HEIGHT_BIT_NV 0x00800000
+#define GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV 0x01000000
+#define GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV 0x02000000
+#define GL_FONT_UNDERLINE_POSITION_BIT_NV 0x04000000
+#define GL_FONT_UNDERLINE_THICKNESS_BIT_NV 0x08000000
+#define GL_FONT_HAS_KERNING_BIT_NV 0x10000000
+#define GL_PRIMARY_COLOR_NV 0x852C
+#define GL_SECONDARY_COLOR_NV 0x852D
+#define GL_ROUNDED_RECT_NV 0xE8
+#define GL_RELATIVE_ROUNDED_RECT_NV 0xE9
+#define GL_ROUNDED_RECT2_NV 0xEA
+#define GL_RELATIVE_ROUNDED_RECT2_NV 0xEB
+#define GL_ROUNDED_RECT4_NV 0xEC
+#define GL_RELATIVE_ROUNDED_RECT4_NV 0xED
+#define GL_ROUNDED_RECT8_NV 0xEE
+#define GL_RELATIVE_ROUNDED_RECT8_NV 0xEF
+#define GL_RELATIVE_RECT_NV 0xF7
+#define GL_FONT_GLYPHS_AVAILABLE_NV 0x9368
+#define GL_FONT_TARGET_UNAVAILABLE_NV 0x9369
+#define GL_FONT_UNAVAILABLE_NV 0x936A
+#define GL_FONT_UNINTELLIGIBLE_NV 0x936B
+#define GL_CONIC_CURVE_TO_NV 0x1A
+#define GL_RELATIVE_CONIC_CURVE_TO_NV 0x1B
+#define GL_FONT_NUM_GLYPH_INDICES_BIT_NV 0x20000000
+#define GL_STANDARD_FONT_FORMAT_NV 0x936C
+#define GL_2_BYTES_NV 0x1407
+#define GL_3_BYTES_NV 0x1408
+#define GL_4_BYTES_NV 0x1409
+#define GL_EYE_LINEAR_NV 0x2400
+#define GL_OBJECT_LINEAR_NV 0x2401
+#define GL_CONSTANT_NV 0x8576
+#define GL_PATH_PROJECTION_NV 0x1701
+#define GL_PATH_MODELVIEW_NV 0x1700
+#define GL_PATH_MODELVIEW_STACK_DEPTH_NV 0x0BA3
+#define GL_PATH_MODELVIEW_MATRIX_NV 0x0BA6
+#define GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV 0x0D36
+#define GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV 0x84E3
+#define GL_PATH_PROJECTION_STACK_DEPTH_NV 0x0BA4
+#define GL_PATH_PROJECTION_MATRIX_NV 0x0BA7
+#define GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV 0x0D38
+#define GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV 0x84E4
+#define GL_FRAGMENT_INPUT_NV 0x936D
+
+typedef GLuint (QOPENGLF_APIENTRYP PFNGLGENPATHSNVPROC) (GLsizei range);
+typedef void (QOPENGLF_APIENTRYP PFNGLDELETEPATHSNVPROC) (GLuint path, GLsizei range);
+typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPATHNVPROC) (GLuint path);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOMMANDSNVPROC) (GLuint path, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOORDSNVPROC) (GLuint path, GLsizei numCoords, GLenum coordType, const void *coords);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOMMANDSNVPROC) (GLuint path, GLsizei commandStart, GLsizei commandsToDelete, GLsizei numCommands, const GLubyte *commands, GLsizei numCoords, GLenum coordType, const void *coords);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHSUBCOORDSNVPROC) (GLuint path, GLsizei coordStart, GLsizei numCoords, GLenum coordType, const void *coords);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTRINGNVPROC) (GLuint path, GLenum format, GLsizei length, const void *pathString);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHSNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLsizei numGlyphs, GLenum type, const void *charcodes, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHGLYPHRANGENVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyph, GLsizei numGlyphs, GLenum handleMissingGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
+typedef void (QOPENGLF_APIENTRYP PFNGLWEIGHTPATHSNVPROC) (GLuint resultPath, GLsizei numPaths, const GLuint *paths, const GLfloat *weights);
+typedef void (QOPENGLF_APIENTRYP PFNGLCOPYPATHNVPROC) (GLuint resultPath, GLuint srcPath);
+typedef void (QOPENGLF_APIENTRYP PFNGLINTERPOLATEPATHSNVPROC) (GLuint resultPath, GLuint pathA, GLuint pathB, GLfloat weight);
+typedef void (QOPENGLF_APIENTRYP PFNGLTRANSFORMPATHNVPROC) (GLuint resultPath, GLuint srcPath, GLenum transformType, const GLfloat *transformValues);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, const GLint *value);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERINVPROC) (GLuint path, GLenum pname, GLint value);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, const GLfloat *value);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHPARAMETERFNVPROC) (GLuint path, GLenum pname, GLfloat value);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHDASHARRAYNVPROC) (GLuint path, GLsizei dashCount, const GLfloat *dashArray);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILFUNCNVPROC) (GLenum func, GLint ref, GLuint mask);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHSTENCILDEPTHOFFSETNVPROC) (GLfloat factor, GLfloat units);
+typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask);
+typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask);
+typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum transformType, const GLfloat *transformValues);
+typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum transformType, const GLfloat *transformValues);
+typedef void (QOPENGLF_APIENTRYP PFNGLPATHCOVERDEPTHFUNCNVPROC) (GLenum func);
+typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHNVPROC) (GLuint path, GLenum coverMode);
+typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHNVPROC) (GLuint path, GLenum coverMode);
+typedef void (QOPENGLF_APIENTRYP PFNGLCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);
+typedef void (QOPENGLF_APIENTRYP PFNGLCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);
+typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERIVNVPROC) (GLuint path, GLenum pname, GLint *value);
+typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHPARAMETERFVNVPROC) (GLuint path, GLenum pname, GLfloat *value);
+typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOMMANDSNVPROC) (GLuint path, GLubyte *commands);
+typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHCOORDSNVPROC) (GLuint path, GLfloat *coords);
+typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHDASHARRAYNVPROC) (GLuint path, GLfloat *dashArray);
+typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICSNVPROC) (GLbitfield metricQueryMask, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLsizei stride, GLfloat *metrics);
+typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHMETRICRANGENVPROC) (GLbitfield metricQueryMask, GLuint firstPathName, GLsizei numPaths, GLsizei stride, GLfloat *metrics);
+typedef void (QOPENGLF_APIENTRYP PFNGLGETPATHSPACINGNVPROC) (GLenum pathListMode, GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLfloat advanceScale, GLfloat kerningScale, GLenum transformType, GLfloat *returnedSpacing);
+typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINFILLPATHNVPROC) (GLuint path, GLuint mask, GLfloat x, GLfloat y);
+typedef GLboolean (QOPENGLF_APIENTRYP PFNGLISPOINTINSTROKEPATHNVPROC) (GLuint path, GLfloat x, GLfloat y);
+typedef GLfloat (QOPENGLF_APIENTRYP PFNGLGETPATHLENGTHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments);
+typedef GLboolean (QOPENGLF_APIENTRYP PFNGLPOINTALONGPATHNVPROC) (GLuint path, GLsizei startSegment, GLsizei numSegments, GLfloat distance, GLfloat *x, GLfloat *y, GLfloat *tangentX, GLfloat *tangentY);
+typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X2FNVPROC) (GLenum matrixMode, const GLfloat *m);
+typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOAD3X3FNVPROC) (GLenum matrixMode, const GLfloat *m);
+typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m);
+typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X2FNVPROC) (GLenum matrixMode, const GLfloat *m);
+typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULT3X3FNVPROC) (GLenum matrixMode, const GLfloat *m);
+typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC) (GLenum matrixMode, const GLfloat *m);
+typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHNVPROC) (GLuint path, GLenum fillMode, GLuint mask, GLenum coverMode);
+typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC) (GLuint path, GLint reference, GLuint mask, GLenum coverMode);
+typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLenum fillMode, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);
+typedef void (QOPENGLF_APIENTRYP PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC) (GLsizei numPaths, GLenum pathNameType, const void *paths, GLuint pathBase, GLint reference, GLuint mask, GLenum coverMode, GLenum transformType, const GLfloat *transformValues);
+typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXRANGENVPROC) (GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint pathParameterTemplate, GLfloat emScale, GLuint baseAndCount[2]);
+typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, const void *fontName, GLbitfield fontStyle, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
+typedef GLenum (QOPENGLF_APIENTRYP PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC) (GLuint firstPathName, GLenum fontTarget, GLsizeiptr fontSize, const void *fontData, GLsizei faceIndex, GLuint firstGlyphIndex, GLsizei numGlyphs, GLuint pathParameterTemplate, GLfloat emScale);
+typedef void (QOPENGLF_APIENTRYP PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC) (GLuint program, GLint location, GLenum genMode, GLint components, const GLfloat *coeffs);
+typedef void (QOPENGLF_APIENTRYP PFNGLGETPROGRAMRESOURCEFVNVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLfloat *params);
+#endif
+
+#ifndef GL_FLAT
+#define GL_FLAT 0x1D00
+#endif
+
+#ifndef GL_INVERT
+#define GL_INVERT 0x150A
+#endif
+
+// this one originates from fixed pipeline so may not be in GLES ext headers, but we need it still
+#ifndef GL_OBJECT_LINEAR_NV
+#define GL_OBJECT_LINEAR_NV 0x2401
+#endif
+
+#ifndef GL_EXT_direct_state_access
+typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADFEXTPROC) (GLenum mode, const GLfloat *m);
+typedef void (QOPENGLF_APIENTRYP PFNGLMATRIXLOADIDENTITYEXTPROC) (GLenum mode);
+#endif
+
+// When building on a system with GLES 2.0 or 3.0, we may still compile the NVPR
+// code path even though it's never used. Keep it compiling by defining the
+// necessary ES 3.1 separable program constants.
+#ifndef GL_FRAGMENT_SHADER_BIT
+#define GL_FRAGMENT_SHADER_BIT 0x00000002
+#endif
+#ifndef GL_UNIFORM
+#define GL_UNIFORM 0x92E1
+#endif
+
+class QQuickNvprFunctionsPrivate;
+
+class QQuickNvprFunctions
+{
+public:
+ QQuickNvprFunctions();
+ ~QQuickNvprFunctions();
+
+ static QSurfaceFormat format();
+ static bool isSupported();
+
+ bool create();
+
+ bool createFragmentOnlyPipeline(const char *fragmentShaderSource, GLuint *pipeline, GLuint *program);
+
+ PFNGLGENPATHSNVPROC genPaths = nullptr;
+ PFNGLDELETEPATHSNVPROC deletePaths = nullptr;
+ PFNGLISPATHNVPROC isPath = nullptr;
+ PFNGLPATHCOMMANDSNVPROC pathCommands = nullptr;
+ PFNGLPATHCOORDSNVPROC pathCoords = nullptr;
+ PFNGLPATHSUBCOMMANDSNVPROC pathSubCommands = nullptr;
+ PFNGLPATHSUBCOORDSNVPROC pathSubCoords = nullptr;
+ PFNGLPATHSTRINGNVPROC pathString = nullptr;
+ PFNGLPATHGLYPHSNVPROC pathGlyphs = nullptr;
+ PFNGLPATHGLYPHRANGENVPROC pathGlyphRange = nullptr;
+ PFNGLWEIGHTPATHSNVPROC weightPaths = nullptr;
+ PFNGLCOPYPATHNVPROC copyPath = nullptr;
+ PFNGLINTERPOLATEPATHSNVPROC interpolatePaths = nullptr;
+ PFNGLTRANSFORMPATHNVPROC transformPath = nullptr;
+ PFNGLPATHPARAMETERIVNVPROC pathParameteriv = nullptr;
+ PFNGLPATHPARAMETERINVPROC pathParameteri = nullptr;
+ PFNGLPATHPARAMETERFVNVPROC pathParameterfv = nullptr;
+ PFNGLPATHPARAMETERFNVPROC pathParameterf = nullptr;
+ PFNGLPATHDASHARRAYNVPROC pathDashArray = nullptr;
+ PFNGLPATHSTENCILFUNCNVPROC pathStencilFunc = nullptr;
+ PFNGLPATHSTENCILDEPTHOFFSETNVPROC pathStencilDepthOffset = nullptr;
+ PFNGLSTENCILFILLPATHNVPROC stencilFillPath = nullptr;
+ PFNGLSTENCILSTROKEPATHNVPROC stencilStrokePath = nullptr;
+ PFNGLSTENCILFILLPATHINSTANCEDNVPROC stencilFillPathInstanced = nullptr;
+ PFNGLSTENCILSTROKEPATHINSTANCEDNVPROC stencilStrokePathInstanced = nullptr;
+ PFNGLPATHCOVERDEPTHFUNCNVPROC pathCoverDepthFunc = nullptr;
+ PFNGLCOVERFILLPATHNVPROC coverFillPath = nullptr;
+ PFNGLCOVERSTROKEPATHNVPROC coverStrokePath = nullptr;
+ PFNGLCOVERFILLPATHINSTANCEDNVPROC coverFillPathInstanced = nullptr;
+ PFNGLCOVERSTROKEPATHINSTANCEDNVPROC coverStrokePathInstanced = nullptr;
+ PFNGLGETPATHPARAMETERIVNVPROC getPathParameteriv = nullptr;
+ PFNGLGETPATHPARAMETERFVNVPROC getPathParameterfv = nullptr;
+ PFNGLGETPATHCOMMANDSNVPROC getPathCommands = nullptr;
+ PFNGLGETPATHCOORDSNVPROC getPathCoords = nullptr;
+ PFNGLGETPATHDASHARRAYNVPROC getPathDashArray = nullptr;
+ PFNGLGETPATHMETRICSNVPROC getPathMetrics = nullptr;
+ PFNGLGETPATHMETRICRANGENVPROC getPathMetricRange = nullptr;
+ PFNGLGETPATHSPACINGNVPROC getPathSpacing = nullptr;
+ PFNGLISPOINTINFILLPATHNVPROC isPointInFillPath = nullptr;
+ PFNGLISPOINTINSTROKEPATHNVPROC isPointInStrokePath = nullptr;
+ PFNGLGETPATHLENGTHNVPROC getPathLength = nullptr;
+ PFNGLPOINTALONGPATHNVPROC getPointAlongPath = nullptr;
+ PFNGLMATRIXLOAD3X2FNVPROC matrixLoad3x2f = nullptr;
+ PFNGLMATRIXLOAD3X3FNVPROC matrixLoad3x3f = nullptr;
+ PFNGLMATRIXLOADTRANSPOSE3X3FNVPROC matrixLoadTranspose3x3f = nullptr;
+ PFNGLMATRIXMULT3X2FNVPROC matrixMult3x2f = nullptr;
+ PFNGLMATRIXMULT3X3FNVPROC matrixMult3x3f = nullptr;
+ PFNGLMATRIXMULTTRANSPOSE3X3FNVPROC matrixMultTranspose3x3f = nullptr;
+ PFNGLSTENCILTHENCOVERFILLPATHNVPROC stencilThenCoverFillPath = nullptr;
+ PFNGLSTENCILTHENCOVERSTROKEPATHNVPROC stencilThenCoverStrokePath = nullptr;
+ PFNGLSTENCILTHENCOVERFILLPATHINSTANCEDNVPROC stencilThenCoverFillPathInstanced = nullptr;
+ PFNGLSTENCILTHENCOVERSTROKEPATHINSTANCEDNVPROC stencilThenCoverStrokePathInstanced = nullptr;
+ PFNGLPATHGLYPHINDEXRANGENVPROC pathGlyphIndexRange = nullptr;
+ PFNGLPATHGLYPHINDEXARRAYNVPROC pathGlyphIndexArray = nullptr;
+ PFNGLPATHMEMORYGLYPHINDEXARRAYNVPROC pathMemoryGlyphIndexArray = nullptr;
+ PFNGLPROGRAMPATHFRAGMENTINPUTGENNVPROC programPathFragmentInputGen = nullptr;
+ PFNGLGETPROGRAMRESOURCEFVNVPROC getProgramResourcefv = nullptr;
+
+ PFNGLMATRIXLOADFEXTPROC matrixLoadf = nullptr;
+ PFNGLMATRIXLOADIDENTITYEXTPROC matrixLoadIdentity = nullptr;
+
+private:
+ QQuickNvprFunctionsPrivate *d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(opengl)
+
+#endif // QQUICKNVPRFUNCTIONS_P_H
diff --git a/src/imports/shapes/qquicknvprfunctions_p_p.h b/src/imports/shapes/qquicknvprfunctions_p_p.h
new file mode 100644
index 0000000000..6df20566af
--- /dev/null
+++ b/src/imports/shapes/qquicknvprfunctions_p_p.h
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKNVPRFUNCTIONS_P_P_H
+#define QQUICKNVPRFUNCTIONS_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquicknvprfunctions_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QQuickNvprFunctionsPrivate
+{
+public:
+ QQuickNvprFunctionsPrivate(QQuickNvprFunctions *q_ptr) : q(q_ptr) { }
+
+ bool resolve();
+
+ QQuickNvprFunctions *q;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKNVPRFUNCTIONS_P_P_H
diff --git a/src/imports/shapes/qquickshape.cpp b/src/imports/shapes/qquickshape.cpp
new file mode 100644
index 0000000000..1054af30de
--- /dev/null
+++ b/src/imports/shapes/qquickshape.cpp
@@ -0,0 +1,1331 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickshape_p.h"
+#include "qquickshape_p_p.h"
+#include "qquickshapegenericrenderer_p.h"
+#include "qquickshapenvprrenderer_p.h"
+#include "qquickshapesoftwarerenderer_p.h"
+#include <private/qsgtexture_p.h>
+#include <private/qquicksvgparser_p.h>
+#include <QtGui/private/qdrawhelper_p.h>
+#include <QOpenGLFunctions>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmlmodule QtQuick.Shapes 1.0
+ \title Qt Quick Shapes QML Types
+ \ingroup qmlmodules
+ \brief Provides QML types for drawing stroked and filled shapes
+
+ To use the types in this module, import the module with the following line:
+
+ \code
+ import QtQuick.Shapes 1.0
+ \endcode
+*/
+
+QQuickShapeStrokeFillParams::QQuickShapeStrokeFillParams()
+ : strokeColor(Qt::white),
+ strokeWidth(1),
+ fillColor(Qt::white),
+ fillRule(QQuickShapePath::OddEvenFill),
+ joinStyle(QQuickShapePath::BevelJoin),
+ miterLimit(2),
+ capStyle(QQuickShapePath::SquareCap),
+ strokeStyle(QQuickShapePath::SolidLine),
+ dashOffset(0),
+ fillGradient(nullptr)
+{
+ dashPattern << 4 << 2; // 4 * strokeWidth dash followed by 2 * strokeWidth space
+}
+
+QPainterPath QQuickShapePathCommands::toPainterPath() const
+{
+ QPainterPath p;
+ int coordIdx = 0;
+ for (int i = 0; i < cmd.count(); ++i) {
+ switch (cmd[i]) {
+ case QQuickShapePathCommands::MoveTo:
+ p.moveTo(coords[coordIdx], coords[coordIdx + 1]);
+ coordIdx += 2;
+ break;
+ case QQuickShapePathCommands::LineTo:
+ p.lineTo(coords[coordIdx], coords[coordIdx + 1]);
+ coordIdx += 2;
+ break;
+ case QQuickShapePathCommands::QuadTo:
+ p.quadTo(coords[coordIdx], coords[coordIdx + 1],
+ coords[coordIdx + 2], coords[coordIdx + 3]);
+ coordIdx += 4;
+ break;
+ case QQuickShapePathCommands::CubicTo:
+ p.cubicTo(coords[coordIdx], coords[coordIdx + 1],
+ coords[coordIdx + 2], coords[coordIdx + 3],
+ coords[coordIdx + 4], coords[coordIdx + 5]);
+ coordIdx += 6;
+ break;
+ case QQuickShapePathCommands::ArcTo:
+ // does not map to the QPainterPath API; reuse the helper code from QQuickSvgParser
+ QQuickSvgParser::pathArc(p,
+ coords[coordIdx], coords[coordIdx + 1], // radius
+ coords[coordIdx + 2], // xAxisRotation
+ !qFuzzyIsNull(coords[coordIdx + 6]), // useLargeArc
+ !qFuzzyIsNull(coords[coordIdx + 5]), // sweep flag
+ coords[coordIdx + 3], coords[coordIdx + 4], // end
+ p.currentPosition().x(), p.currentPosition().y());
+ coordIdx += 7;
+ break;
+ default:
+ qWarning("Unknown JS path command: %d", cmd[i]);
+ break;
+ }
+ }
+ return p;
+}
+
+/*!
+ \qmltype ShapePath
+ \instantiates QQuickShapePath
+ \inqmlmodule QtQuick.Shapes
+ \ingroup qtquick-paths
+ \ingroup qtquick-views
+ \inherits Path
+ \brief Describes a Path and associated properties for stroking and filling
+ \since 5.10
+
+ A Shape contains one or more ShapePath elements. At least one ShapePath is
+ necessary in order to have a Shape output anything visible. A ShapePath
+ itself is a \l Path with additional properties describing the stroking and
+ filling parameters, such as the stroke width and color, the fill color or
+ gradient, join and cap styles, and so on. As with ordinary \l Path objects,
+ ShapePath also contains a list of path elements like \l PathMove, \l PathLine,
+ \l PathCubic, \l PathQuad, \l PathArc, together with a starting position.
+
+ Any property changes in these data sets will be bubble up and change the
+ output of the Shape. This means that it is simple and easy to change, or
+ even animate, the starting and ending position, control points, or any
+ stroke or fill parameters using the usual QML bindings and animation types
+ like NumberAnimation.
+
+ In the following example the line join style changes automatically based on
+ the value of joinStyleIndex:
+
+ \code
+ ShapePath {
+ strokeColor: "black"
+ strokeWidth: 16
+ fillColor: "transparent"
+ capStyle: ShapePath.RoundCap
+
+ property int joinStyleIndex: 0
+ property variant styles: [ ShapePath.BevelJoin, ShapePath.MiterJoin, ShapePath.RoundJoin ]
+
+ joinStyle: styles[joinStyleIndex]
+
+ startX: 30
+ startY: 30
+ PathLine { x: 100; y: 100 }
+ PathLine { x: 30; y: 100 }
+ }
+ \endcode
+
+ Once associated with a Shape, here is the output with a joinStyleIndex
+ of 2 (ShapePath.RoundJoin):
+
+ \image visualpath-code-example.png
+ */
+
+QQuickShapePathPrivate::QQuickShapePathPrivate()
+ : dirty(DirtyAll)
+{
+}
+
+QQuickShapePath::QQuickShapePath(QObject *parent)
+ : QQuickPath(*(new QQuickShapePathPrivate), parent)
+{
+ // The inherited changed() and the shapePathChanged() signals remain
+ // distinct, and this is intentional. Combining the two is not possible due
+ // to the difference in semantics and the need to act (see dirty flag
+ // below) differently on QQuickPath-related changes.
+
+ connect(this, &QQuickPath::changed, [this]() {
+ Q_D(QQuickShapePath);
+ d->dirty |= QQuickShapePathPrivate::DirtyPath;
+ emit shapePathChanged();
+ });
+}
+
+QQuickShapePath::~QQuickShapePath()
+{
+}
+
+/*!
+ \qmlproperty color QtQuick.Shapes::ShapePath::strokeColor
+
+ This property holds the stroking color.
+
+ When set to \c transparent, no stroking occurs.
+
+ The default value is \c white.
+ */
+
+QColor QQuickShapePath::strokeColor() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.strokeColor;
+}
+
+void QQuickShapePath::setStrokeColor(const QColor &color)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.strokeColor != color) {
+ d->sfp.strokeColor = color;
+ d->dirty |= QQuickShapePathPrivate::DirtyStrokeColor;
+ emit strokeColorChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty color QtQuick.Shapes::ShapePath::strokeWidth
+
+ This property holds the stroke width.
+
+ When set to a negative value, no stroking occurs.
+
+ The default value is 1.
+ */
+
+qreal QQuickShapePath::strokeWidth() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.strokeWidth;
+}
+
+void QQuickShapePath::setStrokeWidth(qreal w)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.strokeWidth != w) {
+ d->sfp.strokeWidth = w;
+ d->dirty |= QQuickShapePathPrivate::DirtyStrokeWidth;
+ emit strokeWidthChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty color QtQuick.Shapes::ShapePath::fillColor
+
+ This property holds the fill color.
+
+ When set to \c transparent, no filling occurs.
+
+ The default value is \c white.
+ */
+
+QColor QQuickShapePath::fillColor() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.fillColor;
+}
+
+void QQuickShapePath::setFillColor(const QColor &color)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.fillColor != color) {
+ d->sfp.fillColor = color;
+ d->dirty |= QQuickShapePathPrivate::DirtyFillColor;
+ emit fillColorChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::ShapePath::fillRule
+
+ This property holds the fill rule. The default value is
+ ShapePath.OddEvenFill. For an example on fill rules, see
+ QPainterPath::setFillRule().
+
+ \list
+ \li ShapePath.OddEvenFill
+ \li ShapePath.WindingFill
+ \endlist
+ */
+
+QQuickShapePath::FillRule QQuickShapePath::fillRule() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.fillRule;
+}
+
+void QQuickShapePath::setFillRule(FillRule fillRule)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.fillRule != fillRule) {
+ d->sfp.fillRule = fillRule;
+ d->dirty |= QQuickShapePathPrivate::DirtyFillRule;
+ emit fillRuleChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::ShapePath::joinStyle
+
+ This property defines how joins between two connected lines are drawn. The
+ default value is ShapePath.BevelJoin.
+
+ \list
+ \li ShapePath.MiterJoin - The outer edges of the lines are extended to meet at an angle, and this area is filled.
+ \li ShapePath.BevelJoin - The triangular notch between the two lines is filled.
+ \li ShapePath.RoundJoin - A circular arc between the two lines is filled.
+ \endlist
+ */
+
+QQuickShapePath::JoinStyle QQuickShapePath::joinStyle() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.joinStyle;
+}
+
+void QQuickShapePath::setJoinStyle(JoinStyle style)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.joinStyle != style) {
+ d->sfp.joinStyle = style;
+ d->dirty |= QQuickShapePathPrivate::DirtyStyle;
+ emit joinStyleChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty int QtQuick.Shapes::ShapePath::miterLimit
+
+ When ShapePath.joinStyle is set to ShapePath.MiterJoin, this property
+ specifies how far the miter join can extend from the join point.
+
+ The default value is 2.
+ */
+
+int QQuickShapePath::miterLimit() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.miterLimit;
+}
+
+void QQuickShapePath::setMiterLimit(int limit)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.miterLimit != limit) {
+ d->sfp.miterLimit = limit;
+ d->dirty |= QQuickShapePathPrivate::DirtyStyle;
+ emit miterLimitChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::ShapePath::capStyle
+
+ This property defines how the end points of lines are drawn. The
+ default value is ShapePath.SquareCap.
+
+ \list
+ \li ShapePath.FlatCap - A square line end that does not cover the end point of the line.
+ \li ShapePath.SquareCap - A square line end that covers the end point and extends beyond it by half the line width.
+ \li ShapePath.RoundCap - A rounded line end.
+ \endlist
+ */
+
+QQuickShapePath::CapStyle QQuickShapePath::capStyle() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.capStyle;
+}
+
+void QQuickShapePath::setCapStyle(CapStyle style)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.capStyle != style) {
+ d->sfp.capStyle = style;
+ d->dirty |= QQuickShapePathPrivate::DirtyStyle;
+ emit capStyleChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::ShapePath::strokeStyle
+
+ This property defines the style of stroking. The default value is
+ ShapePath.SolidLine.
+
+ \list
+ \li ShapePath.SolidLine - A plain line.
+ \li ShapePath.DashLine - Dashes separated by a few pixels.
+ \endlist
+ */
+
+QQuickShapePath::StrokeStyle QQuickShapePath::strokeStyle() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.strokeStyle;
+}
+
+void QQuickShapePath::setStrokeStyle(StrokeStyle style)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.strokeStyle != style) {
+ d->sfp.strokeStyle = style;
+ d->dirty |= QQuickShapePathPrivate::DirtyDash;
+ emit strokeStyleChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty real QtQuick.Shapes::ShapePath::dashOffset
+
+ This property defines the starting point on the dash pattern, measured in
+ units used to specify the dash pattern.
+
+ The default value is 0.
+
+ \sa QPen::setDashOffset()
+ */
+
+qreal QQuickShapePath::dashOffset() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.dashOffset;
+}
+
+void QQuickShapePath::setDashOffset(qreal offset)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.dashOffset != offset) {
+ d->sfp.dashOffset = offset;
+ d->dirty |= QQuickShapePathPrivate::DirtyDash;
+ emit dashOffsetChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty list<real> QtQuick.Shapes::ShapePath::dashPattern
+
+ This property defines the dash pattern when ShapePath.strokeStyle is set
+ to ShapePath.DashLine. The pattern must be specified as an even number of
+ positive entries where the entries 1, 3, 5... are the dashes and 2, 4, 6...
+ are the spaces. The pattern is specified in units of the pen's width.
+
+ The default value is (4, 2), meaning a dash of 4 * ShapePath.strokeWidth
+ pixels followed by a space of 2 * ShapePath.strokeWidth pixels.
+
+ \sa QPen::setDashPattern()
+ */
+
+QVector<qreal> QQuickShapePath::dashPattern() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.dashPattern;
+}
+
+void QQuickShapePath::setDashPattern(const QVector<qreal> &array)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.dashPattern != array) {
+ d->sfp.dashPattern = array;
+ d->dirty |= QQuickShapePathPrivate::DirtyDash;
+ emit dashPatternChanged();
+ emit shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty ShapeGradient QtQuick.Shapes::ShapePath::fillGradient
+
+ This property defines the fill gradient. By default no gradient is enabled
+ and the value is \c null. In this case the fill uses a solid color based on
+ the value of ShapePath.fillColor.
+
+ When set, ShapePath.fillColor is ignored and filling is done using one of
+ the ShapeGradient subtypes.
+
+ \note The Gradient type cannot be used here. Rather, prefer using one of the
+ advanced subtypes, like LinearGradient.
+ */
+
+QQuickShapeGradient *QQuickShapePath::fillGradient() const
+{
+ Q_D(const QQuickShapePath);
+ return d->sfp.fillGradient;
+}
+
+void QQuickShapePath::setFillGradient(QQuickShapeGradient *gradient)
+{
+ Q_D(QQuickShapePath);
+ if (d->sfp.fillGradient != gradient) {
+ if (d->sfp.fillGradient)
+ qmlobject_disconnect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
+ this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
+ d->sfp.fillGradient = gradient;
+ if (d->sfp.fillGradient)
+ qmlobject_connect(d->sfp.fillGradient, QQuickShapeGradient, SIGNAL(updated()),
+ this, QQuickShapePath, SLOT(_q_fillGradientChanged()));
+ d->dirty |= QQuickShapePathPrivate::DirtyFillGradient;
+ emit shapePathChanged();
+ }
+}
+
+void QQuickShapePathPrivate::_q_fillGradientChanged()
+{
+ Q_Q(QQuickShapePath);
+ dirty |= DirtyFillGradient;
+ emit q->shapePathChanged();
+}
+
+void QQuickShapePath::resetFillGradient()
+{
+ setFillGradient(nullptr);
+}
+
+/*!
+ \qmltype Shape
+ \instantiates QQuickShape
+ \inqmlmodule QtQuick.Shapes
+ \ingroup qtquick-paths
+ \ingroup qtquick-views
+ \inherits Item
+ \brief Renders a path
+ \since 5.10
+
+ Renders a path either by generating geometry via QPainterPath and manual
+ triangulation or by using a GPU vendor extension like \c{GL_NV_path_rendering}.
+
+ This approach is different from rendering shapes via QQuickPaintedItem or
+ the 2D Canvas because the path never gets rasterized in software. Therefore
+ Shape is suitable for creating shapes spreading over larger areas of the
+ screen, avoiding the performance penalty for texture uploads or framebuffer
+ blits. In addition, the declarative API allows manipulating, binding to,
+ and even animating the path element properties like starting and ending
+ position, the control points, etc.
+
+ The types for specifying path elements are shared between \l PathView and
+ Shape. However, not all Shape implementations support all path
+ element types, while some may not make sense for PathView. Shape's
+ currently supported subset is: PathMove, PathLine, PathQuad, PathCubic,
+ PathArc, PathSvg.
+
+ See \l Path for a detailed overview of the supported path elements.
+
+ \code
+ Shape {
+ width: 200
+ height: 150
+ anchors.centerIn: parent
+ ShapePath {
+ strokeWidth: 4
+ strokeColor: "red"
+ fillGradient: LinearGradient {
+ x1: 20; y1: 20
+ x2: 180; y2: 130
+ GradientStop { position: 0; color: "blue" }
+ GradientStop { position: 0.2; color: "green" }
+ GradientStop { position: 0.4; color: "red" }
+ GradientStop { position: 0.6; color: "yellow" }
+ GradientStop { position: 1; color: "cyan" }
+ }
+ strokeStyle: ShapePath.DashLine
+ dashPattern: [ 1, 4 ]
+ startX: 20; startY: 20
+ PathLine { x: 180; y: 130 }
+ PathLine { x: 20; y: 130 }
+ PathLine { x: 20; y: 20 }
+ }
+ }
+ \endcode
+
+ \image pathitem-code-example.png
+
+ Like \l Item, Shape also allows any visual or non-visual objects to be
+ declared as children. ShapePath objects are handled specially. This is
+ useful since it allows adding visual items, like \l Rectangle or \l Image,
+ and non-visual objects, like \l Timer directly as children of Shape.
+
+ \note It is important to be aware of performance implications, in particular
+ when the application is running on the generic Shape implementation due to
+ not having support for accelerated path rendering. The geometry generation
+ happens entirely on the CPU in this case, and this is potentially
+ expensive. Changing the set of path elements, changing the properties of
+ these elements, or changing certain properties of the Shape itself all lead
+ to retriangulation of the affected elements on every change. Therefore,
+ applying animation to such properties can affect performance on less
+ powerful systems. If animating properties other than stroke and fill colors
+ is a must, it is recommended to target systems providing
+ \c{GL_NV_path_rendering} where the cost of path property changes is much
+ smaller.
+
+ \note However, the data-driven, declarative nature of the Shape API often
+ means better cacheability for the underlying CPU and GPU resources. A
+ property change in one ShapePath will only lead to reprocessing the affected
+ ShapePath, leaving other parts of the Shape unchanged. Therefore, a heavily
+ changing (for example, animating) property can often result in a lower
+ overall system load than with imperative painting approaches (for example,
+ QPainter).
+
+ The following list summarizes the available Shape rendering approaches:
+
+ \list
+
+ \li When running with the default, OpenGL backend of Qt Quick, both the
+ generic, triangulation-based and the NVIDIA-specific
+ \c{GL_NV_path_rendering} methods are available. The choice is made at
+ runtime, depending on the graphics driver's capabilities. When this is not
+ desired, applications can force using the generic method by setting the
+ Shape.vendorExtensionsEnabled property to \c false.
+
+ \li The \c software backend is fully supported. The path is rendered via
+ QPainter::strokePath() and QPainter::fillPath() in this case.
+
+ \li The Direct 3D 12 backend is not currently supported.
+
+ \li The OpenVG backend is not currently supported.
+
+ \endlist
+
+ \sa Path, PathMove, PathLine, PathQuad, PathCubic, PathArc, PathSvg
+*/
+
+QQuickShapePrivate::QQuickShapePrivate()
+ : componentComplete(true),
+ spChanged(false),
+ rendererType(QQuickShape::UnknownRenderer),
+ async(false),
+ status(QQuickShape::Null),
+ renderer(nullptr),
+ enableVendorExts(true)
+{
+}
+
+QQuickShapePrivate::~QQuickShapePrivate()
+{
+ delete renderer;
+}
+
+void QQuickShapePrivate::_q_shapePathChanged()
+{
+ Q_Q(QQuickShape);
+ spChanged = true;
+ q->polish();
+}
+
+void QQuickShapePrivate::setStatus(QQuickShape::Status newStatus)
+{
+ Q_Q(QQuickShape);
+ if (status != newStatus) {
+ status = newStatus;
+ emit q->statusChanged();
+ }
+}
+
+QQuickShape::QQuickShape(QQuickItem *parent)
+ : QQuickItem(*(new QQuickShapePrivate), parent)
+{
+ setFlag(ItemHasContents);
+}
+
+QQuickShape::~QQuickShape()
+{
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::Shape::rendererType
+
+ This property determines which path rendering backend is active.
+
+ \list
+
+ \li Shape.UnknownRenderer - The renderer is unknown.
+
+ \li Shape.GeometryRenderer - The generic, driver independent solution
+ for OpenGL. Uses the same CPU-based triangulation approach as QPainter's
+ OpenGL 2 paint engine. This is the default on non-NVIDIA hardware when the
+ default, OpenGL Qt Quick scenegraph backend is in use.
+
+ \li Shape.NvprRenderer - Path items are rendered by performing OpenGL
+ calls using the \c{GL_NV_path_rendering} extension. This is the default on
+ NVIDIA hardware when the default, OpenGL Qt Quick scenegraph backend is in
+ use.
+
+ \li Shape.SoftwareRenderer - Pure QPainter drawing using the raster
+ paint engine. This is the default, and only, option when the Qt Quick
+ scenegraph is running with the \c software backend.
+
+ \endlist
+*/
+
+QQuickShape::RendererType QQuickShape::rendererType() const
+{
+ Q_D(const QQuickShape);
+ return d->rendererType;
+}
+
+/*!
+ \qmlproperty bool QtQuick.Shapes::Shape::asynchronous
+
+ When Shape.rendererType is Shape.GeometryRenderer, the input path is
+ triangulated on the CPU during the polishing phase of the Shape. This is
+ potentially expensive. To offload this work to separate worker threads, set
+ this property to \c true.
+
+ When enabled, making a Shape visible will not wait for the content to
+ become available. Instead, the gui/main thread is not blocked and the
+ results of the path rendering are shown only when all the asynchronous work
+ has been finished.
+
+ The default value is \c false.
+ */
+
+bool QQuickShape::asynchronous() const
+{
+ Q_D(const QQuickShape);
+ return d->async;
+}
+
+void QQuickShape::setAsynchronous(bool async)
+{
+ Q_D(QQuickShape);
+ if (d->async != async) {
+ d->async = async;
+ emit asynchronousChanged();
+ if (d->componentComplete)
+ d->_q_shapePathChanged();
+ }
+}
+
+/*!
+ \qmlproperty bool QtQuick.Shapes::Shape::vendorExtensionsEnabled
+
+ This property controls the usage of non-standard OpenGL extensions like
+ GL_NV_path_rendering. To disable Shape.NvprRenderer and force a uniform
+ behavior regardless of the graphics card and drivers, set this property to
+ \c false.
+
+ The default value is \c true.
+ */
+
+bool QQuickShape::vendorExtensionsEnabled() const
+{
+ Q_D(const QQuickShape);
+ return d->enableVendorExts;
+}
+
+void QQuickShape::setVendorExtensionsEnabled(bool enable)
+{
+ Q_D(QQuickShape);
+ if (d->enableVendorExts != enable) {
+ d->enableVendorExts = enable;
+ emit vendorExtensionsEnabledChanged();
+ }
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::Shape::status
+
+ This property determines the status of the Shape and is relevant when
+ Shape.asynchronous is set to \c true.
+
+ \list
+
+ \li Shape.Null - Not yet initialized.
+
+ \li Shape.Ready - The Shape has finished processing.
+
+ \li Shape.Processing - The path is being processed.
+
+ \endlist
+ */
+
+QQuickShape::Status QQuickShape::status() const
+{
+ Q_D(const QQuickShape);
+ return d->status;
+}
+
+static void vpe_append(QQmlListProperty<QObject> *property, QObject *obj)
+{
+ QQuickShape *item = static_cast<QQuickShape *>(property->object);
+ QQuickShapePrivate *d = QQuickShapePrivate::get(item);
+ QQuickShapePath *path = qobject_cast<QQuickShapePath *>(obj);
+ if (path)
+ d->qmlData.sp.append(path);
+
+ QQuickItemPrivate::data_append(property, obj);
+
+ if (path && d->componentComplete) {
+ QObject::connect(path, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
+ d->_q_shapePathChanged();
+ }
+}
+
+static void vpe_clear(QQmlListProperty<QObject> *property)
+{
+ QQuickShape *item = static_cast<QQuickShape *>(property->object);
+ QQuickShapePrivate *d = QQuickShapePrivate::get(item);
+
+ for (QQuickShapePath *p : d->qmlData.sp)
+ QObject::disconnect(p, SIGNAL(shapePathChanged()), item, SLOT(_q_shapePathChanged()));
+
+ d->qmlData.sp.clear();
+
+ QQuickItemPrivate::data_clear(property);
+
+ if (d->componentComplete)
+ d->_q_shapePathChanged();
+}
+
+/*!
+ \qmlproperty list<Object> QtQuick.Shapes::Shape::data
+
+ This property holds the ShapePath objects that define the contents of the
+ Shape. It can also contain any other type of objects, since Shape, like Item,
+ allows adding any visual or non-visual objects as children.
+
+ \default
+ */
+
+QQmlListProperty<QObject> QQuickShape::data()
+{
+ return QQmlListProperty<QObject>(this,
+ nullptr,
+ vpe_append,
+ QQuickItemPrivate::data_count,
+ QQuickItemPrivate::data_at,
+ vpe_clear);
+}
+
+void QQuickShape::classBegin()
+{
+ Q_D(QQuickShape);
+ d->componentComplete = false;
+}
+
+void QQuickShape::componentComplete()
+{
+ Q_D(QQuickShape);
+ d->componentComplete = true;
+
+ for (QQuickShapePath *p : d->qmlData.sp)
+ connect(p, SIGNAL(shapePathChanged()), this, SLOT(_q_shapePathChanged()));
+
+ d->_q_shapePathChanged();
+}
+
+void QQuickShape::updatePolish()
+{
+ Q_D(QQuickShape);
+
+ if (!d->spChanged)
+ return;
+
+ d->spChanged = false;
+
+ if (!d->renderer) {
+ d->createRenderer();
+ if (!d->renderer)
+ return;
+ emit rendererChanged();
+ }
+
+ // endSync() is where expensive calculations may happen (or get kicked off
+ // on worker threads), depending on the backend. Therefore do this only
+ // when the item is visible.
+ if (isVisible())
+ d->sync();
+
+ update();
+}
+
+void QQuickShape::itemChange(ItemChange change, const ItemChangeData &data)
+{
+ Q_D(QQuickShape);
+
+ // sync may have been deferred; do it now if the item became visible
+ if (change == ItemVisibleHasChanged && data.boolValue)
+ d->_q_shapePathChanged();
+
+ QQuickItem::itemChange(change, data);
+}
+
+QSGNode *QQuickShape::updatePaintNode(QSGNode *node, UpdatePaintNodeData *)
+{
+ // Called on the render thread, with the gui thread blocked. We can now
+ // safely access gui thread data.
+
+ Q_D(QQuickShape);
+ if (d->renderer) {
+ if (!node)
+ node = d->createNode();
+ d->renderer->updateNode();
+ }
+ return node;
+}
+
+// the renderer object lives on the gui thread
+void QQuickShapePrivate::createRenderer()
+{
+ Q_Q(QQuickShape);
+ QSGRendererInterface *ri = q->window()->rendererInterface();
+ if (!ri)
+ return;
+
+ switch (ri->graphicsApi()) {
+#if QT_CONFIG(opengl)
+ case QSGRendererInterface::OpenGL:
+ if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) {
+ rendererType = QQuickShape::NvprRenderer;
+ renderer = new QQuickShapeNvprRenderer;
+ } else {
+ rendererType = QQuickShape::GeometryRenderer;
+ renderer = new QQuickShapeGenericRenderer(q);
+ }
+ break;
+#endif
+ case QSGRendererInterface::Software:
+ rendererType = QQuickShape::SoftwareRenderer;
+ renderer = new QQuickShapeSoftwareRenderer;
+ break;
+ default:
+ qWarning("No path backend for this graphics API yet");
+ break;
+ }
+}
+
+// the node lives on the render thread
+QSGNode *QQuickShapePrivate::createNode()
+{
+ Q_Q(QQuickShape);
+ QSGNode *node = nullptr;
+ if (!q->window())
+ return node;
+ QSGRendererInterface *ri = q->window()->rendererInterface();
+ if (!ri)
+ return node;
+
+ switch (ri->graphicsApi()) {
+#if QT_CONFIG(opengl)
+ case QSGRendererInterface::OpenGL:
+ if (enableVendorExts && QQuickShapeNvprRenderNode::isSupported()) {
+ node = new QQuickShapeNvprRenderNode;
+ static_cast<QQuickShapeNvprRenderer *>(renderer)->setNode(
+ static_cast<QQuickShapeNvprRenderNode *>(node));
+ } else {
+ node = new QQuickShapeGenericNode;
+ static_cast<QQuickShapeGenericRenderer *>(renderer)->setRootNode(
+ static_cast<QQuickShapeGenericNode *>(node));
+ }
+ break;
+#endif
+ case QSGRendererInterface::Software:
+ node = new QQuickShapeSoftwareRenderNode(q);
+ static_cast<QQuickShapeSoftwareRenderer *>(renderer)->setNode(
+ static_cast<QQuickShapeSoftwareRenderNode *>(node));
+ break;
+ default:
+ qWarning("No path backend for this graphics API yet");
+ break;
+ }
+
+ return node;
+}
+
+static void q_asyncShapeReady(void *data)
+{
+ QQuickShapePrivate *self = static_cast<QQuickShapePrivate *>(data);
+ self->setStatus(QQuickShape::Ready);
+}
+
+void QQuickShapePrivate::sync()
+{
+ const bool useAsync = async && renderer->flags().testFlag(QQuickAbstractPathRenderer::SupportsAsync);
+ if (useAsync) {
+ setStatus(QQuickShape::Processing);
+ renderer->setAsyncCallback(q_asyncShapeReady, this);
+ }
+
+ if (!jsData.isValid()) {
+ // Standard route: The path and stroke/fill parameters are provided via
+ // QML elements.
+ const int count = qmlData.sp.count();
+ renderer->beginSync(count);
+
+ for (int i = 0; i < count; ++i) {
+ QQuickShapePath *p = qmlData.sp[i];
+ int &dirty(QQuickShapePathPrivate::get(p)->dirty);
+
+ if (dirty & QQuickShapePathPrivate::DirtyPath)
+ renderer->setPath(i, p);
+ if (dirty & QQuickShapePathPrivate::DirtyStrokeColor)
+ renderer->setStrokeColor(i, p->strokeColor());
+ if (dirty & QQuickShapePathPrivate::DirtyStrokeWidth)
+ renderer->setStrokeWidth(i, p->strokeWidth());
+ if (dirty & QQuickShapePathPrivate::DirtyFillColor)
+ renderer->setFillColor(i, p->fillColor());
+ if (dirty & QQuickShapePathPrivate::DirtyFillRule)
+ renderer->setFillRule(i, p->fillRule());
+ if (dirty & QQuickShapePathPrivate::DirtyStyle) {
+ renderer->setJoinStyle(i, p->joinStyle(), p->miterLimit());
+ renderer->setCapStyle(i, p->capStyle());
+ }
+ if (dirty & QQuickShapePathPrivate::DirtyDash)
+ renderer->setStrokeStyle(i, p->strokeStyle(), p->dashOffset(), p->dashPattern());
+ if (dirty & QQuickShapePathPrivate::DirtyFillGradient)
+ renderer->setFillGradient(i, p->fillGradient());
+
+ dirty = 0;
+ }
+
+ renderer->endSync(useAsync);
+ } else {
+
+ // ### there is no public API to reach this code path atm
+ Q_UNREACHABLE();
+
+ // Path and stroke/fill params provided from JavaScript. This avoids
+ // QObjects at the expense of not supporting changes afterwards.
+ const int count = jsData.paths.count();
+ renderer->beginSync(count);
+
+ for (int i = 0; i < count; ++i) {
+ renderer->setJSPath(i, jsData.paths[i]);
+ const QQuickShapeStrokeFillParams sfp(jsData.sfp[i]);
+ renderer->setStrokeColor(i, sfp.strokeColor);
+ renderer->setStrokeWidth(i, sfp.strokeWidth);
+ renderer->setFillColor(i, sfp.fillColor);
+ renderer->setFillRule(i, sfp.fillRule);
+ renderer->setJoinStyle(i, sfp.joinStyle, sfp.miterLimit);
+ renderer->setCapStyle(i, sfp.capStyle);
+ renderer->setStrokeStyle(i, sfp.strokeStyle, sfp.dashOffset, sfp.dashPattern);
+ renderer->setFillGradient(i, sfp.fillGradient);
+ }
+
+ renderer->endSync(useAsync);
+ }
+
+ if (!useAsync)
+ setStatus(QQuickShape::Ready);
+}
+
+// ***** gradient support *****
+
+/*!
+ \qmltype ShapeGradient
+ \instantiates QQuickShapeGradient
+ \inqmlmodule QtQuick.Shapes
+ \ingroup qtquick-paths
+ \ingroup qtquick-views
+ \inherits Gradient
+ \brief Base type of Shape fill gradients
+ \since 5.10
+
+ This is an abstract base class for gradients like LinearGradient and
+ cannot be created directly. It extends \l Gradient with properties like the
+ spread mode.
+ */
+
+QQuickShapeGradient::QQuickShapeGradient(QObject *parent)
+ : QQuickGradient(parent),
+ m_spread(PadSpread)
+{
+}
+
+/*!
+ \qmlproperty enumeration QtQuick.Shapes::ShapeGradient::spread
+
+ Specifies how the area outside the gradient area should be filled. The
+ default value is ShapeGradient.PadSpread.
+
+ \list
+ \li ShapeGradient.PadSpread - The area is filled with the closest stop color.
+ \li ShapeGradient.RepeatSpread - The gradient is repeated outside the gradient area.
+ \li ShapeGradient.ReflectSpread - The gradient is reflected outside the gradient area.
+ \endlist
+ */
+
+QQuickShapeGradient::SpreadMode QQuickShapeGradient::spread() const
+{
+ return m_spread;
+}
+
+void QQuickShapeGradient::setSpread(SpreadMode mode)
+{
+ if (m_spread != mode) {
+ m_spread = mode;
+ emit spreadChanged();
+ emit updated();
+ }
+}
+
+/*!
+ \qmltype LinearGradient
+ \instantiates QQuickShapeLinearGradient
+ \inqmlmodule QtQuick.Shapes
+ \ingroup qtquick-paths
+ \ingroup qtquick-views
+ \inherits ShapeGradient
+ \brief Linear gradient
+ \since 5.10
+
+ Linear gradients interpolate colors between start and end points. Outside
+ these points the gradient is either padded, reflected or repeated depending
+ on the spread type.
+
+ \note LinearGradient is not compatible with Rectangle items that only
+ support Gradient. This type is to be used with Shape.
+
+ \sa QLinearGradient
+ */
+
+QQuickShapeLinearGradient::QQuickShapeLinearGradient(QObject *parent)
+ : QQuickShapeGradient(parent)
+{
+}
+
+/*!
+ \qmlproperty real QtQuick.Shapes::LinearGradient::x1
+ \qmlproperty real QtQuick.Shapes::LinearGradient::y1
+ \qmlproperty real QtQuick.Shapes::LinearGradient::x2
+ \qmlproperty real QtQuick.Shapes::LinearGradient::y2
+
+ These properties define the start and end points between which color
+ interpolation occurs. By default both the stard and end points are set to
+ (0, 0).
+ */
+
+qreal QQuickShapeLinearGradient::x1() const
+{
+ return m_start.x();
+}
+
+void QQuickShapeLinearGradient::setX1(qreal v)
+{
+ if (m_start.x() != v) {
+ m_start.setX(v);
+ emit x1Changed();
+ emit updated();
+ }
+}
+
+qreal QQuickShapeLinearGradient::y1() const
+{
+ return m_start.y();
+}
+
+void QQuickShapeLinearGradient::setY1(qreal v)
+{
+ if (m_start.y() != v) {
+ m_start.setY(v);
+ emit y1Changed();
+ emit updated();
+ }
+}
+
+qreal QQuickShapeLinearGradient::x2() const
+{
+ return m_end.x();
+}
+
+void QQuickShapeLinearGradient::setX2(qreal v)
+{
+ if (m_end.x() != v) {
+ m_end.setX(v);
+ emit x2Changed();
+ emit updated();
+ }
+}
+
+qreal QQuickShapeLinearGradient::y2() const
+{
+ return m_end.y();
+}
+
+void QQuickShapeLinearGradient::setY2(qreal v)
+{
+ if (m_end.y() != v) {
+ m_end.setY(v);
+ emit y2Changed();
+ emit updated();
+ }
+}
+
+#if QT_CONFIG(opengl)
+
+// contexts sharing with each other get the same cache instance
+class QQuickShapeGradientCacheWrapper
+{
+public:
+ QQuickShapeGradientCache *get(QOpenGLContext *context)
+ {
+ return m_resource.value<QQuickShapeGradientCache>(context);
+ }
+
+private:
+ QOpenGLMultiGroupSharedResource m_resource;
+};
+
+QQuickShapeGradientCache *QQuickShapeGradientCache::currentCache()
+{
+ static QQuickShapeGradientCacheWrapper qt_path_gradient_caches;
+ return qt_path_gradient_caches.get(QOpenGLContext::currentContext());
+}
+
+// let QOpenGLContext manage the lifetime of the cached textures
+QQuickShapeGradientCache::~QQuickShapeGradientCache()
+{
+ m_cache.clear();
+}
+
+void QQuickShapeGradientCache::invalidateResource()
+{
+ m_cache.clear();
+}
+
+void QQuickShapeGradientCache::freeResource(QOpenGLContext *)
+{
+ qDeleteAll(m_cache);
+ m_cache.clear();
+}
+
+static void generateGradientColorTable(const QQuickShapeGradientCache::GradientDesc &gradient,
+ uint *colorTable, int size, float opacity)
+{
+ int pos = 0;
+ const QGradientStops &s = gradient.stops;
+ const bool colorInterpolation = true;
+
+ uint alpha = qRound(opacity * 256);
+ uint current_color = ARGB_COMBINE_ALPHA(s[0].second.rgba(), alpha);
+ qreal incr = 1.0 / qreal(size);
+ qreal fpos = 1.5 * incr;
+ colorTable[pos++] = ARGB2RGBA(qPremultiply(current_color));
+
+ while (fpos <= s.first().first) {
+ colorTable[pos] = colorTable[pos - 1];
+ pos++;
+ fpos += incr;
+ }
+
+ if (colorInterpolation)
+ current_color = qPremultiply(current_color);
+
+ const int sLast = s.size() - 1;
+ for (int i = 0; i < sLast; ++i) {
+ qreal delta = 1/(s[i+1].first - s[i].first);
+ uint next_color = ARGB_COMBINE_ALPHA(s[i + 1].second.rgba(), alpha);
+ if (colorInterpolation)
+ next_color = qPremultiply(next_color);
+
+ while (fpos < s[i+1].first && pos < size) {
+ int dist = int(256 * ((fpos - s[i].first) * delta));
+ int idist = 256 - dist;
+ if (colorInterpolation)
+ colorTable[pos] = ARGB2RGBA(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist));
+ else
+ colorTable[pos] = ARGB2RGBA(qPremultiply(INTERPOLATE_PIXEL_256(current_color, idist, next_color, dist)));
+ ++pos;
+ fpos += incr;
+ }
+ current_color = next_color;
+ }
+
+ Q_ASSERT(s.size() > 0);
+
+ uint last_color = ARGB2RGBA(qPremultiply(ARGB_COMBINE_ALPHA(s[sLast].second.rgba(), alpha)));
+ for ( ; pos < size; ++pos)
+ colorTable[pos] = last_color;
+
+ colorTable[size-1] = last_color;
+}
+
+QSGTexture *QQuickShapeGradientCache::get(const GradientDesc &grad)
+{
+ QSGPlainTexture *tx = m_cache[grad];
+ if (!tx) {
+ QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
+ GLuint id;
+ f->glGenTextures(1, &id);
+ f->glBindTexture(GL_TEXTURE_2D, id);
+ static const uint W = 1024; // texture size is 1024x1
+ uint buf[W];
+ generateGradientColorTable(grad, buf, W, 1.0f);
+ f->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, W, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
+ tx = new QSGPlainTexture;
+ tx->setTextureId(id);
+ switch (grad.spread) {
+ case QQuickShapeGradient::PadSpread:
+ tx->setHorizontalWrapMode(QSGTexture::ClampToEdge);
+ tx->setVerticalWrapMode(QSGTexture::ClampToEdge);
+ break;
+ case QQuickShapeGradient::RepeatSpread:
+ tx->setHorizontalWrapMode(QSGTexture::Repeat);
+ tx->setVerticalWrapMode(QSGTexture::Repeat);
+ break;
+ case QQuickShapeGradient::ReflectSpread:
+ tx->setHorizontalWrapMode(QSGTexture::MirroredRepeat);
+ tx->setVerticalWrapMode(QSGTexture::MirroredRepeat);
+ break;
+ default:
+ qWarning("Unknown gradient spread mode %d", grad.spread);
+ break;
+ }
+ m_cache[grad] = tx;
+ }
+ return tx;
+}
+
+#endif // QT_CONFIG(opengl)
+
+QT_END_NAMESPACE
+
+#include "moc_qquickshape_p.cpp"
diff --git a/src/imports/shapes/qquickshape_p.h b/src/imports/shapes/qquickshape_p.h
new file mode 100644
index 0000000000..db0b449c6c
--- /dev/null
+++ b/src/imports/shapes/qquickshape_p.h
@@ -0,0 +1,287 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKSHAPE_P_H
+#define QQUICKSHAPE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickitem.h"
+
+#include <private/qtquickglobal_p.h>
+#include <private/qquickpath_p_p.h>
+#include <private/qquickrectangle_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickShapePathPrivate;
+class QQuickShapePrivate;
+
+class QQuickShapeGradient : public QQuickGradient
+{
+ Q_OBJECT
+ Q_PROPERTY(SpreadMode spread READ spread WRITE setSpread NOTIFY spreadChanged)
+ Q_CLASSINFO("DefaultProperty", "stops")
+
+public:
+ enum SpreadMode {
+ PadSpread,
+ RepeatSpread,
+ ReflectSpread
+ };
+ Q_ENUM(SpreadMode)
+
+ QQuickShapeGradient(QObject *parent = nullptr);
+
+ SpreadMode spread() const;
+ void setSpread(SpreadMode mode);
+
+signals:
+ void spreadChanged();
+
+private:
+ SpreadMode m_spread;
+};
+
+class QQuickShapeLinearGradient : public QQuickShapeGradient
+{
+ Q_OBJECT
+ Q_PROPERTY(qreal x1 READ x1 WRITE setX1 NOTIFY x1Changed)
+ Q_PROPERTY(qreal y1 READ y1 WRITE setY1 NOTIFY y1Changed)
+ Q_PROPERTY(qreal x2 READ x2 WRITE setX2 NOTIFY x2Changed)
+ Q_PROPERTY(qreal y2 READ y2 WRITE setY2 NOTIFY y2Changed)
+ Q_CLASSINFO("DefaultProperty", "stops")
+
+public:
+ QQuickShapeLinearGradient(QObject *parent = nullptr);
+
+ qreal x1() const;
+ void setX1(qreal v);
+ qreal y1() const;
+ void setY1(qreal v);
+ qreal x2() const;
+ void setX2(qreal v);
+ qreal y2() const;
+ void setY2(qreal v);
+
+signals:
+ void x1Changed();
+ void y1Changed();
+ void x2Changed();
+ void y2Changed();
+
+private:
+ QPointF m_start;
+ QPointF m_end;
+};
+
+class QQuickShapePath : public QQuickPath
+{
+ Q_OBJECT
+
+ Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor NOTIFY strokeColorChanged)
+ Q_PROPERTY(qreal strokeWidth READ strokeWidth WRITE setStrokeWidth NOTIFY strokeWidthChanged)
+ Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor NOTIFY fillColorChanged)
+ Q_PROPERTY(FillRule fillRule READ fillRule WRITE setFillRule NOTIFY fillRuleChanged)
+ Q_PROPERTY(JoinStyle joinStyle READ joinStyle WRITE setJoinStyle NOTIFY joinStyleChanged)
+ Q_PROPERTY(int miterLimit READ miterLimit WRITE setMiterLimit NOTIFY miterLimitChanged)
+ Q_PROPERTY(CapStyle capStyle READ capStyle WRITE setCapStyle NOTIFY capStyleChanged)
+ Q_PROPERTY(StrokeStyle strokeStyle READ strokeStyle WRITE setStrokeStyle NOTIFY strokeStyleChanged)
+ Q_PROPERTY(qreal dashOffset READ dashOffset WRITE setDashOffset NOTIFY dashOffsetChanged)
+ Q_PROPERTY(QVector<qreal> dashPattern READ dashPattern WRITE setDashPattern NOTIFY dashPatternChanged)
+ Q_PROPERTY(QQuickShapeGradient *fillGradient READ fillGradient WRITE setFillGradient RESET resetFillGradient)
+
+public:
+ enum FillRule {
+ OddEvenFill = Qt::OddEvenFill,
+ WindingFill = Qt::WindingFill
+ };
+ Q_ENUM(FillRule)
+
+ enum JoinStyle {
+ MiterJoin = Qt::MiterJoin,
+ BevelJoin = Qt::BevelJoin,
+ RoundJoin = Qt::RoundJoin
+ };
+ Q_ENUM(JoinStyle)
+
+ enum CapStyle {
+ FlatCap = Qt::FlatCap,
+ SquareCap = Qt::SquareCap,
+ RoundCap = Qt::RoundCap
+ };
+ Q_ENUM(CapStyle)
+
+ enum StrokeStyle {
+ SolidLine = Qt::SolidLine,
+ DashLine = Qt::DashLine
+ };
+ Q_ENUM(StrokeStyle)
+
+ QQuickShapePath(QObject *parent = nullptr);
+ ~QQuickShapePath();
+
+ QColor strokeColor() const;
+ void setStrokeColor(const QColor &color);
+
+ qreal strokeWidth() const;
+ void setStrokeWidth(qreal w);
+
+ QColor fillColor() const;
+ void setFillColor(const QColor &color);
+
+ FillRule fillRule() const;
+ void setFillRule(FillRule fillRule);
+
+ JoinStyle joinStyle() const;
+ void setJoinStyle(JoinStyle style);
+
+ int miterLimit() const;
+ void setMiterLimit(int limit);
+
+ CapStyle capStyle() const;
+ void setCapStyle(CapStyle style);
+
+ StrokeStyle strokeStyle() const;
+ void setStrokeStyle(StrokeStyle style);
+
+ qreal dashOffset() const;
+ void setDashOffset(qreal offset);
+
+ QVector<qreal> dashPattern() const;
+ void setDashPattern(const QVector<qreal> &array);
+
+ QQuickShapeGradient *fillGradient() const;
+ void setFillGradient(QQuickShapeGradient *gradient);
+ void resetFillGradient();
+
+Q_SIGNALS:
+ void shapePathChanged();
+ void strokeColorChanged();
+ void strokeWidthChanged();
+ void fillColorChanged();
+ void fillRuleChanged();
+ void joinStyleChanged();
+ void miterLimitChanged();
+ void capStyleChanged();
+ void strokeStyleChanged();
+ void dashOffsetChanged();
+ void dashPatternChanged();
+ void fillGradientChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickShapePath)
+ Q_DECLARE_PRIVATE(QQuickShapePath)
+ Q_PRIVATE_SLOT(d_func(), void _q_fillGradientChanged())
+};
+
+class QQuickShape : public QQuickItem
+{
+ Q_OBJECT
+ Q_PROPERTY(RendererType renderer READ rendererType NOTIFY rendererChanged)
+ Q_PROPERTY(bool asynchronous READ asynchronous WRITE setAsynchronous NOTIFY asynchronousChanged)
+ Q_PROPERTY(bool vendorExtensionsEnabled READ vendorExtensionsEnabled WRITE setVendorExtensionsEnabled NOTIFY vendorExtensionsEnabledChanged)
+ Q_PROPERTY(Status status READ status NOTIFY statusChanged)
+ Q_PROPERTY(QQmlListProperty<QObject> data READ data)
+ Q_CLASSINFO("DefaultProperty", "data")
+
+public:
+ enum RendererType {
+ UnknownRenderer,
+ GeometryRenderer,
+ NvprRenderer,
+ SoftwareRenderer
+ };
+ Q_ENUM(RendererType)
+
+ enum Status {
+ Null,
+ Ready,
+ Processing
+ };
+ Q_ENUM(Status)
+
+ QQuickShape(QQuickItem *parent = nullptr);
+ ~QQuickShape();
+
+ RendererType rendererType() const;
+
+ bool asynchronous() const;
+ void setAsynchronous(bool async);
+
+ bool vendorExtensionsEnabled() const;
+ void setVendorExtensionsEnabled(bool enable);
+
+ Status status() const;
+
+ QQmlListProperty<QObject> data();
+
+protected:
+ QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *) override;
+ void updatePolish() override;
+ void itemChange(ItemChange change, const ItemChangeData &data) override;
+ void componentComplete() override;
+ void classBegin() override;
+
+Q_SIGNALS:
+ void rendererChanged();
+ void asynchronousChanged();
+ void vendorExtensionsEnabledChanged();
+ void statusChanged();
+
+private:
+ Q_DISABLE_COPY(QQuickShape)
+ Q_DECLARE_PRIVATE(QQuickShape)
+ Q_PRIVATE_SLOT(d_func(), void _q_shapePathChanged())
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickShape)
+
+#endif // QQUICKSHAPE_P_H
diff --git a/src/imports/shapes/qquickshape_p_p.h b/src/imports/shapes/qquickshape_p_p.h
new file mode 100644
index 0000000000..888488efcd
--- /dev/null
+++ b/src/imports/shapes/qquickshape_p_p.h
@@ -0,0 +1,281 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKSHAPE_P_P_H
+#define QQUICKSHAPE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickshape_p.h"
+#include <QtQuick/private/qquickitem_p.h>
+#include <QPainterPath>
+#include <QColor>
+#include <QBrush>
+#include <private/qopenglcontext_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class QSGPlainTexture;
+
+struct QQuickShapePathCommands
+{
+ enum Command {
+ MoveTo,
+ LineTo,
+ QuadTo,
+ CubicTo,
+ ArcTo
+ };
+
+ QVector<Command> cmd;
+ QVector<float> coords;
+
+ QPainterPath toPainterPath() const;
+};
+
+struct QQuickShapeStrokeFillParams
+{
+ QQuickShapeStrokeFillParams();
+
+ QColor strokeColor;
+ qreal strokeWidth;
+ QColor fillColor;
+ QQuickShapePath::FillRule fillRule;
+ QQuickShapePath::JoinStyle joinStyle;
+ int miterLimit;
+ QQuickShapePath::CapStyle capStyle;
+ QQuickShapePath::StrokeStyle strokeStyle;
+ qreal dashOffset;
+ QVector<qreal> dashPattern;
+ QQuickShapeGradient *fillGradient;
+};
+
+class QQuickAbstractPathRenderer
+{
+public:
+ enum Flag {
+ SupportsAsync = 0x01
+ };
+ Q_DECLARE_FLAGS(Flags, Flag)
+
+ virtual ~QQuickAbstractPathRenderer() { }
+
+ // Gui thread
+ virtual void beginSync(int totalCount) = 0;
+ virtual void endSync(bool async) = 0;
+ virtual void setAsyncCallback(void (*)(void *), void *) { }
+ virtual Flags flags() const { return 0; }
+ // - QML API
+ virtual void setPath(int index, const QQuickPath *path) = 0;
+ // - JS API
+ virtual void setJSPath(int index, const QQuickShapePathCommands &path) = 0;
+ // - stroke/fill parameters
+ virtual void setStrokeColor(int index, const QColor &color) = 0;
+ virtual void setStrokeWidth(int index, qreal w) = 0;
+ virtual void setFillColor(int index, const QColor &color) = 0;
+ virtual void setFillRule(int index, QQuickShapePath::FillRule fillRule) = 0;
+ virtual void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) = 0;
+ virtual void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) = 0;
+ virtual void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern) = 0;
+ virtual void setFillGradient(int index, QQuickShapeGradient *gradient) = 0;
+
+ // Render thread, with gui blocked
+ virtual void updateNode() = 0;
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickAbstractPathRenderer::Flags)
+
+class QQuickShapePathPrivate : public QQuickPathPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickShapePath)
+
+public:
+ enum Dirty {
+ DirtyPath = 0x01,
+ DirtyStrokeColor = 0x02,
+ DirtyStrokeWidth = 0x04,
+ DirtyFillColor = 0x08,
+ DirtyFillRule = 0x10,
+ DirtyStyle = 0x20,
+ DirtyDash = 0x40,
+ DirtyFillGradient = 0x80,
+
+ DirtyAll = 0xFF
+ };
+
+ QQuickShapePathPrivate();
+
+ void _q_pathChanged();
+ void _q_fillGradientChanged();
+
+ static QQuickShapePathPrivate *get(QQuickShapePath *p) { return p->d_func(); }
+
+ int dirty;
+ QQuickShapeStrokeFillParams sfp;
+};
+
+class QQuickShapePrivate : public QQuickItemPrivate
+{
+ Q_DECLARE_PUBLIC(QQuickShape)
+
+public:
+ QQuickShapePrivate();
+ ~QQuickShapePrivate();
+
+ void createRenderer();
+ QSGNode *createNode();
+ void sync();
+
+ void _q_shapePathChanged();
+ void setStatus(QQuickShape::Status newStatus);
+
+ static QQuickShapePrivate *get(QQuickShape *item) { return item->d_func(); }
+
+ bool componentComplete;
+ bool spChanged;
+ QQuickShape::RendererType rendererType;
+ bool async;
+ QQuickShape::Status status;
+ QQuickAbstractPathRenderer *renderer;
+
+ struct {
+ QVector<QQuickShapePath *> sp;
+ } qmlData;
+
+ struct {
+ bool isValid() const { Q_ASSERT(paths.count() == sfp.count()); return !paths.isEmpty(); }
+ QVector<QQuickShapePathCommands> paths;
+ QVector<QQuickShapeStrokeFillParams> sfp;
+ } jsData;
+
+ bool enableVendorExts;
+};
+
+class QQuickShapePathObject : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQuickShapePathObject(QObject *parent = nullptr) : QObject(parent) { }
+
+ void setV4Engine(QV4::ExecutionEngine *engine);
+ QV4::ReturnedValue v4value() const { return m_v4value.value(); }
+
+ QQuickShapePath path;
+
+ void clear();
+
+private:
+ QV4::PersistentValue m_v4value;
+};
+
+class QQuickShapeStrokeFillParamsObject : public QObject
+{
+ Q_OBJECT
+
+public:
+ QQuickShapeStrokeFillParamsObject(QObject *parent = nullptr) : QObject(parent) { }
+
+ void setV4Engine(QV4::ExecutionEngine *engine);
+ QV4::ReturnedValue v4value() const { return m_v4value.value(); }
+
+ QQuickShapeStrokeFillParams sfp;
+ QV4::PersistentValue v4fillGradient;
+
+ void clear();
+
+private:
+ QV4::PersistentValue m_v4value;
+};
+
+#if QT_CONFIG(opengl)
+
+class QQuickShapeGradientCache : public QOpenGLSharedResource
+{
+public:
+ struct GradientDesc {
+ QGradientStops stops;
+ QPointF start;
+ QPointF end;
+ QQuickShapeGradient::SpreadMode spread;
+ bool operator==(const GradientDesc &other) const
+ {
+ return start == other.start && end == other.end && spread == other.spread
+ && stops == other.stops;
+ }
+ };
+
+ QQuickShapeGradientCache(QOpenGLContext *context) : QOpenGLSharedResource(context->shareGroup()) { }
+ ~QQuickShapeGradientCache();
+
+ void invalidateResource() override;
+ void freeResource(QOpenGLContext *) override;
+
+ QSGTexture *get(const GradientDesc &grad);
+
+ static QQuickShapeGradientCache *currentCache();
+
+private:
+ QHash<GradientDesc, QSGPlainTexture *> m_cache;
+};
+
+inline uint qHash(const QQuickShapeGradientCache::GradientDesc &v, uint seed = 0)
+{
+ uint h = seed;
+ h += v.start.x() + v.end.y() + v.spread;
+ for (int i = 0; i < 3 && i < v.stops.count(); ++i)
+ h += v.stops[i].second.rgba();
+ return h;
+}
+
+#endif // QT_CONFIG(opengl)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/imports/shapes/qquickshapegenericrenderer.cpp b/src/imports/shapes/qquickshapegenericrenderer.cpp
new file mode 100644
index 0000000000..ca5f2a0e91
--- /dev/null
+++ b/src/imports/shapes/qquickshapegenericrenderer.cpp
@@ -0,0 +1,784 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickshapegenericrenderer_p.h"
+#include <QtGui/private/qtriangulator_p.h>
+#include <QtGui/private/qtriangulatingstroker_p.h>
+#include <QThreadPool>
+
+#if QT_CONFIG(opengl)
+#include <QSGVertexColorMaterial>
+#include <QOpenGLContext>
+#include <QOffscreenSurface>
+#include <QtGui/private/qopenglextensions_p.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+static const qreal TRI_SCALE = 1;
+
+struct ColoredVertex // must match QSGGeometry::ColoredPoint2D
+{
+ float x, y;
+ QQuickShapeGenericRenderer::Color4ub color;
+ void set(float nx, float ny, QQuickShapeGenericRenderer::Color4ub ncolor)
+ {
+ x = nx; y = ny; color = ncolor;
+ }
+};
+
+static inline QQuickShapeGenericRenderer::Color4ub colorToColor4ub(const QColor &c)
+{
+ QQuickShapeGenericRenderer::Color4ub color = {
+ uchar(qRound(c.redF() * c.alphaF() * 255)),
+ uchar(qRound(c.greenF() * c.alphaF() * 255)),
+ uchar(qRound(c.blueF() * c.alphaF() * 255)),
+ uchar(qRound(c.alphaF() * 255))
+ };
+ return color;
+}
+
+QQuickShapeGenericStrokeFillNode::QQuickShapeGenericStrokeFillNode(QQuickWindow *window)
+ : m_geometry(new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(), 0, 0)),
+ m_window(window),
+ m_material(nullptr)
+{
+ setGeometry(m_geometry);
+ activateMaterial(MatSolidColor);
+#ifdef QSG_RUNTIME_DESCRIPTION
+ qsgnode_set_description(this, QLatin1String("stroke-fill"));
+#endif
+}
+
+QQuickShapeGenericStrokeFillNode::~QQuickShapeGenericStrokeFillNode()
+{
+ delete m_geometry;
+}
+
+void QQuickShapeGenericStrokeFillNode::activateMaterial(Material m)
+{
+ switch (m) {
+ case MatSolidColor:
+ // Use vertexcolor material. Items with different colors remain batchable
+ // this way, at the expense of having to provide per-vertex color values.
+ if (!m_solidColorMaterial)
+ m_solidColorMaterial.reset(QQuickShapeGenericMaterialFactory::createVertexColor(m_window));
+ m_material = m_solidColorMaterial.data();
+ break;
+ case MatLinearGradient:
+ if (!m_linearGradientMaterial)
+ m_linearGradientMaterial.reset(QQuickShapeGenericMaterialFactory::createLinearGradient(m_window, this));
+ m_material = m_linearGradientMaterial.data();
+ break;
+ default:
+ qWarning("Unknown material %d", m);
+ return;
+ }
+
+ if (material() != m_material)
+ setMaterial(m_material);
+}
+
+static bool q_supportsElementIndexUint(QSGRendererInterface::GraphicsApi api)
+{
+ static bool elementIndexUint = true;
+#if QT_CONFIG(opengl)
+ if (api == QSGRendererInterface::OpenGL) {
+ static bool elementIndexUintChecked = false;
+ if (!elementIndexUintChecked) {
+ elementIndexUintChecked = true;
+ QOpenGLContext *context = QOpenGLContext::currentContext();
+ QScopedPointer<QOpenGLContext> dummyContext;
+ QScopedPointer<QOffscreenSurface> dummySurface;
+ bool ok = true;
+ if (!context) {
+ dummyContext.reset(new QOpenGLContext);
+ dummyContext->create();
+ context = dummyContext.data();
+ dummySurface.reset(new QOffscreenSurface);
+ dummySurface->setFormat(context->format());
+ dummySurface->create();
+ ok = context->makeCurrent(dummySurface.data());
+ }
+ if (ok) {
+ elementIndexUint = static_cast<QOpenGLExtensions *>(context->functions())->hasOpenGLExtension(
+ QOpenGLExtensions::ElementIndexUint);
+ }
+ }
+ }
+#else
+ Q_UNUSED(api);
+#endif
+ return elementIndexUint;
+}
+
+QQuickShapeGenericRenderer::~QQuickShapeGenericRenderer()
+{
+ for (ShapePathData &d : m_sp) {
+ if (d.pendingFill)
+ d.pendingFill->orphaned = true;
+ if (d.pendingStroke)
+ d.pendingStroke->orphaned = true;
+ }
+}
+
+// sync, and so triangulation too, happens on the gui thread
+// - except when async is set, in which case triangulation is moved to worker threads
+
+void QQuickShapeGenericRenderer::beginSync(int totalCount)
+{
+ if (m_sp.count() != totalCount) {
+ m_sp.resize(totalCount);
+ m_accDirty |= DirtyList;
+ }
+ for (ShapePathData &d : m_sp)
+ d.syncDirty = 0;
+}
+
+void QQuickShapeGenericRenderer::setPath(int index, const QQuickPath *path)
+{
+ ShapePathData &d(m_sp[index]);
+ d.path = path ? path->path() : QPainterPath();
+ d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom;
+}
+
+void QQuickShapeGenericRenderer::setJSPath(int index, const QQuickShapePathCommands &path)
+{
+ ShapePathData &d(m_sp[index]);
+ d.path = path.toPainterPath();
+ d.syncDirty |= DirtyFillGeom | DirtyStrokeGeom;
+}
+
+void QQuickShapeGenericRenderer::setStrokeColor(int index, const QColor &color)
+{
+ ShapePathData &d(m_sp[index]);
+ d.strokeColor = colorToColor4ub(color);
+ d.syncDirty |= DirtyColor;
+}
+
+void QQuickShapeGenericRenderer::setStrokeWidth(int index, qreal w)
+{
+ ShapePathData &d(m_sp[index]);
+ d.strokeWidth = w;
+ if (w >= 0.0f)
+ d.pen.setWidthF(w);
+ d.syncDirty |= DirtyStrokeGeom;
+}
+
+void QQuickShapeGenericRenderer::setFillColor(int index, const QColor &color)
+{
+ ShapePathData &d(m_sp[index]);
+ d.fillColor = colorToColor4ub(color);
+ d.syncDirty |= DirtyColor;
+}
+
+void QQuickShapeGenericRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule)
+{
+ ShapePathData &d(m_sp[index]);
+ d.fillRule = Qt::FillRule(fillRule);
+ d.syncDirty |= DirtyFillGeom;
+}
+
+void QQuickShapeGenericRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit)
+{
+ ShapePathData &d(m_sp[index]);
+ d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
+ d.pen.setMiterLimit(miterLimit);
+ d.syncDirty |= DirtyStrokeGeom;
+}
+
+void QQuickShapeGenericRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle)
+{
+ ShapePathData &d(m_sp[index]);
+ d.pen.setCapStyle(Qt::PenCapStyle(capStyle));
+ d.syncDirty |= DirtyStrokeGeom;
+}
+
+void QQuickShapeGenericRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern)
+{
+ ShapePathData &d(m_sp[index]);
+ d.pen.setStyle(Qt::PenStyle(strokeStyle));
+ if (strokeStyle == QQuickShapePath::DashLine) {
+ d.pen.setDashPattern(dashPattern);
+ d.pen.setDashOffset(dashOffset);
+ }
+ d.syncDirty |= DirtyStrokeGeom;
+}
+
+void QQuickShapeGenericRenderer::setFillGradient(int index, QQuickShapeGradient *gradient)
+{
+ ShapePathData &d(m_sp[index]);
+ d.fillGradientActive = gradient != nullptr;
+#if QT_CONFIG(opengl)
+ if (gradient) {
+ d.fillGradient.stops = gradient->gradientStops(); // sorted
+ d.fillGradient.spread = gradient->spread();
+ if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) {
+ d.fillGradient.start = QPointF(g->x1(), g->y1());
+ d.fillGradient.end = QPointF(g->x2(), g->y2());
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+#endif
+ d.syncDirty |= DirtyFillGradient;
+}
+
+void QQuickShapeFillRunnable::run()
+{
+ QQuickShapeGenericRenderer::triangulateFill(path, fillColor, &fillVertices, &fillIndices, &indexType, supportsElementIndexUint);
+ emit done(this);
+}
+
+void QQuickShapeStrokeRunnable::run()
+{
+ QQuickShapeGenericRenderer::triangulateStroke(path, pen, strokeColor, &strokeVertices, clipSize);
+ emit done(this);
+}
+
+void QQuickShapeGenericRenderer::setAsyncCallback(void (*callback)(void *), void *data)
+{
+ m_asyncCallback = callback;
+ m_asyncCallbackData = data;
+}
+
+static QThreadPool *pathWorkThreadPool = nullptr;
+
+static void deletePathWorkThreadPool()
+{
+ delete pathWorkThreadPool;
+ pathWorkThreadPool = nullptr;
+}
+
+void QQuickShapeGenericRenderer::endSync(bool async)
+{
+ bool didKickOffAsync = false;
+
+ for (int i = 0; i < m_sp.count(); ++i) {
+ ShapePathData &d(m_sp[i]);
+ if (!d.syncDirty)
+ continue;
+
+ m_accDirty |= d.syncDirty;
+
+ // Use a shadow dirty flag in order to avoid losing state in case there are
+ // multiple syncs with different dirty flags before we get to updateNode()
+ // on the render thread (with the gui thread blocked). For our purposes
+ // here syncDirty is still required since geometry regeneration must only
+ // happen when there was an actual change in this particular sync round.
+ d.effectiveDirty |= d.syncDirty;
+
+ if (d.path.isEmpty()) {
+ d.fillVertices.clear();
+ d.fillIndices.clear();
+ d.strokeVertices.clear();
+ continue;
+ }
+
+ if (async && !pathWorkThreadPool) {
+ qAddPostRoutine(deletePathWorkThreadPool);
+ pathWorkThreadPool = new QThreadPool;
+ const int idealCount = QThread::idealThreadCount();
+ pathWorkThreadPool->setMaxThreadCount(idealCount > 0 ? idealCount * 2 : 4);
+ }
+
+ if ((d.syncDirty & DirtyFillGeom) && d.fillColor.a) {
+ d.path.setFillRule(d.fillRule);
+ if (m_api == QSGRendererInterface::Unknown)
+ m_api = m_item->window()->rendererInterface()->graphicsApi();
+ if (async) {
+ QQuickShapeFillRunnable *r = new QQuickShapeFillRunnable;
+ r->setAutoDelete(false);
+ if (d.pendingFill)
+ d.pendingFill->orphaned = true;
+ d.pendingFill = r;
+ r->path = d.path;
+ r->fillColor = d.fillColor;
+ r->supportsElementIndexUint = q_supportsElementIndexUint(m_api);
+ // Unlikely in practice but in theory m_sp could be
+ // resized. Therefore, capture 'i' instead of 'd'.
+ QObject::connect(r, &QQuickShapeFillRunnable::done, qApp, [this, i](QQuickShapeFillRunnable *r) {
+ // Bail out when orphaned (meaning either another run was
+ // started after this one, or the renderer got destroyed).
+ if (!r->orphaned && i < m_sp.count()) {
+ ShapePathData &d(m_sp[i]);
+ d.fillVertices = r->fillVertices;
+ d.fillIndices = r->fillIndices;
+ d.indexType = r->indexType;
+ d.pendingFill = nullptr;
+ d.effectiveDirty |= DirtyFillGeom;
+ maybeUpdateAsyncItem();
+ }
+ r->deleteLater();
+ });
+ didKickOffAsync = true;
+ pathWorkThreadPool->start(r);
+ } else {
+ triangulateFill(d.path, d.fillColor, &d.fillVertices, &d.fillIndices, &d.indexType, q_supportsElementIndexUint(m_api));
+ }
+ }
+
+ if ((d.syncDirty & DirtyStrokeGeom) && d.strokeWidth >= 0.0f && d.strokeColor.a) {
+ if (async) {
+ QQuickShapeStrokeRunnable *r = new QQuickShapeStrokeRunnable;
+ r->setAutoDelete(false);
+ if (d.pendingStroke)
+ d.pendingStroke->orphaned = true;
+ d.pendingStroke = r;
+ r->path = d.path;
+ r->pen = d.pen;
+ r->strokeColor = d.strokeColor;
+ r->clipSize = QSize(m_item->width(), m_item->height());
+ QObject::connect(r, &QQuickShapeStrokeRunnable::done, qApp, [this, i](QQuickShapeStrokeRunnable *r) {
+ if (!r->orphaned && i < m_sp.count()) {
+ ShapePathData &d(m_sp[i]);
+ d.strokeVertices = r->strokeVertices;
+ d.pendingStroke = nullptr;
+ d.effectiveDirty |= DirtyStrokeGeom;
+ maybeUpdateAsyncItem();
+ }
+ r->deleteLater();
+ });
+ didKickOffAsync = true;
+ pathWorkThreadPool->start(r);
+ } else {
+ triangulateStroke(d.path, d.pen, d.strokeColor, &d.strokeVertices,
+ QSize(m_item->width(), m_item->height()));
+ }
+ }
+ }
+
+ if (!didKickOffAsync && async && m_asyncCallback)
+ m_asyncCallback(m_asyncCallbackData);
+}
+
+void QQuickShapeGenericRenderer::maybeUpdateAsyncItem()
+{
+ for (const ShapePathData &d : qAsConst(m_sp)) {
+ if (d.pendingFill || d.pendingStroke)
+ return;
+ }
+ m_accDirty |= DirtyFillGeom | DirtyStrokeGeom;
+ m_item->update();
+ if (m_asyncCallback)
+ m_asyncCallback(m_asyncCallbackData);
+}
+
+// the stroke/fill triangulation functions may be invoked either on the gui
+// thread or some worker thread and must thus be self-contained.
+void QQuickShapeGenericRenderer::triangulateFill(const QPainterPath &path,
+ const Color4ub &fillColor,
+ VertexContainerType *fillVertices,
+ IndexContainerType *fillIndices,
+ QSGGeometry::Type *indexType,
+ bool supportsElementIndexUint)
+{
+ const QVectorPath &vp = qtVectorPathForPath(path);
+
+ QTriangleSet ts = qTriangulate(vp, QTransform::fromScale(TRI_SCALE, TRI_SCALE), 1, supportsElementIndexUint);
+ const int vertexCount = ts.vertices.count() / 2; // just a qreal vector with x,y hence the / 2
+ fillVertices->resize(vertexCount);
+ ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(fillVertices->data());
+ const qreal *vsrc = ts.vertices.constData();
+ for (int i = 0; i < vertexCount; ++i)
+ vdst[i].set(vsrc[i * 2] / TRI_SCALE, vsrc[i * 2 + 1] / TRI_SCALE, fillColor);
+
+ size_t indexByteSize;
+ if (ts.indices.type() == QVertexIndexVector::UnsignedShort) {
+ *indexType = QSGGeometry::UnsignedShortType;
+ // fillIndices is still QVector<quint32>. Just resize to N/2 and pack
+ // the N quint16s into it.
+ fillIndices->resize(ts.indices.size() / 2);
+ indexByteSize = ts.indices.size() * sizeof(quint16);
+ } else {
+ *indexType = QSGGeometry::UnsignedIntType;
+ fillIndices->resize(ts.indices.size());
+ indexByteSize = ts.indices.size() * sizeof(quint32);
+ }
+ memcpy(fillIndices->data(), ts.indices.data(), indexByteSize);
+}
+
+void QQuickShapeGenericRenderer::triangulateStroke(const QPainterPath &path,
+ const QPen &pen,
+ const Color4ub &strokeColor,
+ VertexContainerType *strokeVertices,
+ const QSize &clipSize)
+{
+ const QVectorPath &vp = qtVectorPathForPath(path);
+ const QRectF clip(QPointF(0, 0), clipSize);
+ const qreal inverseScale = 1.0 / TRI_SCALE;
+
+ QTriangulatingStroker stroker;
+ stroker.setInvScale(inverseScale);
+
+ if (pen.style() == Qt::SolidLine) {
+ stroker.process(vp, pen, clip, 0);
+ } else {
+ QDashedStrokeProcessor dashStroker;
+ dashStroker.setInvScale(inverseScale);
+ dashStroker.process(vp, pen, clip, 0);
+ QVectorPath dashStroke(dashStroker.points(), dashStroker.elementCount(),
+ dashStroker.elementTypes(), 0);
+ stroker.process(dashStroke, pen, clip, 0);
+ }
+
+ if (!stroker.vertexCount()) {
+ strokeVertices->clear();
+ return;
+ }
+
+ const int vertexCount = stroker.vertexCount() / 2; // just a float vector with x,y hence the / 2
+ strokeVertices->resize(vertexCount);
+ ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(strokeVertices->data());
+ const float *vsrc = stroker.vertices();
+ for (int i = 0; i < vertexCount; ++i)
+ vdst[i].set(vsrc[i * 2], vsrc[i * 2 + 1], strokeColor);
+}
+
+void QQuickShapeGenericRenderer::setRootNode(QQuickShapeGenericNode *node)
+{
+ if (m_rootNode != node) {
+ m_rootNode = node;
+ m_accDirty |= DirtyList;
+ }
+}
+
+// on the render thread with gui blocked
+void QQuickShapeGenericRenderer::updateNode()
+{
+ if (!m_rootNode || !m_accDirty)
+ return;
+
+// [ m_rootNode ]
+// / / /
+// #0 [ fill ] [ stroke ] [ next ]
+// / / |
+// #1 [ fill ] [ stroke ] [ next ]
+// / / |
+// #2 [ fill ] [ stroke ] [ next ]
+// ...
+// ...
+
+ QQuickShapeGenericNode **nodePtr = &m_rootNode;
+ QQuickShapeGenericNode *prevNode = nullptr;
+
+ for (ShapePathData &d : m_sp) {
+ if (!*nodePtr) {
+ Q_ASSERT(prevNode);
+ *nodePtr = new QQuickShapeGenericNode;
+ prevNode->m_next = *nodePtr;
+ prevNode->appendChildNode(*nodePtr);
+ }
+
+ QQuickShapeGenericNode *node = *nodePtr;
+
+ if (m_accDirty & DirtyList)
+ d.effectiveDirty |= DirtyFillGeom | DirtyStrokeGeom | DirtyColor | DirtyFillGradient;
+
+ if (!d.effectiveDirty) {
+ prevNode = node;
+ nodePtr = &node->m_next;
+ continue;
+ }
+
+ if (d.fillColor.a == 0) {
+ delete node->m_fillNode;
+ node->m_fillNode = nullptr;
+ } else if (!node->m_fillNode) {
+ node->m_fillNode = new QQuickShapeGenericStrokeFillNode(m_item->window());
+ if (node->m_strokeNode)
+ node->removeChildNode(node->m_strokeNode);
+ node->appendChildNode(node->m_fillNode);
+ if (node->m_strokeNode)
+ node->appendChildNode(node->m_strokeNode);
+ d.effectiveDirty |= DirtyFillGeom;
+ }
+
+ if (d.strokeWidth < 0.0f || d.strokeColor.a == 0) {
+ delete node->m_strokeNode;
+ node->m_strokeNode = nullptr;
+ } else if (!node->m_strokeNode) {
+ node->m_strokeNode = new QQuickShapeGenericStrokeFillNode(m_item->window());
+ node->appendChildNode(node->m_strokeNode);
+ d.effectiveDirty |= DirtyStrokeGeom;
+ }
+
+ updateFillNode(&d, node);
+ updateStrokeNode(&d, node);
+
+ d.effectiveDirty = 0;
+
+ prevNode = node;
+ nodePtr = &node->m_next;
+ }
+
+ if (*nodePtr && prevNode) {
+ prevNode->removeChildNode(*nodePtr);
+ delete *nodePtr;
+ *nodePtr = nullptr;
+ }
+
+ m_accDirty = 0;
+}
+
+void QQuickShapeGenericRenderer::updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n)
+{
+ if (d->fillGradientActive) {
+#if QT_CONFIG(opengl)
+ if (d->effectiveDirty & DirtyFillGradient)
+ n->m_fillGradient = d->fillGradient;
+#else
+ Q_UNUSED(n);
+#endif
+ }
+}
+
+void QQuickShapeGenericRenderer::updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node)
+{
+ if (!node->m_fillNode)
+ return;
+ if (!(d->effectiveDirty & (DirtyFillGeom | DirtyColor | DirtyFillGradient)))
+ return;
+
+ // Make a copy of the data that will be accessed by the material on
+ // the render thread. This must be done even when we bail out below.
+ QQuickShapeGenericStrokeFillNode *n = node->m_fillNode;
+ updateShadowDataInNode(d, n);
+
+ QSGGeometry *g = n->m_geometry;
+ if (d->fillVertices.isEmpty()) {
+ if (g->vertexCount() || g->indexCount()) {
+ g->allocate(0, 0);
+ n->markDirty(QSGNode::DirtyGeometry);
+ }
+ return;
+ }
+
+ if (d->fillGradientActive) {
+ n->activateMaterial(QQuickShapeGenericStrokeFillNode::MatLinearGradient);
+ if (d->effectiveDirty & DirtyFillGradient) {
+ // Gradients are implemented via a texture-based material.
+ n->markDirty(QSGNode::DirtyMaterial);
+ // stop here if only the gradient changed; no need to touch the geometry
+ if (!(d->effectiveDirty & DirtyFillGeom))
+ return;
+ }
+ } else {
+ n->activateMaterial(QQuickShapeGenericStrokeFillNode::MatSolidColor);
+ // fast path for updating only color values when no change in vertex positions
+ if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyFillGeom)) {
+ ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData());
+ for (int i = 0; i < g->vertexCount(); ++i)
+ vdst[i].set(vdst[i].x, vdst[i].y, d->fillColor);
+ n->markDirty(QSGNode::DirtyGeometry);
+ return;
+ }
+ }
+
+ const int indexCount = d->indexType == QSGGeometry::UnsignedShortType
+ ? d->fillIndices.count() * 2 : d->fillIndices.count();
+ if (g->indexType() != d->indexType) {
+ g = new QSGGeometry(QSGGeometry::defaultAttributes_ColoredPoint2D(),
+ d->fillVertices.count(), indexCount, d->indexType);
+ n->setGeometry(g);
+ delete n->m_geometry;
+ n->m_geometry = g;
+ } else {
+ g->allocate(d->fillVertices.count(), indexCount);
+ }
+ g->setDrawingMode(QSGGeometry::DrawTriangles);
+ memcpy(g->vertexData(), d->fillVertices.constData(), g->vertexCount() * g->sizeOfVertex());
+ memcpy(g->indexData(), d->fillIndices.constData(), g->indexCount() * g->sizeOfIndex());
+
+ n->markDirty(QSGNode::DirtyGeometry);
+}
+
+void QQuickShapeGenericRenderer::updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node)
+{
+ if (!node->m_strokeNode)
+ return;
+ if (!(d->effectiveDirty & (DirtyStrokeGeom | DirtyColor)))
+ return;
+
+ QQuickShapeGenericStrokeFillNode *n = node->m_strokeNode;
+ QSGGeometry *g = n->m_geometry;
+ if (d->strokeVertices.isEmpty()) {
+ if (g->vertexCount() || g->indexCount()) {
+ g->allocate(0, 0);
+ n->markDirty(QSGNode::DirtyGeometry);
+ }
+ return;
+ }
+
+ n->markDirty(QSGNode::DirtyGeometry);
+
+ // Async loading runs update once, bails out above, then updates again once
+ // ready. Set the material dirty then. This is in-line with fill where the
+ // first activateMaterial() achieves the same.
+ if (!g->vertexCount())
+ n->markDirty(QSGNode::DirtyMaterial);
+
+ if ((d->effectiveDirty & DirtyColor) && !(d->effectiveDirty & DirtyStrokeGeom)) {
+ ColoredVertex *vdst = reinterpret_cast<ColoredVertex *>(g->vertexData());
+ for (int i = 0; i < g->vertexCount(); ++i)
+ vdst[i].set(vdst[i].x, vdst[i].y, d->strokeColor);
+ return;
+ }
+
+ g->allocate(d->strokeVertices.count(), 0);
+ g->setDrawingMode(QSGGeometry::DrawTriangleStrip);
+ memcpy(g->vertexData(), d->strokeVertices.constData(), g->vertexCount() * g->sizeOfVertex());
+}
+
+QSGMaterial *QQuickShapeGenericMaterialFactory::createVertexColor(QQuickWindow *window)
+{
+ QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
+
+#if QT_CONFIG(opengl)
+ if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"...
+ return new QSGVertexColorMaterial;
+#endif
+
+ qWarning("Vertex-color material: Unsupported graphics API %d", api);
+ return nullptr;
+}
+
+QSGMaterial *QQuickShapeGenericMaterialFactory::createLinearGradient(QQuickWindow *window,
+ QQuickShapeGenericStrokeFillNode *node)
+{
+ QSGRendererInterface::GraphicsApi api = window->rendererInterface()->graphicsApi();
+
+#if QT_CONFIG(opengl)
+ if (api == QSGRendererInterface::OpenGL) // ### so much for "generic"...
+ return new QQuickShapeLinearGradientMaterial(node);
+#else
+ Q_UNUSED(node);
+#endif
+
+ qWarning("Linear gradient material: Unsupported graphics API %d", api);
+ return nullptr;
+}
+
+#if QT_CONFIG(opengl)
+
+QSGMaterialType QQuickShapeLinearGradientShader::type;
+
+QQuickShapeLinearGradientShader::QQuickShapeLinearGradientShader()
+{
+ setShaderSourceFile(QOpenGLShader::Vertex,
+ QStringLiteral(":/qt-project.org/items/shaders/lineargradient.vert"));
+ setShaderSourceFile(QOpenGLShader::Fragment,
+ QStringLiteral(":/qt-project.org/items/shaders/lineargradient.frag"));
+}
+
+void QQuickShapeLinearGradientShader::initialize()
+{
+ m_opacityLoc = program()->uniformLocation("opacity");
+ m_matrixLoc = program()->uniformLocation("matrix");
+ m_gradStartLoc = program()->uniformLocation("gradStart");
+ m_gradEndLoc = program()->uniformLocation("gradEnd");
+}
+
+void QQuickShapeLinearGradientShader::updateState(const RenderState &state, QSGMaterial *mat, QSGMaterial *)
+{
+ QQuickShapeLinearGradientMaterial *m = static_cast<QQuickShapeLinearGradientMaterial *>(mat);
+
+ if (state.isOpacityDirty())
+ program()->setUniformValue(m_opacityLoc, state.opacity());
+
+ if (state.isMatrixDirty())
+ program()->setUniformValue(m_matrixLoc, state.combinedMatrix());
+
+ QQuickShapeGenericStrokeFillNode *node = m->node();
+ program()->setUniformValue(m_gradStartLoc, QVector2D(node->m_fillGradient.start));
+ program()->setUniformValue(m_gradEndLoc, QVector2D(node->m_fillGradient.end));
+
+ QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(node->m_fillGradient);
+ tx->bind();
+}
+
+char const *const *QQuickShapeLinearGradientShader::attributeNames() const
+{
+ static const char *const attr[] = { "vertexCoord", "vertexColor", nullptr };
+ return attr;
+}
+
+int QQuickShapeLinearGradientMaterial::compare(const QSGMaterial *other) const
+{
+ Q_ASSERT(other && type() == other->type());
+ const QQuickShapeLinearGradientMaterial *m = static_cast<const QQuickShapeLinearGradientMaterial *>(other);
+
+ QQuickShapeGenericStrokeFillNode *a = node();
+ QQuickShapeGenericStrokeFillNode *b = m->node();
+ Q_ASSERT(a && b);
+ if (a == b)
+ return 0;
+
+ const QQuickShapeGradientCache::GradientDesc *ga = &a->m_fillGradient;
+ const QQuickShapeGradientCache::GradientDesc *gb = &b->m_fillGradient;
+
+ if (int d = ga->spread - gb->spread)
+ return d;
+
+ if (int d = ga->start.x() - gb->start.x())
+ return d;
+ if (int d = ga->start.y() - gb->start.y())
+ return d;
+ if (int d = ga->end.x() - gb->end.x())
+ return d;
+ if (int d = ga->end.y() - gb->end.y())
+ return d;
+
+ if (int d = ga->stops.count() - gb->stops.count())
+ return d;
+
+ for (int i = 0; i < ga->stops.count(); ++i) {
+ if (int d = ga->stops[i].first - gb->stops[i].first)
+ return d;
+ if (int d = ga->stops[i].second.rgba() - gb->stops[i].second.rgba())
+ return d;
+ }
+
+ return 0;
+}
+
+#endif // QT_CONFIG(opengl)
+
+QT_END_NAMESPACE
diff --git a/src/imports/shapes/qquickshapegenericrenderer_p.h b/src/imports/shapes/qquickshapegenericrenderer_p.h
new file mode 100644
index 0000000000..1f36e3decf
--- /dev/null
+++ b/src/imports/shapes/qquickshapegenericrenderer_p.h
@@ -0,0 +1,308 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKSHAPEGENERICRENDERER_P_H
+#define QQUICKSHAPEGENERICRENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickshape_p_p.h"
+#include <qsgnode.h>
+#include <qsggeometry.h>
+#include <qsgmaterial.h>
+#include <qsgrendererinterface.h>
+#include <QtCore/qrunnable.h>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickShapeGenericNode;
+class QQuickShapeGenericStrokeFillNode;
+class QQuickShapeFillRunnable;
+class QQuickShapeStrokeRunnable;
+
+class QQuickShapeGenericRenderer : public QQuickAbstractPathRenderer
+{
+public:
+ enum Dirty {
+ DirtyFillGeom = 0x01,
+ DirtyStrokeGeom = 0x02,
+ DirtyColor = 0x04,
+ DirtyFillGradient = 0x08,
+ DirtyList = 0x10 // only for accDirty
+ };
+
+ QQuickShapeGenericRenderer(QQuickItem *item)
+ : m_item(item),
+ m_api(QSGRendererInterface::Unknown),
+ m_rootNode(nullptr),
+ m_accDirty(0),
+ m_asyncCallback(nullptr),
+ m_asyncCallbackData(nullptr)
+ { }
+ ~QQuickShapeGenericRenderer();
+
+ void beginSync(int totalCount) override;
+ void setPath(int index, const QQuickPath *path) override;
+ void setJSPath(int index, const QQuickShapePathCommands &path) override;
+ void setStrokeColor(int index, const QColor &color) override;
+ void setStrokeWidth(int index, qreal w) override;
+ void setFillColor(int index, const QColor &color) override;
+ void setFillRule(int index, QQuickShapePath::FillRule fillRule) override;
+ void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override;
+ void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override;
+ void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern) override;
+ void setFillGradient(int index, QQuickShapeGradient *gradient) override;
+ void endSync(bool async) override;
+ void setAsyncCallback(void (*)(void *), void *) override;
+ Flags flags() const override { return SupportsAsync; }
+
+ void updateNode() override;
+
+ void setRootNode(QQuickShapeGenericNode *node);
+
+ struct Color4ub { unsigned char r, g, b, a; };
+ typedef QVector<QSGGeometry::ColoredPoint2D> VertexContainerType;
+ typedef QVector<quint32> IndexContainerType;
+
+ static void triangulateFill(const QPainterPath &path,
+ const Color4ub &fillColor,
+ VertexContainerType *fillVertices,
+ IndexContainerType *fillIndices,
+ QSGGeometry::Type *indexType,
+ bool supportsElementIndexUint);
+ static void triangulateStroke(const QPainterPath &path,
+ const QPen &pen,
+ const Color4ub &strokeColor,
+ VertexContainerType *strokeVertices,
+ const QSize &clipSize);
+
+private:
+ void maybeUpdateAsyncItem();
+
+ struct ShapePathData {
+ float strokeWidth;
+ QPen pen;
+ Color4ub strokeColor;
+ Color4ub fillColor;
+ Qt::FillRule fillRule;
+ QPainterPath path;
+ bool fillGradientActive;
+#if QT_CONFIG(opengl)
+ QQuickShapeGradientCache::GradientDesc fillGradient;
+#endif
+ VertexContainerType fillVertices;
+ IndexContainerType fillIndices;
+ QSGGeometry::Type indexType;
+ VertexContainerType strokeVertices;
+ int syncDirty;
+ int effectiveDirty = 0;
+ QQuickShapeFillRunnable *pendingFill = nullptr;
+ QQuickShapeStrokeRunnable *pendingStroke = nullptr;
+ };
+
+ void updateShadowDataInNode(ShapePathData *d, QQuickShapeGenericStrokeFillNode *n);
+ void updateFillNode(ShapePathData *d, QQuickShapeGenericNode *node);
+ void updateStrokeNode(ShapePathData *d, QQuickShapeGenericNode *node);
+
+ QQuickItem *m_item;
+ QSGRendererInterface::GraphicsApi m_api;
+ QQuickShapeGenericNode *m_rootNode;
+ QVector<ShapePathData> m_sp;
+ int m_accDirty;
+ void (*m_asyncCallback)(void *);
+ void *m_asyncCallbackData;
+};
+
+class QQuickShapeFillRunnable : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+public:
+ void run() override;
+
+ bool orphaned = false;
+
+ // input
+ QPainterPath path;
+ QQuickShapeGenericRenderer::Color4ub fillColor;
+ bool supportsElementIndexUint;
+
+ // output
+ QQuickShapeGenericRenderer::VertexContainerType fillVertices;
+ QQuickShapeGenericRenderer::IndexContainerType fillIndices;
+ QSGGeometry::Type indexType;
+
+Q_SIGNALS:
+ void done(QQuickShapeFillRunnable *self);
+};
+
+class QQuickShapeStrokeRunnable : public QObject, public QRunnable
+{
+ Q_OBJECT
+
+public:
+ void run() override;
+
+ bool orphaned = false;
+
+ // input
+ QPainterPath path;
+ QPen pen;
+ QQuickShapeGenericRenderer::Color4ub strokeColor;
+ QSize clipSize;
+
+ // output
+ QQuickShapeGenericRenderer::VertexContainerType strokeVertices;
+
+Q_SIGNALS:
+ void done(QQuickShapeStrokeRunnable *self);
+};
+
+class QQuickShapeGenericStrokeFillNode : public QSGGeometryNode
+{
+public:
+ QQuickShapeGenericStrokeFillNode(QQuickWindow *window);
+ ~QQuickShapeGenericStrokeFillNode();
+
+ enum Material {
+ MatSolidColor,
+ MatLinearGradient
+ };
+
+ void activateMaterial(Material m);
+
+ QQuickWindow *window() const { return m_window; }
+
+ // shadow data for custom materials
+#if QT_CONFIG(opengl)
+ QQuickShapeGradientCache::GradientDesc m_fillGradient;
+#endif
+
+private:
+ QSGGeometry *m_geometry;
+ QQuickWindow *m_window;
+ QSGMaterial *m_material;
+ QScopedPointer<QSGMaterial> m_solidColorMaterial;
+ QScopedPointer<QSGMaterial> m_linearGradientMaterial;
+
+ friend class QQuickShapeGenericRenderer;
+};
+
+class QQuickShapeGenericNode : public QSGNode
+{
+public:
+ QQuickShapeGenericStrokeFillNode *m_fillNode = nullptr;
+ QQuickShapeGenericStrokeFillNode *m_strokeNode = nullptr;
+ QQuickShapeGenericNode *m_next = nullptr;
+};
+
+class QQuickShapeGenericMaterialFactory
+{
+public:
+ static QSGMaterial *createVertexColor(QQuickWindow *window);
+ static QSGMaterial *createLinearGradient(QQuickWindow *window, QQuickShapeGenericStrokeFillNode *node);
+};
+
+#if QT_CONFIG(opengl)
+
+class QQuickShapeLinearGradientShader : public QSGMaterialShader
+{
+public:
+ QQuickShapeLinearGradientShader();
+
+ void initialize() override;
+ void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override;
+ char const *const *attributeNames() const override;
+
+ static QSGMaterialType type;
+
+private:
+ int m_opacityLoc = -1;
+ int m_matrixLoc = -1;
+ int m_gradStartLoc = -1;
+ int m_gradEndLoc = -1;
+};
+
+class QQuickShapeLinearGradientMaterial : public QSGMaterial
+{
+public:
+ QQuickShapeLinearGradientMaterial(QQuickShapeGenericStrokeFillNode *node)
+ : m_node(node)
+ {
+ // Passing RequiresFullMatrix is essential in order to prevent the
+ // batch renderer from baking in simple, translate-only transforms into
+ // the vertex data. The shader will rely on the fact that
+ // vertexCoord.xy is the Shape-space coordinate and so no modifications
+ // are welcome.
+ setFlag(Blending | RequiresFullMatrix);
+ }
+
+ QSGMaterialType *type() const override
+ {
+ return &QQuickShapeLinearGradientShader::type;
+ }
+
+ int compare(const QSGMaterial *other) const override;
+
+ QSGMaterialShader *createShader() const override
+ {
+ return new QQuickShapeLinearGradientShader;
+ }
+
+ QQuickShapeGenericStrokeFillNode *node() const { return m_node; }
+
+private:
+ QQuickShapeGenericStrokeFillNode *m_node;
+};
+
+#endif // QT_CONFIG(opengl)
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSHAPEGENERICRENDERER_P_H
diff --git a/src/imports/shapes/qquickshapenvprrenderer.cpp b/src/imports/shapes/qquickshapenvprrenderer.cpp
new file mode 100644
index 0000000000..f3f8d807ec
--- /dev/null
+++ b/src/imports/shapes/qquickshapenvprrenderer.cpp
@@ -0,0 +1,923 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickshapenvprrenderer_p.h"
+#include <QOpenGLExtraFunctions>
+#include <QOpenGLFramebufferObject>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLBuffer>
+#include <private/qquickpath_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QQuickShapeNvprRenderer::beginSync(int totalCount)
+{
+ if (m_sp.count() != totalCount) {
+ m_sp.resize(totalCount);
+ m_accDirty |= DirtyList;
+ }
+}
+
+void QQuickShapeNvprRenderer::setPath(int index, const QQuickPath *path)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ convertPath(path, &d);
+ d.dirty |= DirtyPath;
+ m_accDirty |= DirtyPath;
+}
+
+void QQuickShapeNvprRenderer::setJSPath(int index, const QQuickShapePathCommands &path)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ convertJSPath(path, &d);
+ d.dirty |= DirtyPath;
+ m_accDirty |= DirtyPath;
+}
+
+void QQuickShapeNvprRenderer::setStrokeColor(int index, const QColor &color)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.strokeColor = color;
+ d.dirty |= DirtyStyle;
+ m_accDirty |= DirtyStyle;
+}
+
+void QQuickShapeNvprRenderer::setStrokeWidth(int index, qreal w)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.strokeWidth = w;
+ d.dirty |= DirtyStyle;
+ m_accDirty |= DirtyStyle;
+}
+
+void QQuickShapeNvprRenderer::setFillColor(int index, const QColor &color)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.fillColor = color;
+ d.dirty |= DirtyStyle;
+ m_accDirty |= DirtyStyle;
+}
+
+void QQuickShapeNvprRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.fillRule = fillRule;
+ d.dirty |= DirtyFillRule;
+ m_accDirty |= DirtyFillRule;
+}
+
+void QQuickShapeNvprRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.joinStyle = joinStyle;
+ d.miterLimit = miterLimit;
+ d.dirty |= DirtyStyle;
+ m_accDirty |= DirtyStyle;
+}
+
+void QQuickShapeNvprRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.capStyle = capStyle;
+ d.dirty |= DirtyStyle;
+ m_accDirty |= DirtyStyle;
+}
+
+void QQuickShapeNvprRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.dashActive = strokeStyle == QQuickShapePath::DashLine;
+ d.dashOffset = dashOffset;
+ d.dashPattern = dashPattern;
+ d.dirty |= DirtyDash;
+ m_accDirty |= DirtyDash;
+}
+
+void QQuickShapeNvprRenderer::setFillGradient(int index, QQuickShapeGradient *gradient)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.fillGradientActive = gradient != nullptr;
+ if (gradient) {
+ d.fillGradient.stops = gradient->gradientStops(); // sorted
+ d.fillGradient.spread = gradient->spread();
+ if (QQuickShapeLinearGradient *g = qobject_cast<QQuickShapeLinearGradient *>(gradient)) {
+ d.fillGradient.start = QPointF(g->x1(), g->y1());
+ d.fillGradient.end = QPointF(g->x2(), g->y2());
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+ d.dirty |= DirtyFillGradient;
+ m_accDirty |= DirtyFillGradient;
+}
+
+void QQuickShapeNvprRenderer::endSync(bool)
+{
+}
+
+void QQuickShapeNvprRenderer::setNode(QQuickShapeNvprRenderNode *node)
+{
+ if (m_node != node) {
+ m_node = node;
+ m_accDirty |= DirtyList;
+ }
+}
+
+QDebug operator<<(QDebug debug, const QQuickShapeNvprRenderer::NvprPath &path)
+{
+ QDebugStateSaver saver(debug);
+ debug.space().noquote();
+ if (!path.str.isEmpty()) {
+ debug << "Path with SVG string" << path.str;
+ return debug;
+ }
+ debug << "Path with" << path.cmd.count() << "commands";
+ int ci = 0;
+ for (GLubyte cmd : path.cmd) {
+ static struct { GLubyte cmd; const char *s; int coordCount; } nameTab[] = {
+ { GL_MOVE_TO_NV, "moveTo", 2 },
+ { GL_LINE_TO_NV, "lineTo", 2 },
+ { GL_QUADRATIC_CURVE_TO_NV, "quadTo", 4 },
+ { GL_CUBIC_CURVE_TO_NV, "cubicTo", 6 },
+ { GL_LARGE_CW_ARC_TO_NV, "arcTo-large-CW", 5 },
+ { GL_LARGE_CCW_ARC_TO_NV, "arcTo-large-CCW", 5 },
+ { GL_SMALL_CW_ARC_TO_NV, "arcTo-small-CW", 5 },
+ { GL_SMALL_CCW_ARC_TO_NV, "arcTo-small-CCW", 5 },
+ { GL_CLOSE_PATH_NV, "closePath", 0 } };
+ for (size_t i = 0; i < sizeof(nameTab) / sizeof(nameTab[0]); ++i) {
+ if (nameTab[i].cmd == cmd) {
+ QByteArray cs;
+ for (int j = 0; j < nameTab[i].coordCount; ++j) {
+ cs.append(QByteArray::number(path.coord[ci++]));
+ cs.append(' ');
+ }
+ debug << "\n " << nameTab[i].s << " " << cs;
+ break;
+ }
+ }
+ }
+ return debug;
+}
+
+static inline void appendCoords(QVector<GLfloat> *v, QQuickCurve *c, QPointF *pos)
+{
+ QPointF p(c->hasRelativeX() ? pos->x() + c->relativeX() : c->x(),
+ c->hasRelativeY() ? pos->y() + c->relativeY() : c->y());
+ v->append(p.x());
+ v->append(p.y());
+ *pos = p;
+}
+
+static inline void appendControlCoords(QVector<GLfloat> *v, QQuickPathQuad *c, const QPointF &pos)
+{
+ QPointF p(c->hasRelativeControlX() ? pos.x() + c->relativeControlX() : c->controlX(),
+ c->hasRelativeControlY() ? pos.y() + c->relativeControlY() : c->controlY());
+ v->append(p.x());
+ v->append(p.y());
+}
+
+static inline void appendControl1Coords(QVector<GLfloat> *v, QQuickPathCubic *c, const QPointF &pos)
+{
+ QPointF p(c->hasRelativeControl1X() ? pos.x() + c->relativeControl1X() : c->control1X(),
+ c->hasRelativeControl1Y() ? pos.y() + c->relativeControl1Y() : c->control1Y());
+ v->append(p.x());
+ v->append(p.y());
+}
+
+static inline void appendControl2Coords(QVector<GLfloat> *v, QQuickPathCubic *c, const QPointF &pos)
+{
+ QPointF p(c->hasRelativeControl2X() ? pos.x() + c->relativeControl2X() : c->control2X(),
+ c->hasRelativeControl2Y() ? pos.y() + c->relativeControl2Y() : c->control2Y());
+ v->append(p.x());
+ v->append(p.y());
+}
+
+void QQuickShapeNvprRenderer::convertPath(const QQuickPath *path, ShapePathGuiData *d)
+{
+ d->path = NvprPath();
+ if (!path)
+ return;
+
+ const QList<QQuickPathElement *> &pp(QQuickPathPrivate::get(path)->_pathElements);
+ if (pp.isEmpty())
+ return;
+
+ QPointF startPos(path->startX(), path->startY());
+ QPointF pos(startPos);
+ if (!qFuzzyIsNull(pos.x()) || !qFuzzyIsNull(pos.y())) {
+ d->path.cmd.append(GL_MOVE_TO_NV);
+ d->path.coord.append(pos.x());
+ d->path.coord.append(pos.y());
+ }
+
+ for (QQuickPathElement *e : pp) {
+ if (QQuickPathMove *o = qobject_cast<QQuickPathMove *>(e)) {
+ d->path.cmd.append(GL_MOVE_TO_NV);
+ appendCoords(&d->path.coord, o, &pos);
+ startPos = pos;
+ } else if (QQuickPathLine *o = qobject_cast<QQuickPathLine *>(e)) {
+ d->path.cmd.append(GL_LINE_TO_NV);
+ appendCoords(&d->path.coord, o, &pos);
+ } else if (QQuickPathQuad *o = qobject_cast<QQuickPathQuad *>(e)) {
+ d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV);
+ appendControlCoords(&d->path.coord, o, pos);
+ appendCoords(&d->path.coord, o, &pos);
+ } else if (QQuickPathCubic *o = qobject_cast<QQuickPathCubic *>(e)) {
+ d->path.cmd.append(GL_CUBIC_CURVE_TO_NV);
+ appendControl1Coords(&d->path.coord, o, pos);
+ appendControl2Coords(&d->path.coord, o, pos);
+ appendCoords(&d->path.coord, o, &pos);
+ } else if (QQuickPathArc *o = qobject_cast<QQuickPathArc *>(e)) {
+ const bool sweepFlag = o->direction() == QQuickPathArc::Clockwise; // maps to CCW, not a typo
+ GLenum cmd;
+ if (o->useLargeArc())
+ cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV;
+ else
+ cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV;
+ d->path.cmd.append(cmd);
+ d->path.coord.append(o->radiusX());
+ d->path.coord.append(o->radiusY());
+ d->path.coord.append(o->xAxisRotation());
+ appendCoords(&d->path.coord, o, &pos);
+ } else if (QQuickPathSvg *o = qobject_cast<QQuickPathSvg *>(e)) {
+ // PathSvg cannot be combined with other elements. But take at
+ // least startX and startY into account.
+ if (d->path.str.isEmpty())
+ d->path.str = QString(QStringLiteral("M %1 %2 ")).arg(pos.x()).arg(pos.y()).toUtf8();
+ d->path.str.append(o->path().toUtf8());
+ } else {
+ qWarning() << "Shape/NVPR: unsupported Path element" << e;
+ }
+ }
+
+ // For compatibility with QTriangulatingStroker. SVG and others would not
+ // implicitly close the path when end_pos == start_pos (start_pos being the
+ // last moveTo pos); that would still need an explicit 'z' or similar. We
+ // don't have an explicit close command, so just fake a close when the
+ // positions match.
+ if (pos == startPos)
+ d->path.cmd.append(GL_CLOSE_PATH_NV);
+}
+
+void QQuickShapeNvprRenderer::convertJSPath(const QQuickShapePathCommands &path, ShapePathGuiData *d)
+{
+ d->path = NvprPath();
+ if (path.cmd.isEmpty())
+ return;
+
+ QPointF startPos(0, 0);
+ QPointF pos(startPos);
+ int coordIdx = 0;
+
+ for (QQuickShapePathCommands::Command cmd : path.cmd) {
+ switch (cmd) {
+ case QQuickShapePathCommands::MoveTo:
+ d->path.cmd.append(GL_MOVE_TO_NV);
+ pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]);
+ startPos = pos;
+ d->path.coord.append(pos.x());
+ d->path.coord.append(pos.y());
+ coordIdx += 2;
+ break;
+ case QQuickShapePathCommands::LineTo:
+ d->path.cmd.append(GL_LINE_TO_NV);
+ pos = QPointF(path.coords[coordIdx], path.coords[coordIdx + 1]);
+ d->path.coord.append(pos.x());
+ d->path.coord.append(pos.y());
+ coordIdx += 2;
+ break;
+ case QQuickShapePathCommands::QuadTo:
+ d->path.cmd.append(GL_QUADRATIC_CURVE_TO_NV);
+ d->path.coord.append(path.coords[coordIdx]);
+ d->path.coord.append(path.coords[coordIdx + 1]);
+ pos = QPointF(path.coords[coordIdx + 2], path.coords[coordIdx + 3]);
+ d->path.coord.append(pos.x());
+ d->path.coord.append(pos.y());
+ coordIdx += 4;
+ break;
+ case QQuickShapePathCommands::CubicTo:
+ d->path.cmd.append(GL_CUBIC_CURVE_TO_NV);
+ d->path.coord.append(path.coords[coordIdx]);
+ d->path.coord.append(path.coords[coordIdx + 1]);
+ d->path.coord.append(path.coords[coordIdx + 2]);
+ d->path.coord.append(path.coords[coordIdx + 3]);
+ pos = QPointF(path.coords[coordIdx + 4], path.coords[coordIdx + 5]);
+ d->path.coord.append(pos.x());
+ d->path.coord.append(pos.y());
+ coordIdx += 6;
+ break;
+ case QQuickShapePathCommands::ArcTo:
+ {
+ const bool sweepFlag = !qFuzzyIsNull(path.coords[coordIdx + 5]);
+ const bool useLargeArc = !qFuzzyIsNull(path.coords[coordIdx + 6]);
+ GLenum cmd;
+ if (useLargeArc)
+ cmd = sweepFlag ? GL_LARGE_CCW_ARC_TO_NV : GL_LARGE_CW_ARC_TO_NV;
+ else
+ cmd = sweepFlag ? GL_SMALL_CCW_ARC_TO_NV : GL_SMALL_CW_ARC_TO_NV;
+ d->path.cmd.append(cmd);
+ d->path.coord.append(path.coords[coordIdx]); // rx
+ d->path.coord.append(path.coords[coordIdx + 1]); // ry
+ d->path.coord.append(path.coords[coordIdx + 2]); // xrot
+ pos = QPointF(path.coords[coordIdx + 3], path.coords[coordIdx + 4]);
+ d->path.coord.append(pos.x());
+ d->path.coord.append(pos.y());
+ coordIdx += 7;
+ }
+ break;
+ default:
+ qWarning("Unknown JS path command: %d", cmd);
+ break;
+ }
+ }
+
+ if (pos == startPos)
+ d->path.cmd.append(GL_CLOSE_PATH_NV);
+}
+
+static inline QVector4D qsg_premultiply(const QColor &c, float globalOpacity)
+{
+ const float o = c.alphaF() * globalOpacity;
+ return QVector4D(c.redF() * o, c.greenF() * o, c.blueF() * o, o);
+}
+
+void QQuickShapeNvprRenderer::updateNode()
+{
+ // Called on the render thread with gui blocked -> update the node with its
+ // own copy of all relevant data.
+
+ if (!m_accDirty)
+ return;
+
+ const int count = m_sp.count();
+ const bool listChanged = m_accDirty & DirtyList;
+ if (listChanged)
+ m_node->m_sp.resize(count);
+
+ for (int i = 0; i < count; ++i) {
+ ShapePathGuiData &src(m_sp[i]);
+ QQuickShapeNvprRenderNode::ShapePathRenderData &dst(m_node->m_sp[i]);
+
+ int dirty = src.dirty;
+ src.dirty = 0;
+ if (listChanged)
+ dirty |= DirtyPath | DirtyStyle | DirtyFillRule | DirtyDash | DirtyFillGradient;
+
+ // updateNode() can be called several times with different dirty
+ // states before render() gets invoked. So accumulate.
+ dst.dirty |= dirty;
+
+ if (dirty & DirtyPath)
+ dst.source = src.path;
+
+ if (dirty & DirtyStyle) {
+ dst.strokeWidth = src.strokeWidth;
+ dst.strokeColor = qsg_premultiply(src.strokeColor, 1.0f);
+ dst.fillColor = qsg_premultiply(src.fillColor, 1.0f);
+ switch (src.joinStyle) {
+ case QQuickShapePath::MiterJoin:
+ dst.joinStyle = GL_MITER_TRUNCATE_NV;
+ break;
+ case QQuickShapePath::BevelJoin:
+ dst.joinStyle = GL_BEVEL_NV;
+ break;
+ case QQuickShapePath::RoundJoin:
+ dst.joinStyle = GL_ROUND_NV;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ dst.miterLimit = src.miterLimit;
+ switch (src.capStyle) {
+ case QQuickShapePath::FlatCap:
+ dst.capStyle = GL_FLAT;
+ break;
+ case QQuickShapePath::SquareCap:
+ dst.capStyle = GL_SQUARE_NV;
+ break;
+ case QQuickShapePath::RoundCap:
+ dst.capStyle = GL_ROUND_NV;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ }
+
+ if (dirty & DirtyFillRule) {
+ switch (src.fillRule) {
+ case QQuickShapePath::OddEvenFill:
+ dst.fillRule = GL_INVERT;
+ break;
+ case QQuickShapePath::WindingFill:
+ dst.fillRule = GL_COUNT_UP_NV;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ }
+
+ if (dirty & DirtyDash) {
+ dst.dashOffset = src.dashOffset;
+ if (src.dashActive) {
+ dst.dashPattern.resize(src.dashPattern.count());
+ // Multiply by strokeWidth because the Shape API follows QPen
+ // meaning the input dash pattern here is in width units.
+ for (int i = 0; i < src.dashPattern.count(); ++i)
+ dst.dashPattern[i] = GLfloat(src.dashPattern[i]) * src.strokeWidth;
+ } else {
+ dst.dashPattern.clear();
+ }
+ }
+
+ if (dirty & DirtyFillGradient) {
+ dst.fillGradientActive = src.fillGradientActive;
+ if (src.fillGradientActive)
+ dst.fillGradient = src.fillGradient;
+ }
+ }
+
+ m_node->markDirty(QSGNode::DirtyMaterial);
+ m_accDirty = 0;
+}
+
+bool QQuickShapeNvprRenderNode::nvprInited = false;
+QQuickNvprFunctions QQuickShapeNvprRenderNode::nvpr;
+QQuickNvprMaterialManager QQuickShapeNvprRenderNode::mtlmgr;
+
+QQuickShapeNvprRenderNode::~QQuickShapeNvprRenderNode()
+{
+ releaseResources();
+}
+
+void QQuickShapeNvprRenderNode::releaseResources()
+{
+ for (ShapePathRenderData &d : m_sp) {
+ if (d.path) {
+ nvpr.deletePaths(d.path, 1);
+ d.path = 0;
+ }
+ if (d.fallbackFbo) {
+ delete d.fallbackFbo;
+ d.fallbackFbo = nullptr;
+ }
+ }
+
+ m_fallbackBlitter.destroy();
+}
+
+void QQuickNvprMaterialManager::create(QQuickNvprFunctions *nvpr)
+{
+ m_nvpr = nvpr;
+}
+
+void QQuickNvprMaterialManager::releaseResources()
+{
+ QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
+ for (MaterialDesc &mtl : m_materials) {
+ if (mtl.ppl) {
+ f->glDeleteProgramPipelines(1, &mtl.ppl);
+ mtl = MaterialDesc();
+ }
+ }
+}
+
+QQuickNvprMaterialManager::MaterialDesc *QQuickNvprMaterialManager::activateMaterial(Material m)
+{
+ QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
+ MaterialDesc &mtl(m_materials[m]);
+
+ if (!mtl.ppl) {
+ if (m == MatSolid) {
+ static const char *fragSrc =
+ "#version 310 es\n"
+ "precision highp float;\n"
+ "out vec4 fragColor;\n"
+ "uniform vec4 color;\n"
+ "uniform float opacity;\n"
+ "void main() {\n"
+ " fragColor = color * opacity;\n"
+ "}\n";
+ if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) {
+ qWarning("NVPR: Failed to create shader pipeline for solid fill");
+ return nullptr;
+ }
+ Q_ASSERT(mtl.ppl && mtl.prg);
+ mtl.uniLoc[0] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "color");
+ Q_ASSERT(mtl.uniLoc[0] >= 0);
+ mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity");
+ Q_ASSERT(mtl.uniLoc[1] >= 0);
+ } else if (m == MatLinearGradient) {
+ static const char *fragSrc =
+ "#version 310 es\n"
+ "precision highp float;\n"
+ "layout(location = 0) in vec2 uv;"
+ "uniform float opacity;\n"
+ "uniform sampler2D gradTab;\n"
+ "uniform vec2 gradStart;\n"
+ "uniform vec2 gradEnd;\n"
+ "out vec4 fragColor;\n"
+ "void main() {\n"
+ " vec2 gradVec = gradEnd - gradStart;\n"
+ " float gradTabIndex = dot(gradVec, uv - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);\n"
+ " fragColor = texture(gradTab, vec2(gradTabIndex, 0.5)) * opacity;\n"
+ "}\n";
+ if (!m_nvpr->createFragmentOnlyPipeline(fragSrc, &mtl.ppl, &mtl.prg)) {
+ qWarning("NVPR: Failed to create shader pipeline for linear gradient");
+ return nullptr;
+ }
+ Q_ASSERT(mtl.ppl && mtl.prg);
+ mtl.uniLoc[1] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "opacity");
+ Q_ASSERT(mtl.uniLoc[1] >= 0);
+ mtl.uniLoc[2] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradStart");
+ Q_ASSERT(mtl.uniLoc[2] >= 0);
+ mtl.uniLoc[3] = f->glGetProgramResourceLocation(mtl.prg, GL_UNIFORM, "gradEnd");
+ Q_ASSERT(mtl.uniLoc[3] >= 0);
+ } else {
+ Q_UNREACHABLE();
+ }
+ }
+
+ f->glBindProgramPipeline(mtl.ppl);
+
+ return &mtl;
+}
+
+void QQuickShapeNvprRenderNode::updatePath(ShapePathRenderData *d)
+{
+ if (d->dirty & QQuickShapeNvprRenderer::DirtyPath) {
+ if (!d->path) {
+ d->path = nvpr.genPaths(1);
+ Q_ASSERT(d->path != 0);
+ }
+ if (d->source.str.isEmpty()) {
+ nvpr.pathCommands(d->path, d->source.cmd.count(), d->source.cmd.constData(),
+ d->source.coord.count(), GL_FLOAT, d->source.coord.constData());
+ } else {
+ nvpr.pathString(d->path, GL_PATH_FORMAT_SVG_NV, d->source.str.count(), d->source.str.constData());
+ }
+ }
+
+ if (d->dirty & QQuickShapeNvprRenderer::DirtyStyle) {
+ nvpr.pathParameterf(d->path, GL_PATH_STROKE_WIDTH_NV, d->strokeWidth);
+ nvpr.pathParameteri(d->path, GL_PATH_JOIN_STYLE_NV, d->joinStyle);
+ nvpr.pathParameteri(d->path, GL_PATH_MITER_LIMIT_NV, d->miterLimit);
+ nvpr.pathParameteri(d->path, GL_PATH_END_CAPS_NV, d->capStyle);
+ nvpr.pathParameteri(d->path, GL_PATH_DASH_CAPS_NV, d->capStyle);
+ }
+
+ if (d->dirty & QQuickShapeNvprRenderer::DirtyDash) {
+ nvpr.pathParameterf(d->path, GL_PATH_DASH_OFFSET_NV, d->dashOffset);
+ // count == 0 -> no dash
+ nvpr.pathDashArray(d->path, d->dashPattern.count(), d->dashPattern.constData());
+ }
+
+ if (d->dirty)
+ d->fallbackValid = false;
+}
+
+void QQuickShapeNvprRenderNode::renderStroke(ShapePathRenderData *d, int strokeStencilValue, int writeMask)
+{
+ QQuickNvprMaterialManager::MaterialDesc *mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid);
+ f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0],
+ d->strokeColor.x(), d->strokeColor.y(), d->strokeColor.z(), d->strokeColor.w());
+ f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity());
+
+ nvpr.stencilThenCoverStrokePath(d->path, strokeStencilValue, writeMask, GL_CONVEX_HULL_NV);
+}
+
+void QQuickShapeNvprRenderNode::renderFill(ShapePathRenderData *d)
+{
+ QQuickNvprMaterialManager::MaterialDesc *mtl = nullptr;
+ if (d->fillGradientActive) {
+ mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatLinearGradient);
+ QSGTexture *tx = QQuickShapeGradientCache::currentCache()->get(d->fillGradient);
+ tx->bind();
+ // uv = vec2(coeff[0] * x + coeff[1] * y + coeff[2], coeff[3] * x + coeff[4] * y + coeff[5])
+ // where x and y are in path coordinate space, which is just what
+ // we need since the gradient's start and stop are in that space too.
+ GLfloat coeff[6] = { 1, 0, 0,
+ 0, 1, 0 };
+ nvpr.programPathFragmentInputGen(mtl->prg, 0, GL_OBJECT_LINEAR_NV, 2, coeff);
+ f->glProgramUniform2f(mtl->prg, mtl->uniLoc[2], d->fillGradient.start.x(), d->fillGradient.start.y());
+ f->glProgramUniform2f(mtl->prg, mtl->uniLoc[3], d->fillGradient.end.x(), d->fillGradient.end.y());
+ } else {
+ mtl = mtlmgr.activateMaterial(QQuickNvprMaterialManager::MatSolid);
+ f->glProgramUniform4f(mtl->prg, mtl->uniLoc[0],
+ d->fillColor.x(), d->fillColor.y(), d->fillColor.z(), d->fillColor.w());
+ }
+ f->glProgramUniform1f(mtl->prg, mtl->uniLoc[1], inheritedOpacity());
+
+ const int writeMask = 0xFF;
+ nvpr.stencilThenCoverFillPath(d->path, d->fillRule, writeMask, GL_BOUNDING_BOX_NV);
+}
+
+void QQuickShapeNvprRenderNode::renderOffscreenFill(ShapePathRenderData *d)
+{
+ if (d->fallbackValid && d->fallbackFbo)
+ return;
+
+ GLfloat bb[4];
+ nvpr.getPathParameterfv(d->path, GL_PATH_STROKE_BOUNDING_BOX_NV, bb);
+ QSize sz = QSizeF(bb[2] - bb[0] + 1, bb[3] - bb[1] + 1).toSize();
+ d->fallbackSize = QSize(qMax(32, sz.width()), qMax(32, sz.height()));
+ d->fallbackTopLeft = QPointF(bb[0], bb[1]);
+
+ if (d->fallbackFbo && d->fallbackFbo->size() != d->fallbackSize) {
+ delete d->fallbackFbo;
+ d->fallbackFbo = nullptr;
+ }
+ if (!d->fallbackFbo)
+ d->fallbackFbo = new QOpenGLFramebufferObject(d->fallbackSize, QOpenGLFramebufferObject::CombinedDepthStencil);
+ if (!d->fallbackFbo->bind())
+ return;
+
+ GLint prevViewport[4];
+ f->glGetIntegerv(GL_VIEWPORT, prevViewport);
+
+ f->glViewport(0, 0, d->fallbackSize.width(), d->fallbackSize.height());
+ f->glDisable(GL_DEPTH_TEST);
+ f->glClearColor(0, 0, 0, 0);
+ f->glClearStencil(0);
+ f->glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
+ f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
+ f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+
+ QMatrix4x4 mv;
+ mv.translate(-d->fallbackTopLeft.x(), -d->fallbackTopLeft.y());
+ nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, mv.constData());
+ QMatrix4x4 proj;
+ proj.ortho(0, d->fallbackSize.width(), d->fallbackSize.height(), 0, 1, -1);
+ nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, proj.constData());
+
+ renderFill(d);
+
+ d->fallbackFbo->release();
+ f->glEnable(GL_DEPTH_TEST);
+ f->glViewport(prevViewport[0], prevViewport[1], prevViewport[2], prevViewport[3]);
+
+ d->fallbackValid = true;
+}
+
+void QQuickShapeNvprRenderNode::setupStencilForCover(bool stencilClip, int sv)
+{
+ if (!stencilClip) {
+ // Assume stencil buffer is cleared to 0 for each frame.
+ // Within the frame dppass=GL_ZERO for glStencilOp ensures stencil is reset and so no need to clear.
+ f->glStencilFunc(GL_NOTEQUAL, 0, 0xFF);
+ f->glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
+ } else {
+ f->glStencilFunc(GL_LESS, sv, 0xFF); // pass if (sv & 0xFF) < (stencil_value & 0xFF)
+ f->glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // dppass: replace with the original value (clip's stencil ref value)
+ }
+}
+
+void QQuickShapeNvprRenderNode::render(const RenderState *state)
+{
+ f = QOpenGLContext::currentContext()->extraFunctions();
+
+ if (!nvprInited) {
+ if (!nvpr.create()) {
+ qWarning("NVPR init failed");
+ return;
+ }
+ mtlmgr.create(&nvpr);
+ nvprInited = true;
+ }
+
+ f->glUseProgram(0);
+ f->glStencilMask(~0);
+ f->glEnable(GL_STENCIL_TEST);
+
+ const bool stencilClip = state->stencilEnabled();
+ // when true, the stencil buffer already has a clip path with a ref value of sv
+ const int sv = state->stencilValue();
+ const bool hasScissor = state->scissorEnabled();
+
+ if (hasScissor) {
+ // scissor rect is already set, just enable scissoring
+ f->glEnable(GL_SCISSOR_TEST);
+ }
+
+ // Depth test against the opaque batches rendered before.
+ f->glEnable(GL_DEPTH_TEST);
+ f->glDepthFunc(GL_LESS);
+ nvpr.pathCoverDepthFunc(GL_LESS);
+ nvpr.pathStencilDepthOffset(-0.05f, -1);
+
+ bool reloadMatrices = true;
+
+ for (ShapePathRenderData &d : m_sp) {
+ updatePath(&d);
+
+ const bool hasFill = d.hasFill();
+ const bool hasStroke = d.hasStroke();
+
+ if (hasFill && stencilClip) {
+ // Fall back to a texture when complex clipping is in use and we have
+ // to fill. Reconciling glStencilFillPath's and the scenegraph's clip
+ // stencil semantics has not succeeded so far...
+ if (hasScissor)
+ f->glDisable(GL_SCISSOR_TEST);
+ renderOffscreenFill(&d);
+ reloadMatrices = true;
+ if (hasScissor)
+ f->glEnable(GL_SCISSOR_TEST);
+ }
+
+ if (reloadMatrices) {
+ reloadMatrices = false;
+ nvpr.matrixLoadf(GL_PATH_MODELVIEW_NV, matrix()->constData());
+ nvpr.matrixLoadf(GL_PATH_PROJECTION_NV, state->projectionMatrix()->constData());
+ }
+
+ // Fill!
+ if (hasFill) {
+ if (!stencilClip) {
+ setupStencilForCover(false, 0);
+ renderFill(&d);
+ } else {
+ if (!m_fallbackBlitter.isCreated())
+ m_fallbackBlitter.create();
+ f->glStencilFunc(GL_EQUAL, sv, 0xFF);
+ f->glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
+ QMatrix4x4 mv = *matrix();
+ mv.translate(d.fallbackTopLeft.x(), d.fallbackTopLeft.y());
+ m_fallbackBlitter.texturedQuad(d.fallbackFbo->texture(), d.fallbackFbo->size(),
+ *state->projectionMatrix(), mv,
+ inheritedOpacity());
+ }
+ }
+
+ // Stroke!
+ if (hasStroke) {
+ const int strokeStencilValue = 0x80;
+ const int writeMask = 0x80;
+
+ setupStencilForCover(stencilClip, sv);
+ if (stencilClip) {
+ // for the stencil step (eff. read mask == 0xFF & ~writeMask)
+ nvpr.pathStencilFunc(GL_EQUAL, sv, 0xFF);
+ // With stencilCLip == true the read mask for the stencil test before the stencil step is 0x7F.
+ // This assumes the clip stencil value is <= 127.
+ if (sv >= strokeStencilValue)
+ qWarning("Shape/NVPR: stencil clip ref value %d too large; expect rendering errors", sv);
+ }
+
+ renderStroke(&d, strokeStencilValue, writeMask);
+ }
+
+ if (stencilClip)
+ nvpr.pathStencilFunc(GL_ALWAYS, 0, ~0);
+
+ d.dirty = 0;
+ }
+
+ f->glBindProgramPipeline(0);
+}
+
+QSGRenderNode::StateFlags QQuickShapeNvprRenderNode::changedStates() const
+{
+ return BlendState | StencilState | DepthState | ScissorState;
+}
+
+QSGRenderNode::RenderingFlags QQuickShapeNvprRenderNode::flags() const
+{
+ return DepthAwareRendering; // avoid hitting the less optimal no-opaque-batch path in the renderer
+}
+
+bool QQuickShapeNvprRenderNode::isSupported()
+{
+ static const bool nvprDisabled = qEnvironmentVariableIntValue("QT_NO_NVPR") != 0;
+ return !nvprDisabled && QQuickNvprFunctions::isSupported();
+}
+
+bool QQuickNvprBlitter::create()
+{
+ if (isCreated())
+ destroy();
+
+ m_program = new QOpenGLShaderProgram;
+ if (QOpenGLContext::currentContext()->format().profile() == QSurfaceFormat::CoreProfile) {
+ m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.vert"));
+ m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect_core.frag"));
+ } else {
+ m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Vertex, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.vert"));
+ m_program->addCacheableShaderFromSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/items/shaders/shadereffect.frag"));
+ }
+ m_program->bindAttributeLocation("qt_Vertex", 0);
+ m_program->bindAttributeLocation("qt_MultiTexCoord0", 1);
+ if (!m_program->link())
+ return false;
+
+ m_matrixLoc = m_program->uniformLocation("qt_Matrix");
+ m_opacityLoc = m_program->uniformLocation("qt_Opacity");
+
+ m_buffer = new QOpenGLBuffer;
+ if (!m_buffer->create())
+ return false;
+ m_buffer->bind();
+ m_buffer->allocate(4 * sizeof(GLfloat) * 6);
+ m_buffer->release();
+
+ return true;
+}
+
+void QQuickNvprBlitter::destroy()
+{
+ if (m_program) {
+ delete m_program;
+ m_program = nullptr;
+ }
+ if (m_buffer) {
+ delete m_buffer;
+ m_buffer = nullptr;
+ }
+}
+
+void QQuickNvprBlitter::texturedQuad(GLuint textureId, const QSize &size,
+ const QMatrix4x4 &proj, const QMatrix4x4 &modelview,
+ float opacity)
+{
+ QOpenGLExtraFunctions *f = QOpenGLContext::currentContext()->extraFunctions();
+
+ m_program->bind();
+
+ QMatrix4x4 m = proj * modelview;
+ m_program->setUniformValue(m_matrixLoc, m);
+ m_program->setUniformValue(m_opacityLoc, opacity);
+
+ m_buffer->bind();
+
+ if (size != m_prevSize) {
+ m_prevSize = size;
+
+ QPointF p0(size.width() - 1, size.height() - 1);
+ QPointF p1(0, 0);
+ QPointF p2(0, size.height() - 1);
+ QPointF p3(size.width() - 1, 0);
+
+ GLfloat vertices[6 * 4] = {
+ GLfloat(p0.x()), GLfloat(p0.y()), 1, 0,
+ GLfloat(p1.x()), GLfloat(p1.y()), 0, 1,
+ GLfloat(p2.x()), GLfloat(p2.y()), 0, 0,
+
+ GLfloat(p0.x()), GLfloat(p0.y()), 1, 0,
+ GLfloat(p3.x()), GLfloat(p3.y()), 1, 1,
+ GLfloat(p1.x()), GLfloat(p1.y()), 0, 1,
+ };
+
+ m_buffer->write(0, vertices, sizeof(vertices));
+ }
+
+ m_program->enableAttributeArray(0);
+ m_program->enableAttributeArray(1);
+ f->glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
+ f->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (const void *) (2 * sizeof(GLfloat)));
+
+ f->glBindTexture(GL_TEXTURE_2D, textureId);
+
+ f->glDrawArrays(GL_TRIANGLES, 0, 6);
+
+ f->glBindTexture(GL_TEXTURE_2D, 0);
+ m_buffer->release();
+ m_program->release();
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/shapes/qquickshapenvprrenderer_p.h b/src/imports/shapes/qquickshapenvprrenderer_p.h
new file mode 100644
index 0000000000..ec7ba498f9
--- /dev/null
+++ b/src/imports/shapes/qquickshapenvprrenderer_p.h
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKSHAPENVPRRENDERER_P_H
+#define QQUICKSHAPENVPRRENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickshape_p_p.h"
+#include "qquicknvprfunctions_p.h"
+#include <qsgrendernode.h>
+#include <QColor>
+#include <QVector4D>
+#include <QDebug>
+
+#if QT_CONFIG(opengl)
+
+QT_BEGIN_NAMESPACE
+
+class QQuickShapeNvprRenderNode;
+class QOpenGLFramebufferObject;
+class QOpenGLBuffer;
+class QOpenGLExtraFunctions;
+
+class QQuickShapeNvprRenderer : public QQuickAbstractPathRenderer
+{
+public:
+ enum Dirty {
+ DirtyPath = 0x01,
+ DirtyStyle = 0x02,
+ DirtyFillRule = 0x04,
+ DirtyDash = 0x08,
+ DirtyFillGradient = 0x10,
+ DirtyList = 0x20
+ };
+
+ void beginSync(int totalCount) override;
+ void setPath(int index, const QQuickPath *path) override;
+ void setJSPath(int index, const QQuickShapePathCommands &path) override;
+ void setStrokeColor(int index, const QColor &color) override;
+ void setStrokeWidth(int index, qreal w) override;
+ void setFillColor(int index, const QColor &color) override;
+ void setFillRule(int index, QQuickShapePath::FillRule fillRule) override;
+ void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override;
+ void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override;
+ void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern) override;
+ void setFillGradient(int index, QQuickShapeGradient *gradient) override;
+ void endSync(bool async) override;
+
+ void updateNode() override;
+
+ void setNode(QQuickShapeNvprRenderNode *node);
+
+ struct NvprPath {
+ QVector<GLubyte> cmd;
+ QVector<GLfloat> coord;
+ QByteArray str;
+ };
+
+private:
+ struct ShapePathGuiData {
+ int dirty = 0;
+ NvprPath path;
+ qreal strokeWidth;
+ QColor strokeColor;
+ QColor fillColor;
+ QQuickShapePath::JoinStyle joinStyle;
+ int miterLimit;
+ QQuickShapePath::CapStyle capStyle;
+ QQuickShapePath::FillRule fillRule;
+ bool dashActive;
+ qreal dashOffset;
+ QVector<qreal> dashPattern;
+ bool fillGradientActive;
+ QQuickShapeGradientCache::GradientDesc fillGradient;
+ };
+
+ void convertPath(const QQuickPath *path, ShapePathGuiData *d);
+ void convertJSPath(const QQuickShapePathCommands &path, ShapePathGuiData *d);
+
+ QQuickShapeNvprRenderNode *m_node = nullptr;
+ int m_accDirty = 0;
+
+ QVector<ShapePathGuiData> m_sp;
+};
+
+QDebug operator<<(QDebug debug, const QQuickShapeNvprRenderer::NvprPath &path);
+
+class QQuickNvprMaterialManager
+{
+public:
+ enum Material {
+ MatSolid,
+ MatLinearGradient,
+
+ NMaterials
+ };
+
+ struct MaterialDesc {
+ GLuint ppl = 0;
+ GLuint prg = 0;
+ int uniLoc[4];
+ };
+
+ void create(QQuickNvprFunctions *nvpr);
+ MaterialDesc *activateMaterial(Material m);
+ void releaseResources();
+
+private:
+ QQuickNvprFunctions *m_nvpr = nullptr;
+ MaterialDesc m_materials[NMaterials];
+};
+
+class QQuickNvprBlitter
+{
+public:
+ bool create();
+ void destroy();
+ bool isCreated() const { return m_program != nullptr; }
+ void texturedQuad(GLuint textureId, const QSize &size,
+ const QMatrix4x4 &proj, const QMatrix4x4 &modelview,
+ float opacity);
+
+private:
+ QOpenGLShaderProgram *m_program = nullptr;
+ QOpenGLBuffer *m_buffer = nullptr;
+ int m_matrixLoc = -1;
+ int m_opacityLoc = -1;
+ QSize m_prevSize;
+};
+
+class QQuickShapeNvprRenderNode : public QSGRenderNode
+{
+public:
+ ~QQuickShapeNvprRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ StateFlags changedStates() const override;
+ RenderingFlags flags() const override;
+
+ static bool isSupported();
+
+private:
+ struct ShapePathRenderData {
+ GLuint path = 0;
+ int dirty = 0;
+ QQuickShapeNvprRenderer::NvprPath source;
+ GLfloat strokeWidth;
+ QVector4D strokeColor;
+ QVector4D fillColor;
+ GLenum joinStyle;
+ GLint miterLimit;
+ GLenum capStyle;
+ GLenum fillRule;
+ GLfloat dashOffset;
+ QVector<GLfloat> dashPattern;
+ bool fillGradientActive;
+ QQuickShapeGradientCache::GradientDesc fillGradient;
+ QOpenGLFramebufferObject *fallbackFbo = nullptr;
+ bool fallbackValid = false;
+ QSize fallbackSize;
+ QPointF fallbackTopLeft;
+
+ bool hasFill() const { return !qFuzzyIsNull(fillColor.w()) || fillGradientActive; }
+ bool hasStroke() const { return strokeWidth >= 0.0f && !qFuzzyIsNull(strokeColor.w()); }
+ };
+
+ void updatePath(ShapePathRenderData *d);
+ void renderStroke(ShapePathRenderData *d, int strokeStencilValue, int writeMask);
+ void renderFill(ShapePathRenderData *d);
+ void renderOffscreenFill(ShapePathRenderData *d);
+ void setupStencilForCover(bool stencilClip, int sv);
+
+ static bool nvprInited;
+ static QQuickNvprFunctions nvpr;
+ static QQuickNvprMaterialManager mtlmgr;
+
+ QQuickNvprBlitter m_fallbackBlitter;
+ QOpenGLExtraFunctions *f = nullptr;
+
+ QVector<ShapePathRenderData> m_sp;
+
+ friend class QQuickShapeNvprRenderer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(opengl)
+
+#endif // QQUICKSHAPENVPRRENDERER_P_H
diff --git a/src/imports/shapes/qquickshapesoftwarerenderer.cpp b/src/imports/shapes/qquickshapesoftwarerenderer.cpp
new file mode 100644
index 0000000000..b3373106af
--- /dev/null
+++ b/src/imports/shapes/qquickshapesoftwarerenderer.cpp
@@ -0,0 +1,277 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickshapesoftwarerenderer_p.h"
+#include <private/qquickpath_p_p.h>
+
+QT_BEGIN_NAMESPACE
+
+void QQuickShapeSoftwareRenderer::beginSync(int totalCount)
+{
+ if (m_sp.count() != totalCount) {
+ m_sp.resize(totalCount);
+ m_accDirty |= DirtyList;
+ }
+}
+
+void QQuickShapeSoftwareRenderer::setPath(int index, const QQuickPath *path)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.path = path ? path->path() : QPainterPath();
+ d.dirty |= DirtyPath;
+ m_accDirty |= DirtyPath;
+}
+
+void QQuickShapeSoftwareRenderer::setJSPath(int index, const QQuickShapePathCommands &path)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.path = path.toPainterPath();
+ d.dirty |= DirtyPath;
+ m_accDirty |= DirtyPath;
+}
+
+void QQuickShapeSoftwareRenderer::setStrokeColor(int index, const QColor &color)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.pen.setColor(color);
+ d.dirty |= DirtyPen;
+ m_accDirty |= DirtyPen;
+}
+
+void QQuickShapeSoftwareRenderer::setStrokeWidth(int index, qreal w)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.strokeWidth = w;
+ if (w >= 0.0f)
+ d.pen.setWidthF(w);
+ d.dirty |= DirtyPen;
+ m_accDirty |= DirtyPen;
+}
+
+void QQuickShapeSoftwareRenderer::setFillColor(int index, const QColor &color)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.fillColor = color;
+ d.brush.setColor(color);
+ d.dirty |= DirtyBrush;
+ m_accDirty |= DirtyBrush;
+}
+
+void QQuickShapeSoftwareRenderer::setFillRule(int index, QQuickShapePath::FillRule fillRule)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.fillRule = Qt::FillRule(fillRule);
+ d.dirty |= DirtyFillRule;
+ m_accDirty |= DirtyFillRule;
+}
+
+void QQuickShapeSoftwareRenderer::setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.pen.setJoinStyle(Qt::PenJoinStyle(joinStyle));
+ d.pen.setMiterLimit(miterLimit);
+ d.dirty |= DirtyPen;
+ m_accDirty |= DirtyPen;
+}
+
+void QQuickShapeSoftwareRenderer::setCapStyle(int index, QQuickShapePath::CapStyle capStyle)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ d.pen.setCapStyle(Qt::PenCapStyle(capStyle));
+ d.dirty |= DirtyPen;
+ m_accDirty |= DirtyPen;
+}
+
+void QQuickShapeSoftwareRenderer::setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ switch (strokeStyle) {
+ case QQuickShapePath::SolidLine:
+ d.pen.setStyle(Qt::SolidLine);
+ break;
+ case QQuickShapePath::DashLine:
+ d.pen.setStyle(Qt::CustomDashLine);
+ d.pen.setDashPattern(dashPattern);
+ d.pen.setDashOffset(dashOffset);
+ break;
+ default:
+ break;
+ }
+ d.dirty |= DirtyPen;
+ m_accDirty |= DirtyPen;
+}
+
+void QQuickShapeSoftwareRenderer::setFillGradient(int index, QQuickShapeGradient *gradient)
+{
+ ShapePathGuiData &d(m_sp[index]);
+ if (QQuickShapeLinearGradient *linearGradient = qobject_cast<QQuickShapeLinearGradient *>(gradient)) {
+ QLinearGradient painterGradient(linearGradient->x1(), linearGradient->y1(),
+ linearGradient->x2(), linearGradient->y2());
+ painterGradient.setStops(linearGradient->gradientStops()); // sorted
+ switch (gradient->spread()) {
+ case QQuickShapeGradient::PadSpread:
+ painterGradient.setSpread(QGradient::PadSpread);
+ break;
+ case QQuickShapeGradient::RepeatSpread:
+ painterGradient.setSpread(QGradient::RepeatSpread);
+ break;
+ case QQuickShapeGradient::ReflectSpread:
+ painterGradient.setSpread(QGradient::ReflectSpread);
+ break;
+ default:
+ break;
+ }
+ d.brush = QBrush(painterGradient);
+ } else {
+ d.brush = QBrush(d.fillColor);
+ }
+ d.dirty |= DirtyBrush;
+ m_accDirty |= DirtyBrush;
+}
+
+void QQuickShapeSoftwareRenderer::endSync(bool)
+{
+}
+
+void QQuickShapeSoftwareRenderer::setNode(QQuickShapeSoftwareRenderNode *node)
+{
+ if (m_node != node) {
+ m_node = node;
+ m_accDirty |= DirtyList;
+ }
+}
+
+void QQuickShapeSoftwareRenderer::updateNode()
+{
+ if (!m_accDirty)
+ return;
+
+ const int count = m_sp.count();
+ const bool listChanged = m_accDirty & DirtyList;
+ if (listChanged)
+ m_node->m_sp.resize(count);
+
+ m_node->m_boundingRect = QRectF();
+
+ for (int i = 0; i < count; ++i) {
+ ShapePathGuiData &src(m_sp[i]);
+ QQuickShapeSoftwareRenderNode::ShapePathRenderData &dst(m_node->m_sp[i]);
+
+ if (listChanged || (src.dirty & DirtyPath)) {
+ dst.path = src.path;
+ dst.path.setFillRule(src.fillRule);
+ }
+
+ if (listChanged || (src.dirty & DirtyFillRule))
+ dst.path.setFillRule(src.fillRule);
+
+ if (listChanged || (src.dirty & DirtyPen)) {
+ dst.pen = src.pen;
+ dst.strokeWidth = src.strokeWidth;
+ }
+
+ if (listChanged || (src.dirty & DirtyBrush))
+ dst.brush = src.brush;
+
+ src.dirty = 0;
+
+ QRectF br = dst.path.boundingRect();
+ const float sw = qMax(1.0f, dst.strokeWidth);
+ br.adjust(-sw, -sw, sw, sw);
+ m_node->m_boundingRect |= br;
+ }
+
+ m_node->markDirty(QSGNode::DirtyMaterial);
+ m_accDirty = 0;
+}
+
+QQuickShapeSoftwareRenderNode::QQuickShapeSoftwareRenderNode(QQuickShape *item)
+ : m_item(item)
+{
+}
+
+QQuickShapeSoftwareRenderNode::~QQuickShapeSoftwareRenderNode()
+{
+ releaseResources();
+}
+
+void QQuickShapeSoftwareRenderNode::releaseResources()
+{
+}
+
+void QQuickShapeSoftwareRenderNode::render(const RenderState *state)
+{
+ if (m_sp.isEmpty())
+ return;
+
+ QSGRendererInterface *rif = m_item->window()->rendererInterface();
+ QPainter *p = static_cast<QPainter *>(rif->getResource(m_item->window(), QSGRendererInterface::PainterResource));
+ Q_ASSERT(p);
+
+ const QRegion *clipRegion = state->clipRegion();
+ if (clipRegion && !clipRegion->isEmpty())
+ p->setClipRegion(*clipRegion, Qt::ReplaceClip); // must be done before setTransform
+
+ p->setTransform(matrix()->toTransform());
+ p->setOpacity(inheritedOpacity());
+
+ for (const ShapePathRenderData &d : qAsConst(m_sp)) {
+ p->setPen(d.strokeWidth >= 0.0f && d.pen.color() != Qt::transparent ? d.pen : Qt::NoPen);
+ p->setBrush(d.brush.color() != Qt::transparent ? d.brush : Qt::NoBrush);
+ p->drawPath(d.path);
+ }
+}
+
+QSGRenderNode::StateFlags QQuickShapeSoftwareRenderNode::changedStates() const
+{
+ return 0;
+}
+
+QSGRenderNode::RenderingFlags QQuickShapeSoftwareRenderNode::flags() const
+{
+ return BoundedRectRendering; // avoid fullscreen updates by saying we won't draw outside rect()
+}
+
+QRectF QQuickShapeSoftwareRenderNode::rect() const
+{
+ return m_boundingRect;
+}
+
+QT_END_NAMESPACE
diff --git a/src/imports/shapes/qquickshapesoftwarerenderer_p.h b/src/imports/shapes/qquickshapesoftwarerenderer_p.h
new file mode 100644
index 0000000000..53982ce347
--- /dev/null
+++ b/src/imports/shapes/qquickshapesoftwarerenderer_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKSHAPESOFTWARERENDERER_P_H
+#define QQUICKSHAPESOFTWARERENDERER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of a number of Qt sources files. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qquickshape_p_p.h"
+#include <qsgrendernode.h>
+#include <QPen>
+#include <QBrush>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickShapeSoftwareRenderNode;
+
+class QQuickShapeSoftwareRenderer : public QQuickAbstractPathRenderer
+{
+public:
+ enum Dirty {
+ DirtyPath = 0x01,
+ DirtyPen = 0x02,
+ DirtyFillRule = 0x04,
+ DirtyBrush = 0x08,
+ DirtyList = 0x10
+ };
+
+ void beginSync(int totalCount) override;
+ void setPath(int index, const QQuickPath *path) override;
+ void setJSPath(int index, const QQuickShapePathCommands &path) override;
+ void setStrokeColor(int index, const QColor &color) override;
+ void setStrokeWidth(int index, qreal w) override;
+ void setFillColor(int index, const QColor &color) override;
+ void setFillRule(int index, QQuickShapePath::FillRule fillRule) override;
+ void setJoinStyle(int index, QQuickShapePath::JoinStyle joinStyle, int miterLimit) override;
+ void setCapStyle(int index, QQuickShapePath::CapStyle capStyle) override;
+ void setStrokeStyle(int index, QQuickShapePath::StrokeStyle strokeStyle,
+ qreal dashOffset, const QVector<qreal> &dashPattern) override;
+ void setFillGradient(int index, QQuickShapeGradient *gradient) override;
+ void endSync(bool async) override;
+
+ void updateNode() override;
+
+ void setNode(QQuickShapeSoftwareRenderNode *node);
+
+private:
+ QQuickShapeSoftwareRenderNode *m_node = nullptr;
+ int m_accDirty = 0;
+ struct ShapePathGuiData {
+ int dirty = 0;
+ QPainterPath path;
+ QPen pen;
+ float strokeWidth;
+ QColor fillColor;
+ QBrush brush;
+ Qt::FillRule fillRule;
+ };
+ QVector<ShapePathGuiData> m_sp;
+};
+
+class QQuickShapeSoftwareRenderNode : public QSGRenderNode
+{
+public:
+ QQuickShapeSoftwareRenderNode(QQuickShape *item);
+ ~QQuickShapeSoftwareRenderNode();
+
+ void render(const RenderState *state) override;
+ void releaseResources() override;
+ StateFlags changedStates() const override;
+ RenderingFlags flags() const override;
+ QRectF rect() const override;
+
+private:
+ QQuickShape *m_item;
+
+ struct ShapePathRenderData {
+ QPainterPath path;
+ QPen pen;
+ float strokeWidth;
+ QBrush brush;
+ };
+ QVector<ShapePathRenderData> m_sp;
+ QRectF m_boundingRect;
+
+ friend class QQuickShapeSoftwareRenderer;
+};
+
+QT_END_NAMESPACE
+
+#endif // QQUICKSHAPESOFTWARERENDERER_P_H
diff --git a/src/imports/shapes/shapes.pro b/src/imports/shapes/shapes.pro
new file mode 100644
index 0000000000..80e6a22142
--- /dev/null
+++ b/src/imports/shapes/shapes.pro
@@ -0,0 +1,31 @@
+CXX_MODULE = qml
+TARGET = qmlshapesplugin
+TARGETPATH = QtQuick/Shapes
+IMPORT_VERSION = 1.0
+
+QT = core gui qml quick quick-private
+
+HEADERS += \
+ qquickshape_p.h \
+ qquickshape_p_p.h \
+ qquickshapegenericrenderer_p.h \
+ qquickshapesoftwarerenderer_p.h
+
+SOURCES += \
+ plugin.cpp \
+ qquickshape.cpp \
+ qquickshapegenericrenderer.cpp \
+ qquickshapesoftwarerenderer.cpp
+
+qtConfig(opengl) {
+ HEADERS += \
+ qquicknvprfunctions_p.h \
+ qquicknvprfunctions_p_p.h \
+ qquickshapenvprrenderer_p.h
+
+ SOURCES += \
+ qquicknvprfunctions.cpp \
+ qquickshapenvprrenderer.cpp
+}
+
+load(qml_plugin)
diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml
index 8c1744a2b2..7ff51bb6d6 100644
--- a/src/imports/testlib/TestCase.qml
+++ b/src/imports/testlib/TestCase.qml
@@ -757,6 +757,10 @@ Item {
bProperties.push(i); // collect exp's properties
}
+ if (aProperties.length == 0 && bProperties.length == 0) { // at least a special case for QUrl
+ return eq && (JSON.stringify(act) == JSON.stringify(exp));
+ }
+
// Ensures identical properties name
return eq && qtest_compareInternal(aProperties.sort(), bProperties.sort());
@@ -837,7 +841,14 @@ Item {
Returns a snapshot image object of the given \a item.
- The returned image object has the following methods:
+ The returned image object has the following properties:
+ \list
+ \li width Returns the width of the underlying image (since 5.10)
+ \li height Returns the height of the underlying image (since 5.10)
+ \li size Returns the size of the underlying image (since 5.10)
+ \endlist
+
+ Additionally, the returned image object has the following methods:
\list
\li red(x, y) Returns the red channel value of the pixel at \a x, \a y position
\li green(x, y) Returns the green channel value of the pixel at \a x, \a y position
@@ -858,6 +869,21 @@ Item {
var newImage = grabImage(rect);
verify(!newImage.equals(image));
\endcode
+ \li save(path) Saves the image to the given \a path. If the image cannot
+ be saved, an exception will be thrown. (since 5.10)
+
+ This can be useful to perform postmortem analysis on failing tests, for
+ example:
+
+ \code
+ var image = grabImage(rect);
+ try {
+ compare(image.width, 100);
+ } catch (ex) {
+ image.save("debug.png");
+ throw ex;
+ }
+ \endcode
\endlist
diff --git a/src/particles/qquickv4particledata.cpp b/src/particles/qquickv4particledata.cpp
index 967652f31a..e8376f1c27 100644
--- a/src/particles/qquickv4particledata.cpp
+++ b/src/particles/qquickv4particledata.cpp
@@ -296,123 +296,112 @@ public:
QV4::PersistentValue proto;
};
-static QV4::ReturnedValue particleData_discard(QV4::CallContext *ctx)
+static void particleData_discard(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject());
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject);
if (!r || !r->d()->datum)
- return ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));
r->d()->datum->lifeSpan = 0; //Don't kill(), because it could still be in the middle of being created
- return QV4::Encode::undefined();
+ RETURN_RESULT(QV4::Encode::undefined());
}
-static QV4::ReturnedValue particleData_lifeLeft(QV4::CallContext *ctx)
+static void particleData_lifeLeft(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject());
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject);
if (!r || !r->d()->datum)
- return ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));
- return QV4::Encode(r->d()->datum->lifeLeft(r->d()->particleSystem));
+ RETURN_RESULT(QV4::Encode(r->d()->datum->lifeLeft(r->d()->particleSystem)));
}
-static QV4::ReturnedValue particleData_curSize(QV4::CallContext *ctx)
+static void particleData_curSize(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject());
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject);
if (!r || !r->d()->datum)
- return ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));
- return QV4::Encode(r->d()->datum->curSize(r->d()->particleSystem));
+ RETURN_RESULT(QV4::Encode(r->d()->datum->curSize(r->d()->particleSystem)));
}
-#define COLOR_GETTER_AND_SETTER(VAR, NAME) static QV4::ReturnedValue particleData_get_ ## NAME (QV4::CallContext *ctx) \
+#define COLOR_GETTER_AND_SETTER(VAR, NAME) static void particleData_get_ ## NAME (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \
{ \
- QV4::Scope scope(ctx); \
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject()); \
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum) \
- ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); \
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
\
- return QV4::Encode((r->d()->datum->color. VAR )/255.0);\
+ RETURN_RESULT(QV4::Encode((r->d()->datum->color. VAR )/255.0));\
}\
\
-static QV4::ReturnedValue particleData_set_ ## NAME (QV4::CallContext *ctx)\
+static void particleData_set_ ## NAME (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\
{\
- QV4::Scope scope(ctx); \
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject()); \
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum)\
- ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));\
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
\
- double d = ctx->argc() ? ctx->args()[0].toNumber() : 0; \
+ double d = callData->argc ? callData->args[0].toNumber() : 0; \
r->d()->datum->color. VAR = qMin(255, qMax(0, (int)::floor(d * 255.0)));\
- return QV4::Encode::undefined(); \
+ RETURN_UNDEFINED(); \
}
-#define SEMIBOOL_GETTER_AND_SETTER(VARIABLE) static QV4::ReturnedValue particleData_get_ ## VARIABLE (QV4::CallContext *ctx) \
+#define SEMIBOOL_GETTER_AND_SETTER(VARIABLE) static void particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \
{ \
- QV4::Scope scope(ctx); \
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject()); \
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum) \
- ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); \
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
\
- return QV4::Encode(r->d()->datum-> VARIABLE);\
+ RETURN_RESULT(QV4::Encode(r->d()->datum-> VARIABLE));\
}\
\
-static QV4::ReturnedValue particleData_set_ ## VARIABLE (QV4::CallContext *ctx)\
+static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\
{\
- QV4::Scope scope(ctx); \
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject()); \
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum)\
- ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));\
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
\
- r->d()->datum-> VARIABLE = (ctx->argc() && ctx->args()[0].toBoolean()) ? 1.0 : 0.0;\
- return QV4::Encode::undefined(); \
+ r->d()->datum-> VARIABLE = (callData->argc && callData->args[0].toBoolean()) ? 1.0 : 0.0;\
+ RETURN_UNDEFINED(); \
}
-#define FLOAT_GETTER_AND_SETTER(VARIABLE) static QV4::ReturnedValue particleData_get_ ## VARIABLE (QV4::CallContext *ctx) \
+#define FLOAT_GETTER_AND_SETTER(VARIABLE) static void particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \
{ \
- QV4::Scope scope(ctx); \
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject()); \
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum) \
- ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); \
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
\
- return QV4::Encode(r->d()->datum-> VARIABLE);\
+ RETURN_RESULT(QV4::Encode(r->d()->datum-> VARIABLE));\
}\
\
-static QV4::ReturnedValue particleData_set_ ## VARIABLE (QV4::CallContext *ctx)\
+static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\
{\
- QV4::Scope scope(ctx); \
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject()); \
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum)\
- ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));\
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
\
- r->d()->datum-> VARIABLE = ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan();\
- return QV4::Encode::undefined(); \
+ r->d()->datum-> VARIABLE = callData->argc ? callData->args[0].toNumber() : qt_qnan();\
+ RETURN_UNDEFINED(); \
}
-#define FAKE_FLOAT_GETTER_AND_SETTER(VARIABLE, GETTER, SETTER) static QV4::ReturnedValue particleData_get_ ## VARIABLE (QV4::CallContext *ctx) \
+#define FAKE_FLOAT_GETTER_AND_SETTER(VARIABLE, GETTER, SETTER) static void particleData_get_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) \
{ \
- QV4::Scope scope(ctx); \
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject()); \
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum) \
- ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object")); \
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object"))); \
\
- return QV4::Encode(r->d()->datum-> GETTER (r->d()->particleSystem));\
+ RETURN_RESULT(QV4::Encode(r->d()->datum-> GETTER (r->d()->particleSystem)));\
}\
\
-static QV4::ReturnedValue particleData_set_ ## VARIABLE (QV4::CallContext *ctx)\
+static void particleData_set_ ## VARIABLE (const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)\
{\
- QV4::Scope scope(ctx); \
- QV4::Scoped<QV4ParticleData> r(scope, ctx->thisObject()); \
+ QV4::Scoped<QV4ParticleData> r(scope, callData->thisObject); \
if (!r || !r->d()->datum)\
- ctx->engine()->throwError(QStringLiteral("Not a valid ParticleData object"));\
+ RETURN_RESULT(scope.engine->throwError(QStringLiteral("Not a valid ParticleData object")));\
\
- r->d()->datum-> SETTER (ctx->argc() ? ctx->args()[0].toNumber() : qt_qnan(), r->d()->particleSystem);\
- return QV4::Encode::undefined(); \
+ r->d()->datum-> SETTER (callData->argc ? callData->args[0].toNumber() : qt_qnan(), r->d()->particleSystem);\
+ RETURN_UNDEFINED(); \
}
#define REGISTER_ACCESSOR(PROTO, ENGINE, VARIABLE, NAME) \
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
index 430ba4f255..e89b7a63d4 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp
@@ -76,7 +76,7 @@ QV4::Heap::SimpleCallContext *QV4DataCollector::findScope(QV4::ExecutionContext
if (!ctxt)
return 0;
- QV4::Scope s(ctxt->d()->engine);
+ QV4::Scope s(ctxt);
QV4::ScopedContext ctx(s, ctxt);
for (; scope > 0 && ctx; --scope)
ctx = ctx->d()->outer;
diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
index 9e8be84c33..b82df9c6a9 100644
--- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
+++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp
@@ -260,7 +260,7 @@ QV4::Function *QV4Debugger::getFunction() const
if (QV4::Function *function = context->getFunction())
return function;
else
- return context->d()->engine->globalCode;
+ return m_engine->globalCode;
}
void QV4Debugger::runJobUnpaused()
diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
index 6cb1ab4051..d536fd51ed 100644
--- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
+++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
@@ -465,7 +465,7 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a
}
TRACE_PROTOCOL("Context: " << executionContext);
- QV4::ExecutionEngine *engine = executionContext->d()->engine;
+ QV4::ExecutionEngine *engine = executionContext->engine();
if (!engine) {
setError(response, QStringLiteral("No execution engine passed"));
return;
@@ -520,7 +520,7 @@ void NativeDebugger::handleExpressions(QJsonObject *response, const QJsonObject
}
TRACE_PROTOCOL("Context: " << executionContext);
- QV4::ExecutionEngine *engine = executionContext->d()->engine;
+ QV4::ExecutionEngine *engine = executionContext->engine();
if (!engine) {
setError(response, QStringLiteral("No execution engine passed"));
return;
@@ -672,7 +672,7 @@ QV4::Function *NativeDebugger::getFunction() const
if (QV4::Function *function = context->getFunction())
return function;
else
- return context->d()->engine->globalCode;
+ return m_engine->globalCode;
}
void NativeDebugger::pauseAndWait()
diff --git a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
index 90e817e2fc..510c745d4e 100644
--- a/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_profiler/qqmlprofileradapter.cpp
@@ -66,8 +66,6 @@ QQmlProfilerAdapter::QQmlProfilerAdapter(QQmlProfilerService *service, QQmlEngin
}
// convert to QByteArrays that can be sent to the debug client
-// use of QDataStream can skew results
-// (see tst_qqmldebugtrace::trace() benchmark)
static void qQmlProfilerDataToByteArrays(const QQmlProfilerData &d,
QQmlProfiler::LocationHash &locations,
QList<QByteArray> &messages,
diff --git a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
index 0c9fc36463..35beb0ee0d 100644
--- a/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
+++ b/src/plugins/qmltooling/qmldbg_quickprofiler/qquickprofileradapter.cpp
@@ -74,8 +74,6 @@ QQuickProfilerAdapter::~QQuickProfilerAdapter()
}
// convert to QByteArrays that can be sent to the debug client
-// use of QDataStream can skew results
-// (see tst_qqmldebugtrace::trace() benchmark)
static void qQuickProfilerDataToByteArrays(const QQuickProfilerData &data,
QList<QByteArray> &messages)
{
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri
index 31ee917b39..d9b985e33b 100644
--- a/src/qml/compiler/compiler.pri
+++ b/src/qml/compiler/compiler.pri
@@ -41,7 +41,7 @@ SOURCES += \
unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp
else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp
-qtConfig(private_tests):unix: QMAKE_USE_PRIVATE += libdl
+qtConfig(private_tests):qtConfig(dlopen): QMAKE_USE_PRIVATE += libdl
}
qmldevtools_build|qtConfig(qml-interpreter) {
@@ -52,3 +52,7 @@ qmldevtools_build|qtConfig(qml-interpreter) {
$$PWD/qv4instr_moth.cpp \
$$PWD/qv4isel_moth.cpp
}
+
+gcc {
+ equals(QT_GCC_MAJOR_VERSION, 5): QMAKE_CXXFLAGS += -fno-strict-aliasing
+}
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index de04116a6b..d8cd31de29 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -1961,7 +1961,7 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int
// with the correct QML context.
// Look for IDs first.
- for (const IdMapping &mapping : qAsConst(_idObjects))
+ for (const IdMapping &mapping : qAsConst(_idObjects)) {
if (name == mapping.name) {
if (_function->isQmlBinding)
_function->idObjectDependencies.insert(mapping.idIndex);
@@ -1979,12 +1979,14 @@ QV4::IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int
result->isReadOnly = true; // don't allow use as lvalue
return result;
}
+ }
- {
+ if (name.at(0).isUpper()) {
QQmlTypeNameCache::Result r = imports->query(name);
if (r.isValid()) {
if (r.scriptIndex != -1) {
- return subscript(_block->TEMP(_importedScriptsTemp), _block->CONST(QV4::IR::SInt32Type, r.scriptIndex));
+ return _block->SUBSCRIPT(_block->TEMP(_importedScriptsTemp),
+ _block->CONST(QV4::IR::SInt32Type, r.scriptIndex));
} else if (r.type) {
QV4::IR::Name *typeName = _block->NAME(name, line, col);
// Make sure the run-time loads this through the more efficient singleton getter.
diff --git a/src/qml/compiler/qqmlpropertyvalidator_p.h b/src/qml/compiler/qqmlpropertyvalidator_p.h
index d0bd314461..e37b8141f4 100644
--- a/src/qml/compiler/qqmlpropertyvalidator_p.h
+++ b/src/qml/compiler/qqmlpropertyvalidator_p.h
@@ -69,8 +69,8 @@ private:
bool canCoerce(int to, QQmlPropertyCache *fromMo) const;
- QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const Q_REQUIRED_RESULT;
- QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const Q_REQUIRED_RESULT;
+ Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QV4::CompiledData::Location &location, const QString &description) const;
+ Q_REQUIRED_RESULT QVector<QQmlCompileError> recordError(const QQmlCompileError &error) const;
QString stringAt(int index) const { return qmlUnit->stringAt(index); }
QQmlEnginePrivate *enginePrivate;
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index 693a4230ba..5b7a7f9050 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -113,6 +113,18 @@ static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body,
}
}
+static inline bool isSimpleExpr(IR::Expr *e)
+{
+ switch (e->exprKind) {
+ case IR::Expr::TempExpr:
+ case IR::Expr::ArgLocalExpr:
+ case IR::Expr::ConstExpr:
+ return true;
+ default:
+ return false;
+ }
+}
+
Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode)
: _cg(cg)
, _sourceCode(sourceCode)
@@ -573,28 +585,6 @@ IR::Expr *Codegen::member(IR::Expr *base, const QString *name)
}
}
-IR::Expr *Codegen::subscript(IR::Expr *base, IR::Expr *index)
-{
- if (hasError)
- return 0;
-
- if (! base->asTemp() && !base->asArgLocal()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), base);
- base = _block->TEMP(t);
- }
-
- if (! index->asTemp() && !index->asArgLocal() && !index->asConst()) {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), index);
- index = _block->TEMP(t);
- }
-
- Q_ASSERT(base->asTemp() || base->asArgLocal());
- Q_ASSERT(index->asTemp() || index->asArgLocal() || index->asConst());
- return _block->SUBSCRIPT(base, index);
-}
-
IR::Expr *Codegen::argument(IR::Expr *expr)
{
if (expr && !expr->asTemp()) {
@@ -647,12 +637,14 @@ IR::Expr *Codegen::unop(IR::AluOp op, IR::Expr *expr, const SourceLocation &loc)
}
}
}
- if (!expr->asTemp() && !expr->asArgLocal()) {
- const unsigned t = _block->newTemp();
- setLocation(move(_block->TEMP(t), expr), loc);
- expr = _block->TEMP(t);
- }
- Q_ASSERT(expr->asTemp() || expr->asArgLocal());
+
+ TempScope scope(_function);
+ if (isSimpleExpr(expr))
+ return _block->UNOP(op, expr);
+
+ const unsigned t = _block->newTemp();
+ setLocation(move(_block->TEMP(t), expr), loc);
+ expr = _block->TEMP(t);
return _block->UNOP(op, expr);
}
@@ -661,6 +653,8 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AS
if (hasError)
return 0;
+ TempScope scope(_function);
+
if (IR::Const *c1 = left->asConst()) {
if (IR::Const *c2 = right->asConst()) {
if ((c1->type & IR::NumberType) && (c2->type & IR::NumberType)) {
@@ -748,6 +742,8 @@ IR::Stmt *Codegen::move(IR::Expr *target, IR::Expr *source, IR::AluOp op)
return move(target, binop(op, target, source));
}
+ TempScope scope(_function);
+
if (!source->asTemp() && !source->asConst() && !target->asTemp() && !source->asArgLocal() && !target->asArgLocal()) {
unsigned t = _block->newTemp();
_block->MOVE(_block->TEMP(t), source);
@@ -767,6 +763,8 @@ IR::Stmt *Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock
if (hasError)
return 0;
+ TempScope scope(_function);
+
if (! (cond->asTemp() || (cond->asBinop() && cjumpCanHandle(cond->asBinop()->op)) )) {
const unsigned t = _block->newTemp();
move(_block->TEMP(t), cond);
@@ -786,12 +784,16 @@ void Codegen::accept(Node *node)
void Codegen::statement(Statement *ast)
{
+ TempScope scope(_function);
+
_block->nextLocation = ast->firstSourceLocation();
accept(ast);
}
void Codegen::statement(ExpressionNode *ast)
{
+ TempScope scope(_function);
+
if (! ast) {
return;
} else {
@@ -900,6 +902,7 @@ void Codegen::variableDeclaration(VariableDeclaration *ast)
if (lhs->asArgLocal()) {
move(lhs, initializer);
} else {
+ TempScope scope(_function);
int initialized = _block->newTemp();
move(_block->TEMP(initialized), initializer);
move(lhs, _block->TEMP(initialized));
@@ -1103,6 +1106,10 @@ bool Codegen::visit(ArrayLiteral *ast)
if (hasError)
return false;
+ const unsigned t = _block->newTemp();
+
+ TempScope scope(_function);
+
IR::ExprList *args = 0;
IR::ExprList *current = 0;
for (ElementList *it = ast->elements; it; it = it->next) {
@@ -1148,7 +1155,6 @@ bool Codegen::visit(ArrayLiteral *ast)
current->expr = _block->CONST(IR::MissingType, 0);
}
- const unsigned t = _block->newTemp();
move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_array, 0, 0), args));
_expr.code = _block->TEMP(t);
return false;
@@ -1159,11 +1165,25 @@ bool Codegen::visit(ArrayMemberExpression *ast)
if (hasError)
return false;
- Result base = expression(ast->base);
- Result index = expression(ast->expression);
+ IR::Expr *base = *expression(ast->base);
+ if (hasError)
+ return false;
+ if (!isSimpleExpr(base)) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), base);
+ base = _block->TEMP(t);
+ }
+
+ IR::Expr *index = *expression(ast->expression);
if (hasError)
return false;
- _expr.code = subscript(*base, *index);
+ if (!isSimpleExpr(index)) {
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), index);
+ index = _block->TEMP(t);
+ }
+
+ _expr.code = _block->SUBSCRIPT(base, index);
return false;
}
@@ -1299,14 +1319,12 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
}
- if (_expr.accept(nx)) {
- move(left, *right, baseOp(ast->op));
- } else {
- const unsigned t = _block->newTemp();
- move(_block->TEMP(t), *right);
- move(left, _block->TEMP(t), baseOp(ast->op));
+ TempScope scope(_function);
+ const unsigned t = _block->newTemp();
+ move(_block->TEMP(t), *right);
+ move(left, _block->TEMP(t), baseOp(ast->op));
+ if (!_expr.accept(nx))
_expr.code = left;
- }
break;
}
@@ -1320,6 +1338,7 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::Lt:
case QSOperator::StrictEqual:
case QSOperator::StrictNotEqual: {
+ TempScope scope(_function);
if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) {
const unsigned t = _block->newTemp();
setLocation(move(_block->TEMP(t), left), ast->operatorToken);
@@ -1349,6 +1368,7 @@ bool Codegen::visit(BinaryExpression *ast)
case QSOperator::RShift:
case QSOperator::Sub:
case QSOperator::URShift: {
+ TempScope scope(_function);
if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) {
const unsigned t = _block->newTemp();
setLocation(move(_block->TEMP(t), left), ast->operatorToken);
@@ -1400,6 +1420,7 @@ bool Codegen::visit(ConditionalExpression *ast)
IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler());
const unsigned t = _block->newTemp();
+ TempScope scope(_function);
condition(ast->expression, iftrue, iffalse);
@@ -1503,6 +1524,8 @@ bool Codegen::visit(FunctionExpression *ast)
if (hasError)
return false;
+ TempScope scope(_function);
+
int function = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0);
_expr.code = _block->CLOSURE(function);
return false;
@@ -1583,6 +1606,7 @@ bool Codegen::visit(NewExpression *ast)
{
if (hasError)
return false;
+ TempScope scope(_function);
Result base = expression(ast->expression);
if (hasError)
@@ -1602,6 +1626,10 @@ bool Codegen::visit(NewMemberExpression *ast)
if (hasError)
return false;
+ const unsigned t = _block->newTemp();
+
+ TempScope scope(_function);
+
Result base = expression(ast->base);
if (hasError)
return false;
@@ -1622,7 +1650,6 @@ bool Codegen::visit(NewMemberExpression *ast)
(*args_it)->init(actual);
args_it = &(*args_it)->next;
}
- const unsigned t = _block->newTemp();
move(_block->TEMP(t), _block->NEW(expr, args));
_expr.code = _block->TEMP(t);
return false;
@@ -1633,10 +1660,12 @@ bool Codegen::visit(NotExpression *ast)
if (hasError)
return false;
+ const unsigned r = _block->newTemp();
+ TempScope scope(_function);
+
Result expr = expression(ast->expression);
if (hasError)
return false;
- const unsigned r = _block->newTemp();
setLocation(move(_block->TEMP(r), unop(IR::OpNot, *expr, ast->notToken)), ast->notToken);
_expr.code = _block->TEMP(r);
return false;
@@ -1689,6 +1718,9 @@ bool Codegen::visit(ObjectLiteral *ast)
QMap<QString, ObjectPropertyValue> valueMap;
+ const unsigned t = _block->newTemp();
+ TempScope scope(_function);
+
for (PropertyAssignmentList *it = ast->properties; it; it = it->next) {
QString name = it->assignment->name->asString();
if (PropertyNameAndValue *nv = AST::cast<AST::PropertyNameAndValue *>(it->assignment)) {
@@ -1702,7 +1734,13 @@ bool Codegen::visit(ObjectLiteral *ast)
return false;
}
- valueMap[name].value = *value;
+ if (IR::Const *c = (*value)->asConst()) {
+ valueMap[name].value = c;
+ } else {
+ unsigned t = _block->newTemp();
+ move(_block->TEMP(t), *value);
+ valueMap[name].value = _block->TEMP(t);
+ }
} else if (PropertyGetterSetter *gs = AST::cast<AST::PropertyGetterSetter *>(it->assignment)) {
const int function = defineFunction(name, gs, gs->formals, gs->functionBody ? gs->functionBody->elements : 0);
ObjectPropertyValue &v = valueMap[name];
@@ -1773,12 +1811,9 @@ bool Codegen::visit(ObjectLiteral *ast)
current = current->next;
current->expr = _block->CONST(IR::BoolType, true);
- unsigned value = _block->newTemp();
- move(_block->TEMP(value), it->value);
-
current->next = _function->New<IR::ExprList>();
current = current->next;
- current->expr = _block->TEMP(value);
+ current->expr = it->value;
} else {
current->next = _function->New<IR::ExprList>();
current = current->next;
@@ -1811,7 +1846,6 @@ bool Codegen::visit(ObjectLiteral *ast)
args->next = arrayEntries;
}
- const unsigned t = _block->newTemp();
move(_block->TEMP(t), _block->CALL(_block->NAME(IR::Name::builtin_define_object_literal,
ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), args));
@@ -1837,6 +1871,7 @@ bool Codegen::visit(PostDecrementExpression *ast)
const unsigned oldValue = _block->newTemp();
setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->decrementToken)), ast->decrementToken);
+ TempScope scope(_function);
const unsigned newValue = _block->newTemp();
setLocation(move(_block->TEMP(newValue), binop(IR::OpSub, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->decrementToken)), ast->decrementToken);
setLocation(move(*expr, _block->TEMP(newValue)), ast->decrementToken);
@@ -1865,6 +1900,7 @@ bool Codegen::visit(PostIncrementExpression *ast)
const unsigned oldValue = _block->newTemp();
setLocation(move(_block->TEMP(oldValue), unop(IR::OpUPlus, *expr, ast->incrementToken)), ast->incrementToken);
+ TempScope scope(_function);
const unsigned newValue = _block->newTemp();
setLocation(move(_block->TEMP(newValue), binop(IR::OpAdd, _block->TEMP(oldValue), _block->CONST(IR::NumberType, 1), ast->incrementToken)), ast->incrementToken);
setLocation(move(*expr, _block->TEMP(newValue)), ast->incrementToken);
@@ -1961,10 +1997,12 @@ bool Codegen::visit(TildeExpression *ast)
if (hasError)
return false;
+ const unsigned t = _block->newTemp();
+ TempScope scope(_function);
+
Result expr = expression(ast->expression);
if (hasError)
return false;
- const unsigned t = _block->newTemp();
setLocation(move(_block->TEMP(t), unop(IR::OpCompl, *expr, ast->tildeToken)), ast->tildeToken);
_expr.code = _block->TEMP(t);
return false;
@@ -1988,6 +2026,8 @@ bool Codegen::visit(TypeOfExpression *ast)
if (hasError)
return false;
+ TempScope scope(_function);
+
Result expr = expression(ast->expression);
if (hasError)
return false;
@@ -2030,6 +2070,8 @@ bool Codegen::visit(VoidExpression *ast)
if (hasError)
return false;
+ TempScope scope(_function);
+
statement(ast->expression);
_expr.code = _block->CONST(IR::UndefinedType, 0);
return false;
@@ -2040,6 +2082,8 @@ bool Codegen::visit(FunctionDeclaration * ast)
if (hasError)
return false;
+ TempScope scope(_function);
+
if (_variableEnvironment->compilationMode == QmlBinding)
move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0));
_expr.accept(nx);
@@ -2203,6 +2247,8 @@ bool Codegen::visit(Block *ast)
if (hasError)
return false;
+ TempScope scope(_function);
+
for (StatementList *it = ast->statements; it; it = it->next) {
statement(it->statement);
}
@@ -2214,6 +2260,8 @@ bool Codegen::visit(BreakStatement *ast)
if (hasError)
return false;
+ TempScope scope(_function);
+
if (!_loop) {
throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Break outside of loop"));
return false;
@@ -2241,6 +2289,8 @@ bool Codegen::visit(ContinueStatement *ast)
if (hasError)
return false;
+ TempScope scope(_function);
+
Loop *loop = 0;
if (ast->label.isEmpty()) {
for (loop = _loop; loop; loop = loop->parent) {
@@ -2280,6 +2330,8 @@ bool Codegen::visit(DoWhileStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
IR::BasicBlock *loopbody = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *loopcond = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *loopend = _function->newBasicBlock(exceptionHandler());
@@ -2315,6 +2367,8 @@ bool Codegen::visit(ExpressionStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
if (_variableEnvironment->compilationMode == EvalCode || _variableEnvironment->compilationMode == QmlBinding) {
Result e = expression(ast->expression);
if (*e)
@@ -2330,6 +2384,8 @@ bool Codegen::visit(ForEachStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler());
@@ -2376,6 +2432,8 @@ bool Codegen::visit(ForStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler());
@@ -2412,6 +2470,8 @@ bool Codegen::visit(IfStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
IR::BasicBlock *iftrue = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *iffalse = ast->ko ? _function->newBasicBlock(exceptionHandler()) : 0;
IR::BasicBlock *endif = _function->newBasicBlock(exceptionHandler());
@@ -2438,6 +2498,8 @@ bool Codegen::visit(LabelledStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
// check that no outer loop contains the label
Loop *l = _loop;
while (l) {
@@ -2475,6 +2537,8 @@ bool Codegen::visit(LocalForEachStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
IR::BasicBlock *foreachin = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *foreachbody = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *foreachend = _function->newBasicBlock(exceptionHandler());
@@ -2515,6 +2579,8 @@ bool Codegen::visit(LocalForStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
IR::BasicBlock *forcond = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *forbody = _function->newBasicBlock(exceptionHandler());
IR::BasicBlock *forstep = _function->newBasicBlock(exceptionHandler());
@@ -2577,6 +2643,8 @@ bool Codegen::visit(SwitchStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
IR::BasicBlock *switchend = _function->newBasicBlock(exceptionHandler());
if (ast->block) {
@@ -2672,6 +2740,8 @@ bool Codegen::visit(ThrowStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
Result expr = expression(ast->expression);
move(_block->TEMP(_returnAddress), *expr);
IR::ExprList *throwArgs = _function->New<IR::ExprList>();
@@ -2685,6 +2755,8 @@ bool Codegen::visit(TryStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
_function->hasTry = true;
if (_function->isStrict && ast->catchExpression &&
@@ -2763,6 +2835,8 @@ bool Codegen::visit(TryStatement *ast)
_function->addBasicBlock(finallyBody);
_block = finallyBody;
+ TempScope scope(_function);
+
int hasException = _block->newTemp();
move(_block->TEMP(hasException), _block->CALL(_block->NAME(IR::Name::builtin_unwind_exception, /*line*/0, /*column*/0), 0));
@@ -2847,6 +2921,8 @@ bool Codegen::visit(WithStatement *ast)
if (hasError)
return true;
+ TempScope scope(_function);
+
_function->hasWith = true;
const int withObject = _block->newTemp();
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 239ed5c4b9..31e9cc0f46 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -249,6 +249,17 @@ protected:
}
};
+ struct TempScope {
+ TempScope(QV4::IR::Function *f)
+ : function(f),
+ tempCountForScope(f->currentTemp) {}
+ ~TempScope() {
+ function->currentTemp = tempCountForScope;
+ }
+ QV4::IR::Function *function;
+ int tempCountForScope;
+ };
+
Environment *newEnvironment(AST::Node *node, Environment *parent, CompilationMode compilationMode)
{
Environment *env = new Environment(parent, compilationMode);
@@ -311,7 +322,6 @@ protected:
}
QV4::IR::Expr *member(QV4::IR::Expr *base, const QString *name);
- QV4::IR::Expr *subscript(QV4::IR::Expr *base, QV4::IR::Expr *index);
QV4::IR::Expr *argument(QV4::IR::Expr *expr);
QV4::IR::Expr *reference(QV4::IR::Expr *expr);
QV4::IR::Expr *unop(QV4::IR::AluOp op, QV4::IR::Expr *expr, const AST::SourceLocation &loc = AST::SourceLocation());
diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp
index 9832e1c49b..68063c2f71 100644
--- a/src/qml/compiler/qv4compileddata.cpp
+++ b/src/qml/compiler/qv4compileddata.cpp
@@ -82,7 +82,7 @@ static QString cacheFilePath(const QUrl &url)
{
const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url);
const QString localCachePath = localSourcePath + QLatin1Char('c');
- if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable())
+ if (QFile::exists(localCachePath) || QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable())
return localCachePath;
QCryptographicHash fileNameHash(QCryptographicHash::Sha1);
fileNameHash.addData(localSourcePath.toUtf8());
@@ -176,7 +176,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine)
for (uint i = 0; i < data->jsClassTableSize; ++i) {
int memberCount = 0;
const CompiledData::JSClassMember *member = data->jsClassAt(i, &memberCount);
- QV4::InternalClass *klass = engine->emptyClass;
+ QV4::InternalClass *klass = engine->internalClasses[QV4::ExecutionEngine::Class_Object];
for (int j = 0; j < memberCount; ++j, ++member)
klass = klass->addMember(runtimeStrings[member->nameOffset]->identifier, member->isAccessor ? QV4::Attr_Accessor : QV4::Attr_Data);
@@ -733,7 +733,7 @@ static QByteArray ownLibraryChecksum()
if (checksumInitialized)
return libraryChecksum;
checksumInitialized = true;
-#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) && !defined(Q_OS_INTEGRITY)
+#if !defined(QT_NO_DYNAMIC_CAST) && QT_CONFIG(dlopen)
Dl_info libInfo;
if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) {
QFile library(QFile::decodeName(libInfo.dli_fname));
diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h
index fbd6ac8f99..5f46e90ec7 100644
--- a/src/qml/compiler/qv4instr_moth_p.h
+++ b/src/qml/compiler/qv4instr_moth_p.h
@@ -170,11 +170,7 @@ QT_BEGIN_NAMESPACE
#define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1)
-#ifdef MOTH_THREADED_INTERPRETER
-# define MOTH_INSTR_HEADER union { quint32 instructionType; void *code; };
-#else
-# define MOTH_INSTR_HEADER quint32 instructionType;
-#endif
+#define MOTH_INSTR_HEADER quint32 instructionType;
#define MOTH_INSTR_ENUM(I, FMT) I,
#define MOTH_INSTR_SIZE(I, FMT) ((sizeof(QV4::Moth::Instr::instr_##FMT) + MOTH_INSTR_ALIGN_MASK) & ~MOTH_INSTR_ALIGN_MASK)
@@ -194,8 +190,8 @@ struct Param {
// Arg(outer): 4
// Local(outer): 5
// ...
- unsigned scope;
- unsigned index;
+ unsigned scope : 12;
+ unsigned index : 20;
bool isConstant() const { return !scope; }
bool isArgument() const { return scope >= 2 && !(scope &1); }
@@ -901,6 +897,8 @@ template<int N>
struct InstrMeta {
};
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_GCC("-Wuninitialized")
#define MOTH_INSTR_META_TEMPLATE(I, FMT) \
template<> struct InstrMeta<(int)Instr::I> { \
enum { Size = MOTH_INSTR_SIZE(I, FMT) }; \
@@ -914,6 +912,7 @@ struct InstrMeta {
};
FOR_EACH_MOTH_INSTR(MOTH_INSTR_META_TEMPLATE);
#undef MOTH_INSTR_META_TEMPLATE
+QT_WARNING_POP
template<int InstrType>
class InstrData : public InstrMeta<InstrType>::DataType
diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp
index aefb084971..fb805dce02 100644
--- a/src/qml/compiler/qv4isel_moth.cpp
+++ b/src/qml/compiler/qv4isel_moth.cpp
@@ -638,7 +638,7 @@ void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex,
void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target)
{
- if (useFastLookups) {
+ if (0 && useFastLookups) {
Instruction::LoadElementLookup load;
load.lookup = registerIndexedGetterLookup();
load.base = getParam(base);
@@ -657,7 +657,7 @@ void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr
void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase,
IR::Expr *targetIndex)
{
- if (useFastLookups) {
+ if (0 && useFastLookups) {
Instruction::StoreElementLookup store;
store.lookup = registerIndexedSetterLookup();
store.base = getParam(targetBase);
@@ -1436,29 +1436,6 @@ CompilationUnit::~CompilationUnit()
void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine)
{
-#ifdef MOTH_THREADED_INTERPRETER
- // link byte code against addresses of instructions
- for (int i = 0; i < codeRefs.count(); ++i) {
- QByteArray &codeRef = codeRefs[i];
- char *code = codeRef.data();
- int index = 0;
- while (index < codeRef.size()) {
- Instr *genericInstr = reinterpret_cast<Instr *>(code + index);
-
- switch (genericInstr->common.instructionType) {
-#define LINK_INSTRUCTION(InstructionType, Member) \
- case Instr::InstructionType: \
- genericInstr->common.code = VME::instructionJumpTable()[static_cast<int>(genericInstr->common.instructionType)]; \
- index += InstrMeta<(int)Instr::InstructionType>::Size; \
- break;
-
- FOR_EACH_MOTH_INSTR(LINK_INSTRUCTION)
-
- }
- }
- }
-#endif
-
runtimeFunctions.resize(data->functionTableSize);
runtimeFunctions.fill(0);
for (int i = 0 ;i < runtimeFunctions.size(); ++i) {
@@ -1516,17 +1493,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit
QByteArray padding;
-#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP)
- // Map from instruction label back to instruction type. Only needed when persisting
- // already linked compilation units;
- QHash<void*, int> reverseInstructionMapping;
- if (engine) {
- void **instructions = VME::instructionJumpTable();
- for (int i = 0; i < Instr::LastInstruction; ++i)
- reverseInstructionMapping.insert(instructions[i], i);
- }
-#endif
-
for (int i = 0; i < codeRefs.size(); ++i) {
const CompiledData::Function *compiledFunction = unit->functionAt(i);
@@ -1545,27 +1511,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit
QByteArray code = codeRefs.at(i);
-#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP)
- if (!reverseInstructionMapping.isEmpty()) {
- char *codePtr = code.data(); // detaches
- int index = 0;
- while (index < code.size()) {
- Instr *genericInstr = reinterpret_cast<Instr *>(codePtr + index);
-
- genericInstr->common.instructionType = reverseInstructionMapping.value(genericInstr->common.code);
-
- switch (genericInstr->common.instructionType) {
- #define REVERSE_INSTRUCTION(InstructionType, Member) \
- case Instr::InstructionType: \
- index += InstrMeta<(int)Instr::InstructionType>::Size; \
- break;
-
- FOR_EACH_MOTH_INSTR(REVERSE_INSTRUCTION)
- }
- }
- }
-#endif
-
written = device->write(code.constData(), compiledFunction->codeSize);
if (written != qint64(compiledFunction->codeSize)) {
*errorString = device->errorString();
diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h
index b534eaa54f..190fb1bd5b 100644
--- a/src/qml/compiler/qv4jsir_p.h
+++ b/src/qml/compiler/qv4jsir_p.h
@@ -1113,7 +1113,11 @@ public:
return false;
}
- unsigned newTemp();
+ enum TempForWhom {
+ NewTempForCodegen,
+ NewTempForOptimizer
+ };
+ unsigned newTemp(TempForWhom tfw = NewTempForCodegen);
Temp *TEMP(unsigned kind);
ArgLocal *ARG(unsigned index, unsigned scope);
@@ -1278,6 +1282,7 @@ struct Function {
Module *module;
QQmlJS::MemoryPool *pool;
const QString *name;
+ int currentTemp = 0;
int tempCount;
int maxNumberOfArguments;
QSet<QString> strings;
@@ -1511,10 +1516,17 @@ protected:
BasicBlock *currentBB;
};
-inline unsigned BasicBlock::newTemp()
+inline unsigned BasicBlock::newTemp(TempForWhom tfw)
{
Q_ASSERT(!isRemoved());
- return function->tempCount++;
+
+ if (tfw == NewTempForOptimizer)
+ return function->tempCount++;
+
+ int t = function->currentTemp++;
+ if (function->tempCount < function->currentTemp)
+ function->tempCount = function->currentTemp;
+ return t;
}
inline Temp *BasicBlock::TEMP(unsigned index)
diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp
index 731c6ad38f..8cf5fac760 100644
--- a/src/qml/compiler/qv4ssa.cpp
+++ b/src/qml/compiler/qv4ssa.cpp
@@ -2770,7 +2770,7 @@ public:
} else if (Const *c = (*conversion.expr)->asConst()) {
convertConst(c, conversion.targetType);
} else if (ArgLocal *al = (*conversion.expr)->asArgLocal()) {
- Temp *target = bb->TEMP(bb->newTemp());
+ Temp *target = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer));
target->type = conversion.targetType;
Expr *convert = bb->CONVERT(al, conversion.targetType);
Move *convCall = f->NewStmt<Move>();
@@ -2791,7 +2791,7 @@ public:
*conversion.expr = source;
} else if (Temp *t = (*conversion.expr)->asTemp()) {
- Temp *target = bb->TEMP(bb->newTemp());
+ Temp *target = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer));
target->type = conversion.targetType;
Expr *convert = bb->CONVERT(t, conversion.targetType);
Move *convCall = f->NewStmt<Move>();
@@ -2820,7 +2820,7 @@ public:
// to:
// double{%3} = double{-double{%1}};
// int32{%2} = int32{convert(double{%3})};
- Temp *tmp = bb->TEMP(bb->newTemp());
+ Temp *tmp = bb->TEMP(bb->newTemp(BasicBlock::NewTempForOptimizer));
tmp->type = u->type;
Move *extraMove = f->NewStmt<Move>();
worklist.registerNewStatement(extraMove);
@@ -5417,7 +5417,11 @@ void Optimizer::run(QQmlEnginePrivate *qmlEngine, bool doTypeInference, bool pee
showMeTheCode(function, "After loop detection");
// cfg2dot(function, loopDetection.allLoops());
- if (peelLoops) {
+ // ### disable loop peeling for now. It doesn't give any measurable performance
+ // improvements at this time, but significantly increases the size of the
+ // JIT generated code
+ Q_UNUSED(peelLoops);
+ if (0 && peelLoops) {
QVector<LoopDetection::LoopInfo *> innerLoops = loopDetection.innermostLoops();
LoopPeeling(df).run(innerLoops);
diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h
index 24257e99e9..b9d8ae0a6c 100644
--- a/src/qml/compiler/qv4ssa_p.h
+++ b/src/qml/compiler/qv4ssa_p.h
@@ -265,7 +265,7 @@ private:
QHash<BasicBlock *, BasicBlock *> startEndLoops;
};
-class Q_AUTOTEST_EXPORT MoveMapping
+class Q_QML_AUTOTEST_EXPORT MoveMapping
{
#ifdef V4_AUTOTEST
public:
diff --git a/src/qml/configure.json b/src/qml/configure.json
index 2c4887365f..257bedecbc 100644
--- a/src/qml/configure.json
+++ b/src/qml/configure.json
@@ -15,17 +15,20 @@
"features": {
"qml-interpreter": {
"label": "QML interpreter",
- "purpose": "Support for the QML interpreter",
+ "purpose": "Provides the QML interpreter.",
+ "section": "QML",
"output": [ "privateFeature" ]
},
"qml-network": {
"label": "QML network support",
- "purpose": "Provides network transparency for QML",
+ "purpose": "Provides network transparency.",
+ "section": "QML",
"output": [ "publicFeature" ]
},
"qml-profiler": {
"label": "Command line QML Profiler",
- "purpose": "The QML Profiler retrieves QML tracing data from an application.",
+ "purpose": "Supports retrieving QML tracing data from an application.",
+ "section": "QML",
"condition": [
"features.commandlineparser",
"features.localserver",
diff --git a/src/qml/doc/images/cppintegration-ex.png b/src/qml/doc/images/cppintegration-ex.png
new file mode 100644
index 0000000000..0b476ccb93
--- /dev/null
+++ b/src/qml/doc/images/cppintegration-ex.png
Binary files differ
diff --git a/src/qml/doc/snippets/code/backend/backend.cpp b/src/qml/doc/snippets/code/backend/backend.cpp
new file mode 100644
index 0000000000..4a7ee89cec
--- /dev/null
+++ b/src/qml/doc/snippets/code/backend/backend.cpp
@@ -0,0 +1,70 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "backend.h"
+
+BackEnd::BackEnd(QObject *parent) :
+ QObject(parent)
+{
+}
+
+QString BackEnd::userName()
+{
+ return m_userName;
+}
+
+void BackEnd::setUserName(const QString &userName)
+{
+ if (userName == m_userName)
+ return;
+
+ m_userName = userName;
+ emit userNameChanged();
+}
diff --git a/src/qml/doc/snippets/code/backend/backend.h b/src/qml/doc/snippets/code/backend/backend.h
new file mode 100644
index 0000000000..91bb766e1f
--- /dev/null
+++ b/src/qml/doc/snippets/code/backend/backend.h
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef BACKEND_H
+#define BACKEND_H
+
+#include <QObject>
+#include <QString>
+
+class BackEnd : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QString userName READ userName WRITE setUserName NOTIFY userNameChanged)
+
+public:
+ explicit BackEnd(QObject *parent = nullptr);
+
+ QString userName();
+ void setUserName(const QString &userName);
+
+signals:
+ void userNameChanged();
+
+private:
+ QString m_userName;
+};
+
+#endif // BACKEND_H
diff --git a/src/qml/doc/snippets/code/backend/main.cpp b/src/qml/doc/snippets/code/backend/main.cpp
new file mode 100644
index 0000000000..d7a1bcbd4f
--- /dev/null
+++ b/src/qml/doc/snippets/code/backend/main.cpp
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QGuiApplication>
+#include <QQmlApplicationEngine>
+
+#include "backend.h"
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ qmlRegisterType<BackEnd>("io.qt.examples.backend", 1, 0, "BackEnd");
+
+ QQmlApplicationEngine engine;
+ engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
+
+ return app.exec();
+}
diff --git a/src/qml/doc/snippets/code/backend/main.qml b/src/qml/doc/snippets/code/backend/main.qml
new file mode 100644
index 0000000000..3720da8412
--- /dev/null
+++ b/src/qml/doc/snippets/code/backend/main.qml
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** BSD License Usage
+** Alternatively, you may use this file under the terms of the BSD license
+** as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.6
+import QtQuick.Controls 2.0
+//![import]
+import io.qt.examples.backend 1.0
+//![import]
+
+ApplicationWindow {
+ id: root
+ width: 300
+ height: 480
+ visible: true
+
+//![backend]
+ BackEnd {
+ id: backend
+ }
+//![backend]
+
+//![username_input]
+ TextField {
+ text: backend.userName
+ placeholderText: qsTr("User name")
+ anchors.centerIn: parent
+
+ onTextChanged: backend.userName = text
+ }
+//![username_input]
+}
+
diff --git a/src/qml/doc/src/cppintegration/topic.qdoc b/src/qml/doc/src/cppintegration/topic.qdoc
index 1aa3bb6ab5..22115395b1 100644
--- a/src/qml/doc/src/cppintegration/topic.qdoc
+++ b/src/qml/doc/src/cppintegration/topic.qdoc
@@ -27,7 +27,68 @@
/*!
\page qtqml-cppintegration-topic.html
\title Integrating QML and C++
-\brief Description of how to integrate QML and C++ code
+\brief Provides instruction to integrate QML and C++
+
+QML applications often need to handle more advanced and performance-intensive
+tasks in C++. The most common and quickest way to do this is to expose the C++
+class to the QML runtime, provided the C++ implementation is derived from
+QObject. Assuming that you have Qt 5.7 or later installed, the following
+step-by-step instructions guide you through the process of using the C++ class,
+BackEnd, in a QML application:
+
+\list 1
+
+\li Create a new project using the "Qt Quick Application" template in Qt Creator
+
+\note Uncheck the \uicontrol {With ui.qml file} option in the
+\uicontrol {Define Project Details} section of \uicontrol {New Project Wizard}.
+
+\li Add a new C++ class called \c BackEnd to the project and replace its header
+file contents with:
+
+\quotefile code/backend/backend.h
+
+The \c Q_PROPERTY macro declares a property that could be accessed from QML.
+
+\li Replace its C++ file contents with:
+
+\quotefile code/backend/backend.cpp
+
+The \c setUserName function emits the \c userNameChanged signal every time
+\c m_userName value changes. The signal can be handled from QML using the
+\c onUserNameChanged handler.
+
+\li Include \c "backend.h" in \c main.cpp and register the class as a QML type
+under a import URL as shown below:
+
+\quotefile code/backend/main.cpp
+
+The BackEnd class is registered as a type, which is accessible from QML by
+importing the URL, "\c{io.qt.examples.backend 1.0}".
+
+\li Replace the contents of \c main.qml with the following code:
+
+\quotefile code/backend/main.qml
+
+The \c BackEnd instance lets you access the \c userName property, which
+is updated when the TextField's \c text property changes.
+
+\endlist
+
+Now the application can be run.
+
+\borderedimage cppintegration-ex.png
+\caption Application running on Ubuntu
+
+Qt offers several methods to integrate C++ with QML, and the method discussed
+in this tutorial is just one of them. For more details about these methods,
+refer to \l{Overview - QML and C++ Integration}.
+*/
+
+/*!
+\page qtqml-cppintegration-overview.html
+\title Overview - QML and C++ Integration
+\brief Highlights important points about integrating C++ with QML.
QML is designed to be easily extensible through C++ code. The classes in the \l {Qt QML} module
enable QML objects to be loaded and manipulated from C++, and the nature of QML engine's
diff --git a/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc b/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc
index f28ff5082a..99426d984d 100644
--- a/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc
+++ b/src/qml/doc/src/qmllanguageref/syntax/propertybinding.qdoc
@@ -179,6 +179,26 @@ Rectangle {
Now, after the space key is pressed, the rectangle's height will continue
auto-updating to always be three times its width.
+\section3 Debugging overwriting of bindings
+
+A common cause of bugs in QML applications is accidentally overwriting bindings
+with static values from JavaScript statements. To help developers track down
+problems of this kind, the QML engine is able to emit messages whenever a
+binding is lost due to imperative assignments.
+
+In order to generate such messages, you need to enable the informational output
+for the \c{qt.qml.binding.removal} logging category, for instance by calling:
+
+\code
+QLoggingCategory::setFilterRules(QStringLiteral("qt.qml.binding.removal.info=true"));
+\endcode
+
+Please refer to the QLoggingCategory documentation for more information about
+enabling output from logging categories.
+
+Note that is perfectly reasonable in some circumstances to overwrite bindings.
+Any message generated by the QML engine should be treated as a diagnostic aid,
+and not necessarily as evidence of a problem without further investigation.
\section2 Using \c this with Property Binding
diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp
index c2853a39d2..7784eb364e 100644
--- a/src/qml/jit/qv4isel_masm.cpp
+++ b/src/qml/jit/qv4isel_masm.cpp
@@ -621,7 +621,7 @@ void InstructionSelection<JITAssembler>::setQObjectProperty(IR::Expr *source, IR
template <typename JITAssembler>
void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target)
{
- if (useFastLookups) {
+ if (0 && useFastLookups) {
uint lookup = registerIndexedGetterLookup();
generateLookupCall(target, lookup, offsetof(QV4::Lookup, indexedGetter),
JITTargetPlatform::EngineRegister,
@@ -637,7 +637,7 @@ void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *in
template <typename JITAssembler>
void InstructionSelection<JITAssembler>::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex)
{
- if (useFastLookups) {
+ if (0 && useFastLookups) {
uint lookup = registerIndexedSetterLookup();
generateLookupCall(JITAssembler::Void, lookup, offsetof(QV4::Lookup, indexedSetter),
JITTargetPlatform::EngineRegister,
@@ -1639,14 +1639,20 @@ QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory<JITAssembler>::createU
return result;
}
+#endif // ENABLE(ASSEMBLER)
+
QT_BEGIN_NAMESPACE
namespace QV4 { namespace JIT {
+#if ENABLE(ASSEMBLER)
template class Q_QML_EXPORT InstructionSelection<>;
template class Q_QML_EXPORT ISelFactory<>;
+#endif
+
#if defined(V4_BOOTSTRAP)
Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture)
{
+#if ENABLE(ASSEMBLER)
using ARMv7CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>;
using ARM64CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>;
@@ -1667,6 +1673,7 @@ Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &arch
#endif
if (!hostArch.isEmpty() && architecture == hostArch)
return new ISelFactory<>;
+#endif // ENABLE(ASSEMBLER)
return nullptr;
}
@@ -1675,4 +1682,3 @@ Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &arch
} }
QT_END_NAMESPACE
-#endif // ENABLE(ASSEMBLER)
diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp
index 896be07ed5..78546e1509 100644
--- a/src/qml/jit/qv4unop.cpp
+++ b/src/qml/jit/qv4unop.cpp
@@ -83,6 +83,15 @@ template <typename JITAssembler>
void Unop<JITAssembler>::generateUMinus(IR::Expr *source, IR::Expr *target)
{
IR::Temp *targetTemp = target->asTemp();
+
+ if (IR::Const *c = source->asConst()) {
+ if (c->value == 0 && source->type == IR::SInt32Type) {
+ // special case: minus integer 0 is 0, which is not what JS expects it to be, so always
+ // do a runtime call
+ generateRuntimeCall(_as, target, uMinus, PointerToValue(source));
+ return;
+ }
+ }
if (source->type == IR::SInt32Type) {
typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister;
if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
diff --git a/src/qml/jsapi/qjsvalue.cpp b/src/qml/jsapi/qjsvalue.cpp
index bab2e633a7..3a3cf46ddb 100644
--- a/src/qml/jsapi/qjsvalue.cpp
+++ b/src/qml/jsapi/qjsvalue.cpp
@@ -1019,7 +1019,7 @@ QJSValue QJSValue::property(const QString& name) const
if (idx < UINT_MAX)
return property(idx);
- s->makeIdentifier(engine);
+ s->makeIdentifier();
QV4::ScopedValue result(scope, o->get(s));
if (engine->hasException)
result = engine->catchException();
@@ -1090,7 +1090,7 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value)
return;
}
- s->makeIdentifier(scope.engine);
+ s->makeIdentifier();
QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value));
o->put(s, v);
if (engine->hasException)
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index 955cf585e4..9938f60aea 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -46,7 +46,9 @@ SOURCES += \
HEADERS += \
$$PWD/qv4global_p.h \
+ $$PWD/qv4alloca_p.h \
$$PWD/qv4engine_p.h \
+ $$PWD/qv4enginebase_p.h \
$$PWD/qv4context_p.h \
$$PWD/qv4math_p.h \
$$PWD/qv4persistent_p.h \
diff --git a/src/qml/jsruntime/qv4alloca_p.h b/src/qml/jsruntime/qv4alloca_p.h
index 2f486988c1..1e9f83a90e 100644
--- a/src/qml/jsruntime/qv4alloca_p.h
+++ b/src/qml/jsruntime/qv4alloca_p.h
@@ -51,15 +51,58 @@
// We mean it.
//
-#include <qglobal.h>
+#include <QtCore/private/qglobal_p.h>
-#if defined(Q_OS_WIN)
+#if QT_CONFIG(alloca_h)
+# include <alloca.h>
+#elif QT_CONFIG(alloca_malloc_h)
# include <malloc.h>
-# ifndef __GNUC__
+// This does not matter unless compiling in strict standard mode.
+# ifdef Q_CC_MSVC
# define alloca _alloca
# endif
-#elif !defined(Q_OS_BSD4) || defined(Q_OS_DARWIN)
-# include <alloca.h>
+#else
+# include <stdlib.h>
+#endif
+
+// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing
+// the occurrences of alloca() in case it's not supported.
+// Q_ALLOCA_DECLARE and Q_ALLOCA_ASSIGN macros separate
+// memory allocation from the declaration and RAII.
+#define Q_ALLOCA_VAR(type, name, size) \
+ Q_ALLOCA_DECLARE(type, name); \
+ Q_ALLOCA_ASSIGN(type, name, size)
+
+#if QT_CONFIG(alloca)
+
+#define Q_ALLOCA_DECLARE(type, name) \
+ type *name = 0
+
+#define Q_ALLOCA_ASSIGN(type, name, size) \
+ name = static_cast<type*>(alloca(size))
+
+#else
+QT_BEGIN_NAMESPACE
+class Qt_AllocaWrapper
+{
+public:
+ Qt_AllocaWrapper() { m_data = 0; }
+ ~Qt_AllocaWrapper() { free(m_data); }
+ void *data() { return m_data; }
+ void allocate(int size) { m_data = malloc(size); }
+private:
+ void *m_data;
+};
+QT_END_NAMESPACE
+
+#define Q_ALLOCA_DECLARE(type, name) \
+ Qt_AllocaWrapper _qt_alloca_##name; \
+ type *name = nullptr
+
+#define Q_ALLOCA_ASSIGN(type, name, size) \
+ _qt_alloca_##name.allocate(size); \
+ name = static_cast<type*>(_qt_alloca_##name.data())
+
#endif
#endif
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 318db4f904..0905c2828a 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -48,7 +48,7 @@ DEFINE_OBJECT_VTABLE(ArgumentsObject);
void Heap::ArgumentsObject::init(QV4::CallContext *context)
{
- ExecutionEngine *v4 = context->d()->engine;
+ ExecutionEngine *v4 = internalClass->engine;
Object::init();
fullyCreated = false;
@@ -59,8 +59,8 @@ void Heap::ArgumentsObject::init(QV4::CallContext *context)
Scoped<QV4::ArgumentsObject> args(scope, this);
if (context->d()->strictMode) {
- Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee()));
- Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(context->d()->engine->id_caller()));
+ Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee()));
+ Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(v4->id_caller()));
args->setProperty(CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
args->setProperty(CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
args->setProperty(CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
@@ -70,10 +70,10 @@ void Heap::ArgumentsObject::init(QV4::CallContext *context)
args->arrayPut(0, context->args(), context->argc());
args->d()->fullyCreated = true;
} else {
- Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee()));
+ Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee()));
args->setProperty(CalleePropertyIndex, context->d()->function);
}
- Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(context->d()->engine->id_length()));
+ Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length()));
args->setProperty(LengthPropertyIndex, Primitive::fromInt32(context->d()->callData->argc));
}
@@ -82,18 +82,19 @@ void ArgumentsObject::fullyCreate()
if (fullyCreated())
return;
+ Scope scope(engine());
+
uint argCount = context()->callData->argc;
uint numAccessors = qMin(context()->formalParameterCount(), argCount);
ArrayData::realloc(this, Heap::ArrayData::Sparse, argCount, true);
- context()->engine->requireArgumentsAccessors(numAccessors);
+ scope.engine->requireArgumentsAccessors(numAccessors);
- Scope scope(engine());
Scoped<MemberData> md(scope, d()->mappedArguments);
if (numAccessors) {
- d()->mappedArguments.set(scope.engine, md->allocate(engine(), numAccessors));
+ d()->mappedArguments.set(scope.engine, md->allocate(scope.engine, numAccessors));
for (uint i = 0; i < numAccessors; ++i) {
d()->mappedArguments->values.set(scope.engine, i, context()->callData->args[i]);
- arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor);
+ arraySet(i, scope.engine->argumentsAccessors + i, Attr_Accessor);
}
}
arrayPut(numAccessors, context()->callData->args + numAccessors, argCount - numAccessors);
@@ -114,7 +115,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con
bool isMapped = false;
if (arrayData() && index < numAccessors &&
arrayData()->attributes(index).isAccessor() &&
- arrayData()->get(index) == context()->engine->argumentsAccessors[index].getter()->asReturnedValue())
+ arrayData()->get(index) == scope.engine->argumentsAccessors[index].getter()->asReturnedValue())
isMapped = true;
if (isMapped) {
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 4a619858b4..df9884d84a 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -51,6 +51,8 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON
const QV4::VTable QV4::ArrayData::static_vtbl = {
0,
0,
+ 0,
+ 0,
QV4::ArrayData::IsExecutionContext,
QV4::ArrayData::IsString,
QV4::ArrayData::IsObject,
diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h
index c2c81e886b..e1de2e82e6 100644
--- a/src/qml/jsruntime/qv4arraydata_p.h
+++ b/src/qml/jsruntime/qv4arraydata_p.h
@@ -107,7 +107,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) {
Heap::ArrayData *arrayData;
uint index;
- void set(ExecutionEngine *e, Value newVal) {
+ void set(EngineBase *e, Value newVal) {
arrayData->values.set(e, index, newVal);
}
const Value *operator->() const { return &arrayData->values[index]; }
@@ -123,7 +123,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) {
return vtable()->get(this, i);
}
inline bool getProperty(uint index, Property *p, PropertyAttributes *attrs);
- inline void setProperty(ExecutionEngine *e, uint index, const Property *p);
+ inline void setProperty(EngineBase *e, uint index, const Property *p);
inline Index getValueOrSetter(uint index, PropertyAttributes *attrs);
inline PropertyAttributes attributes(uint i) const;
@@ -135,7 +135,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) {
return vtable()->length(this);
}
- void setArrayData(ExecutionEngine *e, uint index, Value newVal) {
+ void setArrayData(EngineBase *e, uint index, Value newVal) {
values.set(e, index, newVal);
}
@@ -146,7 +146,7 @@ V4_ASSERT_IS_TRIVIAL(ArrayData)
struct SimpleArrayData : public ArrayData {
uint mappedIndex(uint index) const { return (index + offset) % values.alloc; }
const Value &data(uint index) const { return values[mappedIndex(index)]; }
- void setData(ExecutionEngine *e, uint index, Value newVal) {
+ void setData(EngineBase *e, uint index, Value newVal) {
values.set(e, mappedIndex(index), newVal);
}
@@ -197,7 +197,7 @@ struct Q_QML_EXPORT ArrayData : public Managed
PropertyAttributes *attrs() const { return d()->attrs; }
void setAttrs(PropertyAttributes *a) { d()->attrs = a; }
const Value *arrayData() const { return d()->values.data(); }
- void setArrayData(ExecutionEngine *e, uint index, Value newVal) {
+ void setArrayData(EngineBase *e, uint index, Value newVal) {
d()->setArrayData(e, index, newVal);
}
@@ -234,6 +234,7 @@ struct Q_QML_EXPORT ArrayData : public Managed
struct Q_QML_EXPORT SimpleArrayData : public ArrayData
{
V4_ARRAYDATA(SimpleArrayData)
+ V4_INTERNALCLASS(SimpleArrayData)
uint mappedIndex(uint index) const { return d()->mappedIndex(index); }
Value data(uint index) const { return d()->data(index); }
@@ -257,6 +258,7 @@ struct Q_QML_EXPORT SimpleArrayData : public ArrayData
struct Q_QML_EXPORT SparseArrayData : public ArrayData
{
V4_ARRAYDATA(SparseArrayData)
+ V4_INTERNALCLASS(SparseArrayData)
V4_NEEDS_DESTROY
ReturnedValue &freeList() { return d()->freeList; }
@@ -308,7 +310,7 @@ bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs)
return true;
}
-void ArrayData::setProperty(QV4::ExecutionEngine *e, uint index, const Property *p)
+void ArrayData::setProperty(QV4::EngineBase *e, uint index, const Property *p)
{
uint mapped = mappedIndex(index);
Q_ASSERT(mapped != UINT_MAX);
diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp
index 601066110f..c8e9ebb2dd 100644
--- a/src/qml/jsruntime/qv4booleanobject.cpp
+++ b/src/qml/jsruntime/qv4booleanobject.cpp
@@ -85,7 +85,7 @@ void BooleanPrototype::method_toString(const BuiltinFunction *, Scope &scope, Ca
result = thisObject->value();
}
- scope.result = scope.engine->newString(QLatin1String(result ? "true" : "false"));
+ scope.result = result ? scope.engine->id_true() : scope.engine->id_false();
}
void BooleanPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData)
diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h
index 9c8b1d67f1..ca2cf7d17a 100644
--- a/src/qml/jsruntime/qv4booleanobject_p.h
+++ b/src/qml/jsruntime/qv4booleanobject_p.h
@@ -76,6 +76,7 @@ struct BooleanCtor: FunctionObject
struct BooleanPrototype: BooleanObject
{
+ V4_PROTOTYPE(objectPrototype)
void init(ExecutionEngine *engine, Object *ctor);
static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 02d3af619e..6807c835b0 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -65,13 +65,14 @@ Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData
uint localsAndFormals = function->compiledFunction->nLocals + sizeof(CallData)/sizeof(Value) - 1 + qMax(static_cast<uint>(callData->argc), function->nFormals);
size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals);
- Heap::CallContext *c = d()->engine->memoryManager->allocManaged<CallContext>(requiredMemory);
- c->init(d()->engine, Heap::ExecutionContext::Type_CallContext);
+ ExecutionEngine *v4 = engine();
+ Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory);
+ c->init(Heap::ExecutionContext::Type_CallContext);
c->v4Function = function;
c->strictMode = function->isStrict();
- c->outer.set(d()->engine, this->d());
+ c->outer.set(v4, this->d());
c->compilationUnit = function->compilationUnit;
c->lookups = function->compilationUnit->runtimeLookups;
@@ -99,14 +100,14 @@ Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData
Heap::WithContext *ExecutionContext::newWithContext(Heap::Object *with)
{
- return d()->engine->memoryManager->alloc<WithContext>(d(), with);
+ return engine()->memoryManager->alloc<WithContext>(d(), with);
}
Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue)
{
Scope scope(this);
ScopedValue e(scope, exceptionValue);
- return d()->engine->memoryManager->alloc<CatchContext>(d(), exceptionVarName, e);
+ return engine()->memoryManager->alloc<CatchContext>(d(), exceptionVarName, e);
}
void ExecutionContext::createMutableBinding(String *name, bool deletable)
@@ -156,35 +157,35 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
void Heap::GlobalContext::init(ExecutionEngine *eng)
{
- Heap::ExecutionContext::init(eng, Heap::ExecutionContext::Type_GlobalContext);
+ Heap::ExecutionContext::init(Heap::ExecutionContext::Type_GlobalContext);
global.set(eng, eng->globalObject->d());
}
void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName,
const Value &exceptionValue)
{
- Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_CatchContext);
- outer.set(engine, outerContext);
+ Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext);
+ outer.set(internalClass->engine, outerContext);
strictMode = outer->strictMode;
callData = outer->callData;
lookups = outer->lookups;
constantTable = outer->constantTable;
compilationUnit = outer->compilationUnit;
- this->exceptionVarName.set(engine, exceptionVarName);
- this->exceptionValue.set(engine, exceptionValue);
+ this->exceptionVarName.set(internalClass->engine, exceptionVarName);
+ this->exceptionValue.set(internalClass->engine, exceptionValue);
}
void Heap::WithContext::init(ExecutionContext *outerContext, Object *with)
{
- Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_WithContext);
- outer.set(engine, outerContext);
+ Heap::ExecutionContext::init(Heap::ExecutionContext::Type_WithContext);
+ outer.set(internalClass->engine, outerContext);
callData = outer->callData;
lookups = outer->lookups;
constantTable = outer->constantTable;
compilationUnit = outer->compilationUnit;
- withObject.set(engine, with);
+ withObject.set(internalClass->engine, with);
}
Identifier * const *SimpleCallContext::formals() const
@@ -211,6 +212,9 @@ unsigned int SimpleCallContext::variableCount() const
bool ExecutionContext::deleteProperty(String *name)
{
+ name->makeIdentifier();
+ Identifier *id = name->identifier();
+
Scope scope(this);
ScopedContext ctx(scope, this);
for (; ctx; ctx = ctx->d()->outer) {
@@ -233,16 +237,14 @@ bool ExecutionContext::deleteProperty(String *name)
return global->deleteProperty(name);
break;
}
- case Heap::ExecutionContext::Type_CallContext: {
- Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d());
- uint index = c->v4Function->internalClass->find(name);
- if (index < UINT_MAX)
- // ### throw in strict mode?
- return false;
+ case Heap::ExecutionContext::Type_CallContext:
Q_FALLTHROUGH();
- }
case Heap::ExecutionContext::Type_SimpleCallContext: {
Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
+ uint index = c->v4Function->internalClass->find(id);
+ if (index < UINT_MAX)
+ // ### throw in strict mode?
+ return false;
ScopedObject qml(scope, c->activation);
if (qml && qml->hasProperty(name))
return qml->deleteProperty(name);
@@ -282,7 +284,7 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio
ExecutionContextSaver ctxSaver(scope);
- SimpleCallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(scope.engine);
+ SimpleCallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext();
ctx->strictMode = function->isStrict();
ctx->callData = callData;
@@ -306,6 +308,9 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio
void ExecutionContext::setProperty(String *name, const Value &value)
{
+ name->makeIdentifier();
+ Identifier *id = name->identifier();
+
Scope scope(this);
ScopedContext ctx(scope, this);
ScopedObject activation(scope);
@@ -337,7 +342,7 @@ void ExecutionContext::setProperty(String *name, const Value &value)
case Heap::ExecutionContext::Type_SimpleCallContext: {
Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
if (c->v4Function) {
- uint index = c->v4Function->internalClass->find(name);
+ uint index = c->v4Function->internalClass->find(id);
if (index < UINT_MAX) {
if (index < c->v4Function->nFormals) {
c->callData->args[c->v4Function->nFormals - index - 1] = value;
@@ -360,7 +365,7 @@ void ExecutionContext::setProperty(String *name, const Value &value)
}
if (activation) {
- uint member = activation->internalClass()->find(name);
+ uint member = activation->internalClass()->find(id);
if (member < UINT_MAX) {
activation->putValue(member, value);
return;
@@ -368,21 +373,21 @@ void ExecutionContext::setProperty(String *name, const Value &value)
}
}
- if (d()->strictMode || name->equals(d()->engine->id_this())) {
+ if (d()->strictMode || name->equals(engine()->id_this())) {
ScopedValue n(scope, name->asReturnedValue());
engine()->throwReferenceError(n);
return;
}
- d()->engine->globalObject->put(name, value);
+ engine()->globalObject->put(name, value);
}
ReturnedValue ExecutionContext::getProperty(String *name)
{
Scope scope(this);
ScopedValue v(scope);
- name->makeIdentifier(scope.engine);
+ name->makeIdentifier();
- if (name->equals(d()->engine->id_this()))
+ if (name->equals(engine()->id_this()))
return thisObject().asReturnedValue();
ScopedContext ctx(scope, this);
@@ -411,23 +416,21 @@ ReturnedValue ExecutionContext::getProperty(String *name)
return v->asReturnedValue();
break;
}
- case Heap::ExecutionContext::Type_CallContext: {
- Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d());
- uint index = c->v4Function->internalClass->find(name);
+ case Heap::ExecutionContext::Type_CallContext:
+ Q_FALLTHROUGH();
+ case Heap::ExecutionContext::Type_SimpleCallContext: {
+ Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
+
+ name->makeIdentifier();
+ Identifier *id = name->identifier();
+ uint index = c->v4Function->internalClass->find(id);
if (index < UINT_MAX) {
if (index < c->v4Function->nFormals)
return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue();
- Q_ASSERT(c->type == Heap::ExecutionContext::Type_CallContext);
- return c->locals[index - c->v4Function->nFormals].asReturnedValue();
- }
- if (c->v4Function->isNamedExpression()) {
- if (c->function && name->equals(ScopedString(scope, c->v4Function->name())))
- return c->function->asReturnedValue();
+ if (c->type == Heap::ExecutionContext::Type_CallContext)
+ return static_cast<Heap::CallContext *>(c)->locals[index - c->v4Function->nFormals].asReturnedValue();
}
- Q_FALLTHROUGH();
- }
- case Heap::ExecutionContext::Type_SimpleCallContext: {
- Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
+
ScopedObject activation(scope, c->activation);
if (activation) {
bool hasProperty = false;
@@ -435,6 +438,12 @@ ReturnedValue ExecutionContext::getProperty(String *name)
if (hasProperty)
return v->asReturnedValue();
}
+
+ if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) {
+ if (name->equals(ScopedString(scope, c->v4Function->name())))
+ if (auto func = static_cast<Heap::CallContext *>(c)->function)
+ return func->asReturnedValue();
+ }
break;
}
case Heap::ExecutionContext::Type_QmlContext: {
@@ -456,9 +465,9 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
Scope scope(this);
ScopedValue v(scope);
base->setM(0);
- name->makeIdentifier(scope.engine);
+ name->makeIdentifier();
- if (name->equals(d()->engine->id_this()))
+ if (name->equals(engine()->id_this()))
return thisObject().asReturnedValue();
ScopedContext ctx(scope, this);
@@ -488,22 +497,21 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
return v->asReturnedValue();
break;
}
- case Heap::ExecutionContext::Type_CallContext: {
- Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d());
- uint index = c->v4Function->internalClass->find(name);
+ case Heap::ExecutionContext::Type_CallContext:
+ Q_FALLTHROUGH();
+ case Heap::ExecutionContext::Type_SimpleCallContext: {
+ Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
+
+ name->makeIdentifier();
+ Identifier *id = name->identifier();
+ uint index = c->v4Function->internalClass->find(id);
if (index < UINT_MAX) {
if (index < c->v4Function->nFormals)
return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue();
- return c->locals[index - c->v4Function->nFormals].asReturnedValue();
- }
- if (c->v4Function->isNamedExpression()) {
- if (c->function && name->equals(ScopedString(scope, c->v4Function->name())))
- return c->function->asReturnedValue();
+ if (c->type == Heap::ExecutionContext::Type_CallContext)
+ return static_cast<Heap::CallContext *>(c)->locals[index - c->v4Function->nFormals].asReturnedValue();
}
- Q_FALLTHROUGH();
- }
- case Heap::ExecutionContext::Type_SimpleCallContext: {
- Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d());
+
ScopedObject activation(scope, c->activation);
if (activation) {
bool hasProperty = false;
@@ -511,6 +519,12 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
if (hasProperty)
return v->asReturnedValue();
}
+
+ if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) {
+ if (name->equals(ScopedString(scope, c->v4Function->name())))
+ if (auto func = static_cast<Heap::CallContext *>(c)->function)
+ return func->asReturnedValue();
+ }
break;
}
case Heap::ExecutionContext::Type_QmlContext: {
@@ -531,7 +545,7 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
Function *ExecutionContext::getFunction() const
{
- Scope scope(d()->engine);
+ Scope scope(engine());
ScopedContext it(scope, this->d());
for (; it; it = it->d()->outer) {
if (const SimpleCallContext *callCtx = it->asSimpleCallContext())
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index 625d9ed424..a854c324d0 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -104,7 +104,6 @@ struct QmlContext;
#define ExecutionContextMembers(class, Member) \
Member(class, NoMark, CallData *, callData) \
- Member(class, NoMark, ExecutionEngine *, engine) \
Member(class, Pointer, ExecutionContext *, outer) \
Member(class, NoMark, Lookup *, lookups) \
Member(class, NoMark, const QV4::Value *, constantTable) \
@@ -124,11 +123,10 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) {
Type_CallContext = 0x6
};
- void init(ExecutionEngine *engine, ContextType t)
+ void init(ContextType t)
{
Base::init();
- this->engine = engine;
type = t;
lineNumber = -1;
}
@@ -146,8 +144,7 @@ Q_STATIC_ASSERT(sizeof(ExecutionContext) == sizeof(Base) + sizeof(ExecutionConte
Q_STATIC_ASSERT(std::is_standard_layout<ExecutionContextData>::value);
Q_STATIC_ASSERT(offsetof(ExecutionContextData, callData) == 0);
-Q_STATIC_ASSERT(offsetof(ExecutionContextData, engine) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE);
-Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, engine) + QT_POINTER_SIZE);
+Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE);
Q_STATIC_ASSERT(offsetof(ExecutionContextData, lookups) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE);
Q_STATIC_ASSERT(offsetof(ExecutionContextData, constantTable) == offsetof(ExecutionContextData, lookups) + QT_POINTER_SIZE);
Q_STATIC_ASSERT(offsetof(ExecutionContextData, compilationUnit) == offsetof(ExecutionContextData, constantTable) + QT_POINTER_SIZE);
@@ -160,9 +157,9 @@ Q_STATIC_ASSERT(offsetof(ExecutionContextData, lineNumber) == offsetof(Execution
DECLARE_HEAP_OBJECT(SimpleCallContext, ExecutionContext) {
DECLARE_MARK_TABLE(SimpleCallContext);
- void init(ExecutionEngine *engine, ContextType t = Type_SimpleCallContext)
+ void init(ContextType t = Type_SimpleCallContext)
{
- ExecutionContext::init(engine, t);
+ ExecutionContext::init(t);
}
inline unsigned int formalParameterCount() const;
@@ -240,8 +237,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
V4_MANAGED(ExecutionContext, Managed)
Q_MANAGED_TYPE(ExecutionContext)
-
- ExecutionEngine *engine() const { return d()->engine; }
+ V4_INTERNALCLASS(ExecutionContext)
Heap::CallContext *newCallContext(Function *f, CallData *callData);
Heap::WithContext *newWithContext(Heap::Object *with);
@@ -281,6 +277,7 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
struct Q_QML_EXPORT SimpleCallContext : public ExecutionContext
{
V4_MANAGED(SimpleCallContext, ExecutionContext)
+ V4_INTERNALCLASS(SimpleCallContext)
// formals are in reverse order
Identifier * const *formals() const;
diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h
index b32b2c3f66..b0373884dd 100644
--- a/src/qml/jsruntime/qv4dateobject_p.h
+++ b/src/qml/jsruntime/qv4dateobject_p.h
@@ -114,6 +114,8 @@ struct DateCtor: FunctionObject
struct DatePrototype: Object
{
+ V4_PROTOTYPE(objectPrototype)
+
void init(ExecutionEngine *engine, Object *ctor);
static double getThisDate(Scope &scope, CallData *callData);
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 6465b15d5a..019936767a 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -109,9 +109,9 @@ using namespace QV4;
static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1);
-static ReturnedValue throwTypeError(CallContext *ctx)
+void throwTypeError(const BuiltinFunction *, Scope &scope, CallData *)
{
- return ctx->engine()->throwTypeError();
+ scope.result = scope.engine->throwTypeError();
}
@@ -130,10 +130,8 @@ QQmlEngine *ExecutionEngine::qmlEngine() const
qint32 ExecutionEngine::maxCallDepth = -1;
ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
- : callDepth(0)
- , executableAllocator(new QV4::ExecutableAllocator)
+ : executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
- , currentContext(0)
, bumperPointerAllocator(new WTF::BumpPointerAllocator)
, jsStack(new WTF::PageAllocation)
, gcStack(new WTF::PageAllocation)
@@ -219,7 +217,13 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
classPool = new InternalClassPool;
- emptyClass = new (classPool) InternalClass(this);
+ internalClasses[Class_Empty] = new (classPool) InternalClass(this);
+ internalClasses[Class_String] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::String::staticVTable());
+ internalClasses[Class_MemberData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::MemberData::staticVTable());
+ internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
+ internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable());
+ internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable());
+ internalClasses[Class_SimpleCallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
jsStrings[String_Empty] = newIdentifier(QString());
jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined"));
@@ -258,90 +262,113 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory)
jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer"));
jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex"));
- jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(emptyClass);
+ InternalClass *ic = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable());
+ jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic);
+ internalClasses[Class_Object] = ic->changePrototype(objectPrototype()->d());
- arrayClass = emptyClass->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable);
- jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(arrayClass, objectPrototype());
+ ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype());
+ Q_ASSERT(ic->prototype);
+ ic = ic->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable);
+ Q_ASSERT(ic->prototype);
+ jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic, objectPrototype());
+ internalClasses[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d());
jsObjects[PropertyListProto] = memoryManager->allocObject<PropertyListPrototype>();
- InternalClass *argsClass = emptyClass->addMember(id_length(), Attr_NotEnumerable);
- argumentsObjectClass = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable);
- strictArgumentsObjectClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
- strictArgumentsObjectClass = strictArgumentsObjectClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
+ InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
+ argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable);
+ internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable);
+ argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
+ internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
*static_cast<Value *>(globalObject) = newObject();
Q_ASSERT(globalObject->d()->vtable());
initRootContext();
- stringClass = emptyClass->addMember(id_length(), Attr_ReadOnly);
- Q_ASSERT(stringClass->find(id_length()) == Heap::StringObject::LengthPropertyIndex);
- jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(stringClass, objectPrototype());
- jsObjects[NumberProto] = memoryManager->allocObject<NumberPrototype>(emptyClass, objectPrototype());
- jsObjects[BooleanProto] = memoryManager->allocObject<BooleanPrototype>(emptyClass, objectPrototype());
- jsObjects[DateProto] = memoryManager->allocObject<DatePrototype>(emptyClass, objectPrototype());
+ ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
+ ic = ic->addMember(id_length(), Attr_ReadOnly);
+ jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic);
+ internalClasses[Class_StringObject] = ic->changePrototype(stringPrototype()->d());
+ Q_ASSERT(internalClasses[EngineBase::Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex);
+
+ jsObjects[NumberProto] = memoryManager->allocObject<NumberPrototype>();
+ jsObjects[BooleanProto] = memoryManager->allocObject<BooleanPrototype>();
+ jsObjects[DateProto] = memoryManager->allocObject<DatePrototype>();
uint index;
- InternalClass *functionProtoClass = emptyClass->addMember(id_prototype(), Attr_NotEnumerable, &index);
+ ic = newInternalClass(QV4::FunctionPrototype::staticVTable(), objectPrototype());
+ ic = ic->addMember(id_prototype(), Attr_NotEnumerable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_Prototype);
- jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(functionProtoClass, objectPrototype());
- functionClass = emptyClass->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
+ jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic, objectPrototype());
+ ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype());
+ ic = ic->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_Prototype);
- scriptFunctionClass = functionClass->addMember(id_name(), Attr_ReadOnly, &index);
+ internalClasses[EngineBase::Class_FunctionObject] = ic;
+ ic = ic->addMember(id_name(), Attr_ReadOnly, &index);
Q_ASSERT(index == Heap::ScriptFunction::Index_Name);
- scriptFunctionClass = scriptFunctionClass->addMember(id_length(), Attr_ReadOnly, &index);
+ ic = ic->changeVTable(ScriptFunction::staticVTable());
+ internalClasses[EngineBase::Class_ScriptFunction] = ic->addMember(id_length(), Attr_ReadOnly, &index);
+ Q_ASSERT(index == Heap::ScriptFunction::Index_Length);
+ internalClasses[EngineBase::Class_BuiltinFunction] = ic->changeVTable(BuiltinFunction::staticVTable());
Q_ASSERT(index == Heap::ScriptFunction::Index_Length);
- protoClass = emptyClass->addMember(id_constructor(), Attr_NotEnumerable, &index);
+ internalClasses[EngineBase::Class_ObjectProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_NotEnumerable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor);
Scope scope(this);
ScopedString str(scope);
- regExpObjectClass = emptyClass->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
+ internalClasses[Class_RegExp] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::RegExp::staticVTable());
+ ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype());
+ ic = ic->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
Q_ASSERT(index == RegExpObject::Index_LastIndex);
- regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_Source);
- regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_Global);
- regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_IgnoreCase);
- regExpObjectClass = regExpObjectClass->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_Multiline);
+ jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic, objectPrototype());
+ internalClasses[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d());
- jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(regExpObjectClass, objectPrototype());
- regExpExecArrayClass = arrayClass->addMember(id_index(), Attr_Data, &index);
+ ic = internalClasses[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index);
Q_ASSERT(index == RegExpObject::Index_ArrayIndex);
- regExpExecArrayClass = regExpExecArrayClass->addMember(id_input(), Attr_Data, &index);
+ internalClasses[EngineBase::Class_RegExpExecArray] = ic->addMember(id_input(), Attr_Data, &index);
Q_ASSERT(index == RegExpObject::Index_ArrayInput);
- errorClass = emptyClass->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index);
+ ic = newInternalClass(ErrorObject::staticVTable(), 0);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_Stack);
- errorClass = errorClass->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_FileName);
- errorClass = errorClass->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index);
+ internalClasses[EngineBase::Class_ErrorObject] = ic;
Q_ASSERT(index == ErrorObject::Index_LineNumber);
- errorClassWithMessage = errorClass->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
+ internalClasses[EngineBase::Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_Message);
- errorProtoClass = emptyClass->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index);
+ ic = newInternalClass(ErrorObject::staticVTable(), objectPrototype());
+ ic = ic->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorPrototype::Index_Constructor);
- errorProtoClass = errorProtoClass->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorPrototype::Index_Message);
- errorProtoClass = errorProtoClass->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index);
+ internalClasses[EngineBase::Class_ErrorProto] = ic->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorPrototype::Index_Name);
jsObjects[GetStack_Function] = BuiltinFunction::create(rootContext(), str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack);
getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0));
- jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(errorProtoClass, objectPrototype());
- jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(errorProtoClass, errorPrototype());
- jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(errorProtoClass, errorPrototype());
- jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(errorProtoClass, errorPrototype());
- jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(errorProtoClass, errorPrototype());
- jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(errorProtoClass, errorPrototype());
- jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(errorProtoClass, errorPrototype());
+ jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto], objectPrototype());
+ jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
+ jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
+ jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
+ jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
+ jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
+ jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>(emptyClass, objectPrototype());
+ jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>();
Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d());
- jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(arrayClass, arrayPrototype()));
+ ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this));
+ jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic, SequencePrototype::defaultPrototype(this)));
ExecutionContext *global = rootContext();
jsObjects[Object_Ctor] = memoryManager->allocObject<ObjectCtor>(global);
@@ -490,7 +517,7 @@ ExecutionEngine::~ExecutionEngine()
for (QV4::CompiledData::CompilationUnit *unit : qAsConst(remainingUnits))
unit->unlink();
- emptyClass->destroy();
+ internalClasses[Class_Empty]->destroy();
delete classPool;
delete bumperPointerAllocator;
delete regExpCache;
@@ -548,6 +575,11 @@ ExecutionContext *ExecutionEngine::pushGlobalContext()
return currentContext;
}
+InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
+{
+ return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : 0);
+}
+
Heap::Object *ExecutionEngine::newObject()
{
return memoryManager->allocObject<Object>();
@@ -864,8 +896,8 @@ static inline char *v4StackTrace(const ExecutionContext *context)
QString result;
QTextStream str(&result);
str << "stack=[";
- if (context && context->d()->engine) {
- const QVector<StackFrame> stackTrace = context->d()->engine->stackTrace(20);
+ if (context && context->engine()) {
+ const QVector<StackFrame> stackTrace = context->engine()->stackTrace(20);
for (int i = 0; i < stackTrace.size(); ++i) {
if (i)
str << ',';
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index a2c774c295..16a043dda5 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -55,6 +55,7 @@
#include "qv4managed_p.h"
#include "qv4context_p.h"
#include <private/qintrusivelist_p.h>
+#include "qv4enginebase_p.h"
#ifndef V4_BOOTSTRAP
# include <private/qv8engine_p.h>
@@ -96,16 +97,10 @@ private:
friend struct ExecutionContext;
friend struct Heap::ExecutionContext;
public:
- qint32 callDepth;
-
ExecutableAllocator *executableAllocator;
ExecutableAllocator *regExpAllocator;
QScopedPointer<EvalISelFactory> iselFactory;
- ExecutionContext *currentContext;
-
- Value *jsStackLimit;
-
WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine.
enum {
@@ -113,7 +108,6 @@ public:
GCStackLimit = 2*1024*1024
};
WTF::PageAllocation *jsStack;
- Value *jsStackBase;
WTF::PageAllocation *gcStack;
@@ -125,10 +119,6 @@ public:
return ptr;
}
- IdentifierTable *identifierTable;
-
- Object *globalObject;
-
Function *globalCode;
#ifdef V4_BOOTSTRAP
@@ -239,25 +229,6 @@ public:
Object *signalHandlerPrototype() const { return reinterpret_cast<Object *>(jsObjects + SignalHandlerProto); }
InternalClassPool *classPool;
- InternalClass *emptyClass;
-
- InternalClass *arrayClass;
- InternalClass *stringClass;
-
- InternalClass *functionClass;
- InternalClass *scriptFunctionClass;
- InternalClass *protoClass;
-
- InternalClass *regExpExecArrayClass;
- InternalClass *regExpObjectClass;
-
- InternalClass *argumentsObjectClass;
- InternalClass *strictArgumentsObjectClass;
-
- InternalClass *errorClass;
- InternalClass *errorClassWithMessage;
- InternalClass *errorProtoClass;
-
EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); }
FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); }
FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); }
@@ -391,6 +362,8 @@ public:
void popContext();
ExecutionContext *parentContext(ExecutionContext *context) const;
+ InternalClass *newInternalClass(const VTable *vtable, Object *prototype);
+
Heap::Object *newObject();
Heap::Object *newObject(InternalClass *internalClass, Object *prototype);
diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h
new file mode 100644
index 0000000000..88f9dfd85c
--- /dev/null
+++ b/src/qml/jsruntime/qv4enginebase_p.h
@@ -0,0 +1,128 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4ENGINEBASE_P_H
+#define QV4ENGINEBASE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qv4global_p.h>
+#include <private/qv4runtimeapi_p.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+// Base class for the execution engine
+
+#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
+#pragma pack(push, 1)
+#endif
+struct EngineBase {
+ Heap::ExecutionContext *current = 0;
+
+ Value *jsStackTop = 0;
+ quint8 hasException = false;
+ quint8 writeBarrierActive = false;
+ quint16 unused = 0;
+#if QT_POINTER_SIZE == 8
+ quint8 padding[4];
+#endif
+ MemoryManager *memoryManager = 0;
+ Runtime runtime;
+
+ qint32 callDepth = 0;
+ Value *jsStackLimit = 0;
+ Value *jsStackBase = 0;
+
+ ExecutionContext *currentContext = 0;
+ IdentifierTable *identifierTable = 0;
+ Object *globalObject = 0;
+
+ enum {
+ Class_Empty,
+ Class_String,
+ Class_MemberData,
+ Class_SimpleArrayData,
+ Class_SparseArrayData,
+ Class_ExecutionContext,
+ Class_SimpleCallContext,
+ Class_Object,
+ Class_ArrayObject,
+ Class_FunctionObject,
+ Class_StringObject,
+ Class_ScriptFunction,
+ Class_BuiltinFunction,
+ Class_ObjectProto,
+ Class_RegExp,
+ Class_RegExpObject,
+ Class_RegExpExecArray,
+ Class_ArgumentsObject,
+ Class_StrictArgumentsObject,
+ Class_ErrorObject,
+ Class_ErrorObjectWithMessage,
+ Class_ErrorProto,
+ NClasses
+ };
+ InternalClass *internalClasses[NClasses];
+};
+#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
+#pragma pack(pop)
+#endif
+
+Q_STATIC_ASSERT(std::is_standard_layout<EngineBase>::value);
+Q_STATIC_ASSERT(offsetof(EngineBase, current) == 0);
+Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current) + QT_POINTER_SIZE);
+Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE);
+Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + QT_POINTER_SIZE);
+Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE);
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp
index 58742a0b84..b3bd28e18b 100644
--- a/src/qml/jsruntime/qv4errorobject.cpp
+++ b/src/qml/jsruntime/qv4errorobject.cpp
@@ -75,7 +75,7 @@ void Heap::ErrorObject::init()
Scope scope(internalClass->engine);
Scoped<QV4::ErrorObject> e(scope, this);
- if (internalClass == scope.engine->errorProtoClass)
+ if (internalClass == scope.engine->internalClasses[EngineBase::Class_ErrorProto])
return;
setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d());
@@ -175,8 +175,6 @@ void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallDa
DEFINE_OBJECT_VTABLE(ErrorObject);
-DEFINE_OBJECT_VTABLE(SyntaxErrorObject);
-
void Heap::SyntaxErrorObject::init(const Value &msg)
{
Heap::ErrorObject::init(msg, SyntaxError);
@@ -322,8 +320,7 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He
obj->setProperty(Index_Constructor, ctor->d());
obj->setProperty(Index_Message, engine->id_empty()->d());
obj->setProperty(Index_Name, engine->newString(QString::fromLatin1(ErrorObject::className(t))));
- if (t == Heap::ErrorObject::Error)
- obj->defineDefaultProperty(engine->id_toString(), method_toString, 0);
+ obj->defineDefaultProperty(engine->id_toString(), method_toString, 0);
}
void ErrorPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData)
diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h
index 5afd9efcba..d556617b48 100644
--- a/src/qml/jsruntime/qv4errorobject_p.h
+++ b/src/qml/jsruntime/qv4errorobject_p.h
@@ -160,7 +160,7 @@ struct ErrorObject: Object {
V4_OBJECT2(ErrorObject, Object)
Q_MANAGED_TYPE(ErrorObject)
- V4_INTERNALCLASS(errorClass)
+ V4_INTERNALCLASS(ErrorObject)
V4_PROTOTYPE(errorPrototype)
V4_NEEDS_DESTROY
@@ -205,8 +205,10 @@ struct ReferenceErrorObject: ErrorObject {
};
struct SyntaxErrorObject: ErrorObject {
- V4_OBJECT2(SyntaxErrorObject, ErrorObject)
+ typedef Heap::SyntaxErrorObject Data;
V4_PROTOTYPE(syntaxErrorPrototype)
+ const Data *d() const { return static_cast<const Data *>(ErrorObject::d()); }
+ Data *d() { return static_cast<Data *>(ErrorObject::d()); }
};
struct TypeErrorObject: ErrorObject {
@@ -326,19 +328,25 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError()
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) {
- return e->memoryManager->allocObject<T>(message.isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), message);
+ InternalClass *ic = e->internalClasses[message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
+ ic = ic->changePrototype(T::defaultPrototype(e)->d());
+ return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), message);
}
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message) {
Scope scope(e);
ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue());
- return e->memoryManager->allocObject<T>(v->isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), v);
+ InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
+ ic = ic->changePrototype(T::defaultPrototype(e)->d());
+ return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v);
}
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column) {
Scope scope(e);
ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue());
- return e->memoryManager->allocObject<T>(v->isUndefined() ? e->errorClass : e->errorClassWithMessage, T::defaultPrototype(e), v, filename, line, column);
+ InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
+ ic = ic->changePrototype(T::defaultPrototype(e)->d());
+ return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v, filename, line, column);
}
diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h
index f13f70e01a..353a6eacff 100644
--- a/src/qml/jsruntime/qv4executableallocator_p.h
+++ b/src/qml/jsruntime/qv4executableallocator_p.h
@@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
-class Q_AUTOTEST_EXPORT ExecutableAllocator
+class Q_QML_AUTOTEST_EXPORT ExecutableAllocator
{
public:
struct ChunkOfPages;
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index ed9e3699f2..4c8117527c 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -59,7 +59,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
{
Q_UNUSED(engine);
- internalClass = engine->emptyClass;
+ internalClass = engine->internalClasses[EngineBase::Class_Empty];
const CompiledData::LEUInt32 *formalsIndices = compiledFunction->formalsTable();
// iterate backwards, so we get the right ordering for duplicate names
Scope scope(engine);
@@ -74,7 +74,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit,
}
// duplicate arguments, need some trick to store them
MemoryManager *mm = engine->memoryManager;
- arg = mm->alloc<String>(mm, arg->d(), engine->newString(QString(0xfffe)));
+ arg = mm->alloc<String>(arg->d(), engine->newString(QString(0xfffe)));
}
}
nFormals = compiledFunction->nFormals;
@@ -92,7 +92,7 @@ Function::~Function()
void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> &parameters)
{
- internalClass = engine->emptyClass;
+ internalClass = engine->internalClasses[EngineBase::Class_Empty];
// iterate backwards, so we get the right ordering for duplicate names
Scope scope(engine);
@@ -106,7 +106,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr
break;
}
// duplicate arguments, need some trick to store them
- arg = engine->memoryManager->alloc<String>(engine->memoryManager, arg->d(), engine->newString(QString(0xfffe)));
+ arg = engine->memoryManager->alloc<String>(arg->d(), engine->newString(QString(0xfffe)));
}
}
nFormals = parameters.size();
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 5c8f03dc72..45fdde98f7 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -124,8 +124,8 @@ void FunctionObject::init(String *n, bool createProto)
Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == Heap::FunctionObject::Index_Prototype);
if (createProto) {
- ScopedObject proto(s, scope()->engine->newObject(s.engine->protoClass, s.engine->objectPrototype()));
- Q_ASSERT(s.engine->protoClass->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor);
+ ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses[EngineBase::Class_ObjectProto], s.engine->objectPrototype()));
+ Q_ASSERT(s.engine->internalClasses[EngineBase::Class_ObjectProto]->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor);
proto->setProperty(Heap::FunctionObject::Index_ProtoConstructor, d());
setProperty(Heap::FunctionObject::Index_Prototype, proto);
} else {
@@ -138,7 +138,7 @@ void FunctionObject::init(String *n, bool createProto)
ReturnedValue FunctionObject::name() const
{
- return get(scope()->engine->id_name());
+ return get(scope()->internalClass->engine->id_name());
}
void FunctionObject::construct(const Managed *that, Scope &scope, CallData *)
@@ -153,7 +153,7 @@ void FunctionObject::call(const Managed *, Scope &scope, CallData *)
Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function)
{
- return scope->d()->engine->memoryManager->allocObject<ScriptFunction>(scope, function);
+ return scope->engine()->memoryManager->allocObject<ScriptFunction>(scope, function);
}
bool FunctionObject::isBinding() const
@@ -369,8 +369,8 @@ void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *call
Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that));
- InternalClass *ic = v4->emptyClass;
- ScopedObject proto(scope, f->protoForConstructor());
+ InternalClass *ic = f->classForConstructor();
+ ScopedObject proto(scope, ic->prototype);
ScopedObject obj(scope, v4->newObject(ic, proto));
callData->thisObject = obj.asReturnedValue();
@@ -433,54 +433,24 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function
ScopedProperty pd(s);
pd->value = s.engine->thrower();
pd->set = s.engine->thrower();
- f->insertMember(scope->d()->engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
- f->insertMember(scope->d()->engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
+ f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
+ f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
}
}
-Heap::Object *ScriptFunction::protoForConstructor() const
+InternalClass *ScriptFunction::classForConstructor() const
{
const Object *o = d()->protoProperty();
- if (o)
- return o->d();
- return engine()->objectPrototype()->d();
-}
-
-
+ InternalClass *ic = d()->cachedClassForConstructor;
+ if (ic && ic->prototype == o->d())
+ return ic;
-DEFINE_OBJECT_VTABLE(OldBuiltinFunction);
-
-void Heap::OldBuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *))
-{
- Heap::FunctionObject::init(scope, name);
- this->code = code;
-}
-
-void OldBuiltinFunction::construct(const Managed *f, Scope &scope, CallData *)
-{
- scope.result = static_cast<const OldBuiltinFunction *>(f)->internalClass()->engine->throwTypeError();
-}
-
-void OldBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData)
-{
- const OldBuiltinFunction *f = static_cast<const OldBuiltinFunction *>(that);
- ExecutionEngine *v4 = scope.engine;
- if (v4->hasException) {
- scope.result = Encode::undefined();
- return;
- }
- CHECK_STACK_LIMITS(v4, scope);
-
- ExecutionContextSaver ctxSaver(scope);
-
- SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4);
- ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context?
- ctx->callData = callData;
- v4->pushContext(ctx);
- Q_ASSERT(v4->current == ctx);
+ ic = engine()->internalClasses[EngineBase::Class_Object];
+ if (o)
+ ic = ic->changePrototype(o->d());
+ d()->cachedClassForConstructor = ic;
- scope.result = f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext));
- v4->memoryManager->freeSimpleCallContext();
+ return ic;
}
DEFINE_OBJECT_VTABLE(BuiltinFunction);
@@ -520,7 +490,7 @@ void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *c
ExecutionContextSaver ctxSaver(scope);
- SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4);
+ SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext();
ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context?
ctx->callData = callData;
v4->pushContext(ctx);
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h
index d8929026ca..6ce5734b6d 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -118,6 +118,8 @@ struct ScriptFunction : FunctionObject {
Index_Length
};
void init(QV4::ExecutionContext *scope, Function *function);
+
+ QV4::InternalClass *cachedClassForConstructor;
};
#define BoundFunctionMembers(class, Member) \
@@ -139,7 +141,7 @@ struct Q_QML_EXPORT FunctionObject: Object {
};
V4_OBJECT2(FunctionObject, Object)
Q_MANAGED_TYPE(FunctionObject)
- V4_INTERNALCLASS(functionClass)
+ V4_INTERNALCLASS(FunctionObject)
V4_PROTOTYPE(functionPrototype)
V4_NEEDS_DESTROY
@@ -192,20 +194,10 @@ struct FunctionPrototype: FunctionObject
static void method_bind(const BuiltinFunction *, Scope &scope, CallData *callData);
};
-struct Q_QML_EXPORT OldBuiltinFunction : FunctionObject {
- V4_OBJECT2(OldBuiltinFunction, FunctionObject)
-
- static void construct(const Managed *, Scope &scope, CallData *);
- static void call(const Managed *that, Scope &scope, CallData *callData);
-};
-
struct Q_QML_EXPORT BuiltinFunction : FunctionObject {
V4_OBJECT2(BuiltinFunction, FunctionObject)
+ V4_INTERNALCLASS(BuiltinFunction)
- static Heap::OldBuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(CallContext *))
- {
- return scope->engine()->memoryManager->allocObject<OldBuiltinFunction>(scope, name, code);
- }
static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *))
{
return scope->engine()->memoryManager->allocObject<BuiltinFunction>(scope, name, code);
@@ -238,12 +230,12 @@ void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index
struct ScriptFunction : FunctionObject {
V4_OBJECT2(ScriptFunction, FunctionObject)
- V4_INTERNALCLASS(scriptFunctionClass)
+ V4_INTERNALCLASS(ScriptFunction)
static void construct(const Managed *, Scope &scope, CallData *callData);
static void call(const Managed *that, Scope &scope, CallData *callData);
- Heap::Object *protoForConstructor() const;
+ InternalClass *classForConstructor() const;
};
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index f0630660d4..0916e8e110 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -332,8 +332,8 @@ DEFINE_OBJECT_VTABLE(EvalFunction);
void Heap::EvalFunction::init(QV4::ExecutionContext *scope)
{
- Heap::FunctionObject::init(scope, scope->d()->engine->id_eval());
Scope s(scope);
+ Heap::FunctionObject::init(scope, s.engine->id_eval());
ScopedFunctionObject f(s, this);
f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1));
}
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index 3d9a672f2f..603da1df7b 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -43,6 +43,7 @@
#include <qv4identifier_p.h>
#include "qv4object_p.h"
#include "qv4identifiertable_p.h"
+#include "qv4value_p.h"
QT_BEGIN_NAMESPACE
@@ -104,6 +105,8 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize)
InternalClass::InternalClass(ExecutionEngine *engine)
: engine(engine)
+ , vtable(0)
+ , prototype(0)
, m_sealed(0)
, m_frozen(0)
, size(0)
@@ -115,6 +118,8 @@ InternalClass::InternalClass(ExecutionEngine *engine)
InternalClass::InternalClass(const QV4::InternalClass &other)
: QQmlJS::Managed()
, engine(other.engine)
+ , vtable(other.vtable)
+ , prototype(other.prototype)
, propertyTable(other.propertyTable)
, nameMap(other.nameMap)
, propertyData(other.propertyData)
@@ -126,6 +131,24 @@ InternalClass::InternalClass(const QV4::InternalClass &other)
Q_ASSERT(extensible);
}
+static void insertHoleIntoPropertyData(Object *object, int idx)
+{
+ Heap::Object *o = object->d();
+ ExecutionEngine *v4 = o->internalClass->engine;
+ int size = o->internalClass->size;
+ for (int i = size - 1; i > idx; --i)
+ o->setProperty(v4, i, *o->propertyData(i - 1));
+}
+
+static void removeFromPropertyData(Object *object, int idx, bool accessor = false)
+{
+ Heap::Object *o = object->d();
+ ExecutionEngine *v4 = o->internalClass->engine;
+ int size = o->internalClass->size;
+ for (int i = idx; i < size; ++i)
+ o->setProperty(v4, i, *o->propertyData(i + (accessor ? 2 : 1)));
+}
+
void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index)
{
uint idx;
@@ -137,10 +160,10 @@ void InternalClass::changeMember(Object *object, String *string, PropertyAttribu
object->setInternalClass(newClass);
if (newClass->size > oldClass->size) {
Q_ASSERT(newClass->size == oldClass->size + 1);
- object->d()->memberData->values.insertData(newClass->engine, idx + 1, Primitive::emptyValue());
+ insertHoleIntoPropertyData(object, idx);
} else if (newClass->size < oldClass->size) {
Q_ASSERT(newClass->size == oldClass->size - 1);
- object->d()->memberData->values.removeData(newClass->engine, idx + 1);
+ removeFromPropertyData(object, idx + 1);
}
}
@@ -167,13 +190,14 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
if (data == propertyData.at(idx))
return this;
- Transition temp = { identifier, 0, (int)data.flags() };
+ Transition temp = { { identifier }, nullptr, (int)data.flags() };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup)
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass = engine->emptyClass;
+ InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
+ newClass = newClass->changePrototype(prototype);
for (uint i = 0; i < size; ++i) {
if (i == idx) {
newClass = newClass->addMember(nameMap.at(i), data);
@@ -187,12 +211,72 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
return newClass;
}
+InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
+{
+ Q_ASSERT(prototype != proto);
+
+ Transition temp = { { nullptr }, 0, Transition::PrototypeChange };
+ temp.prototype = proto;
+
+ Transition &t = lookupOrInsertTransition(temp);
+ if (t.lookup)
+ return t.lookup;
+
+ // create a new class and add it to the tree
+ InternalClass *newClass;
+ if (!size && !prototype) {
+ newClass = engine->newClass(*this);
+ newClass->prototype = proto;
+ } else {
+ newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
+ newClass = newClass->changePrototype(proto);
+ for (uint i = 0; i < size; ++i) {
+ if (!propertyData.at(i).isEmpty())
+ newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
+ }
+ }
+
+ t.lookup = newClass;
+ return newClass;
+}
+
+InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
+{
+ Q_ASSERT(vtable != vt);
+
+ Transition temp = { { nullptr }, nullptr, Transition::VTableChange };
+ temp.vtable = vt;
+
+ Transition &t = lookupOrInsertTransition(temp);
+ if (t.lookup)
+ return t.lookup;
+
+ // create a new class and add it to the tree
+ InternalClass *newClass;
+ if (this == engine->internalClasses[EngineBase::Class_Empty]) {
+ newClass = engine->newClass(*this);
+ newClass->vtable = vt;
+ } else {
+ newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vt);
+ newClass = newClass->changePrototype(prototype);
+ for (uint i = 0; i < size; ++i) {
+ if (!propertyData.at(i).isEmpty())
+ newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
+ }
+ }
+
+ t.lookup = newClass;
+ Q_ASSERT(t.lookup);
+ Q_ASSERT(newClass->vtable);
+ return newClass;
+}
+
InternalClass *InternalClass::nonExtensible()
{
if (!extensible)
return this;
- Transition temp = { Q_NULLPTR, Q_NULLPTR, Transition::NotExtensible};
+ Transition temp = { { nullptr }, nullptr, Transition::NotExtensible};
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup)
return t.lookup;
@@ -240,7 +324,7 @@ InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttribut
InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index)
{
- Transition temp = { identifier, 0, (int)data.flags() };
+ Transition temp = { { identifier }, nullptr, (int)data.flags() };
Transition &t = lookupOrInsertTransition(temp);
if (index)
@@ -276,7 +360,7 @@ void InternalClass::removeMember(Object *object, Identifier *id)
uint propIdx = oldClass->propertyTable.lookup(id);
Q_ASSERT(propIdx < oldClass->size);
- Transition temp = { id, 0, -1 };
+ Transition temp = { { id }, nullptr, -1 };
Transition &t = object->internalClass()->lookupOrInsertTransition(temp);
bool accessor = oldClass->propertyData.at(propIdx).isAccessor();
@@ -285,7 +369,8 @@ void InternalClass::removeMember(Object *object, Identifier *id)
object->setInternalClass(t.lookup);
} else {
// create a new class and add it to the tree
- InternalClass *newClass = oldClass->engine->emptyClass;
+ InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]->changeVTable(oldClass->vtable);
+ newClass = newClass->changePrototype(oldClass->prototype);
for (uint i = 0; i < oldClass->size; ++i) {
if (i == propIdx)
continue;
@@ -298,14 +383,17 @@ void InternalClass::removeMember(Object *object, Identifier *id)
Q_ASSERT(object->internalClass()->size == oldClass->size - (accessor ? 2 : 1));
// remove the entry in the property data
- object->d()->memberData->values.removeData(oldClass->engine, propIdx, accessor ? 2 : 1);
+ removeFromPropertyData(object, propIdx, accessor);
t.lookup = object->internalClass();
Q_ASSERT(t.lookup);
}
-uint InternalClass::find(const Identifier *id)
+uint QV4::InternalClass::find(const String *string)
{
+ engine->identifierTable->identifier(string);
+ const Identifier *id = string->d()->identifier;
+
uint index = propertyTable.lookup(id);
if (index < size)
return index;
@@ -318,7 +406,8 @@ InternalClass *InternalClass::sealed()
if (m_sealed)
return m_sealed;
- m_sealed = engine->emptyClass;
+ m_sealed = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
+ m_sealed = m_sealed->changePrototype(prototype);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
if (attrs.isEmpty())
@@ -347,7 +436,8 @@ InternalClass *InternalClass::frozen()
InternalClass *InternalClass::propertiesFrozen() const
{
- InternalClass *frozen = engine->emptyClass;
+ InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
+ frozen = frozen->changePrototype(prototype);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
if (attrs.isEmpty())
@@ -390,7 +480,23 @@ void InternalClass::destroy()
void InternalClassPool::markObjects(MarkStack *markStack)
{
- Q_UNUSED(markStack);
+ InternalClass *ic = markStack->engine->internalClasses[EngineBase::Class_Empty];
+ Q_ASSERT(!ic->prototype);
+
+ // only need to go two levels into the IC hierarchy, as prototype changes
+ // can only happen there
+ for (auto &t : ic->transitions) {
+ Q_ASSERT(t.lookup);
+ if (t.flags == InternalClassTransition::VTableChange) {
+ InternalClass *ic2 = t.lookup;
+ for (auto &t2 : ic2->transitions) {
+ if (t2.flags == InternalClassTransition::PrototypeChange)
+ t2.lookup->prototype->mark(markStack);
+ }
+ } else if (t.flags == InternalClassTransition::PrototypeChange) {
+ t.lookup->prototype->mark(markStack);
+ }
+ }
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index a29ce5b5ff..df17074e72 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -54,18 +54,17 @@
#include <QHash>
#include <private/qqmljsmemorypool_p.h>
-#include <private/qv4engine_p.h>
-#include <private/qv4identifiertable_p.h>
+#include <private/qv4identifier_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
struct String;
-struct ExecutionEngine;
struct Object;
struct Identifier;
struct VTable;
+struct MarkStack;
struct PropertyHashData;
struct PropertyHash
@@ -222,12 +221,18 @@ private:
struct InternalClassTransition
{
- Identifier *id;
+ union {
+ Identifier *id;
+ const VTable *vtable;
+ Heap::Object *prototype;
+ };
InternalClass *lookup;
int flags;
enum {
// range 0-0xff is reserved for attribute changes
- NotExtensible = 0x100
+ NotExtensible = 0x100,
+ VTableChange = 0x200,
+ PrototypeChange = 0x201
};
bool operator==(const InternalClassTransition &other) const
@@ -239,6 +244,8 @@ struct InternalClassTransition
struct InternalClass : public QQmlJS::Managed {
ExecutionEngine *engine;
+ const VTable *vtable;
+ Heap::Object *prototype;
PropertyHash propertyTable; // id to valueIndex
SharedInternalClassData<Identifier *> nameMap;
@@ -254,41 +261,49 @@ struct InternalClass : public QQmlJS::Managed {
uint size;
bool extensible;
- InternalClass *nonExtensible();
+ Q_REQUIRED_RESULT InternalClass *nonExtensible();
+ Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) {
+ if (vtable == vt)
+ return this;
+ return changeVTableImpl(vt);
+ }
+ Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) {
+ if (prototype == proto)
+ return this;
+ return changePrototypeImpl(proto);
+ }
+
static void addMember(Object *object, String *string, PropertyAttributes data, uint *index);
- InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0);
- InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = 0);
- InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = 0);
+ Q_REQUIRED_RESULT InternalClass *addMember(String *string, PropertyAttributes data, uint *index = 0);
+ Q_REQUIRED_RESULT InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = 0);
+ Q_REQUIRED_RESULT InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = 0);
static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = 0);
static void removeMember(Object *object, Identifier *id);
uint find(const String *string);
- uint find(const Identifier *id);
+ uint find(const Identifier *id)
+ {
+ uint index = propertyTable.lookup(id);
+ if (index < size)
+ return index;
+
+ return UINT_MAX;
+ }
- InternalClass *sealed();
- InternalClass *frozen();
- InternalClass *propertiesFrozen() const;
+ Q_REQUIRED_RESULT InternalClass *sealed();
+ Q_REQUIRED_RESULT InternalClass *frozen();
+ Q_REQUIRED_RESULT InternalClass *propertiesFrozen() const;
void destroy();
private:
+ Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt);
+ Q_QML_EXPORT InternalClass *changePrototypeImpl(Heap::Object *proto);
InternalClass *addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index);
friend struct ExecutionEngine;
InternalClass(ExecutionEngine *engine);
InternalClass(const InternalClass &other);
};
-inline uint InternalClass::find(const String *string)
-{
- engine->identifierTable->identifier(string);
- const Identifier *id = string->d()->identifier;
-
- uint index = propertyTable.lookup(id);
- if (index < size)
- return index;
-
- return UINT_MAX;
-}
-
struct InternalClassPool : public QQmlJS::MemoryPool
{
void markObjects(MarkStack *markStack);
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 11d7767e05..d943ae1340 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -63,7 +63,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu
return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs);
}
- obj = obj->prototype;
+ obj = obj->prototype();
++i;
}
level = Size;
@@ -76,7 +76,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu
return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs);
}
- obj = obj->prototype;
+ obj = obj->prototype();
}
return Primitive::emptyValue().asReturnedValue();
}
@@ -98,7 +98,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs
return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs);
}
- obj = obj->prototype;
+ obj = obj->prototype();
++i;
}
level = Size;
@@ -111,7 +111,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs
return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs);
}
- obj = obj->prototype;
+ obj = obj->prototype();
}
return Primitive::emptyValue().asReturnedValue();
}
@@ -284,11 +284,17 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va
ReturnedValue v = l->lookup(object, proto, &attrs);
if (v != Primitive::emptyValue().asReturnedValue()) {
l->type = object.type();
- l->proto = proto;
+ l->proto = proto->d();
if (attrs.isData()) {
- if (l->level == 0)
- l->getter = Lookup::primitiveGetter0;
- else if (l->level == 1)
+ if (l->level == 0) {
+ uint nInline = l->proto->vtable()->nInlineProperties;
+ if (l->index < nInline)
+ l->getter = Lookup::primitiveGetter0Inline;
+ else {
+ l->index -= nInline;
+ l->getter = Lookup::primitiveGetter0MemberData;
+ }
+ } else if (l->level == 1)
l->getter = Lookup::primitiveGetter1;
return v;
} else {
@@ -307,15 +313,18 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const
{
Lookup l1 = *l;
- if (l1.getter == Lookup::getter0 || l1.getter == Lookup::getter1) {
+ if (l1.getter == Lookup::getter0MemberData || l1.getter == Lookup::getter0Inline || l1.getter == Lookup::getter1) {
if (const Object *o = object.as<Object>()) {
ReturnedValue v = o->getLookup(l);
Lookup l2 = *l;
- if (l->index != UINT_MAX && (l2.getter == Lookup::getter0 || l2.getter == Lookup::getter1)) {
- // if we have a getter0, make sure it comes first
- if (l2.getter == Lookup::getter0)
- qSwap(l1, l2);
+ if (l2.index != UINT_MAX) {
+ if (l1.getter != Lookup::getter0Inline) {
+ if (l2.getter == Lookup::getter0Inline ||
+ (l1.getter != Lookup::getter0MemberData && l2.getter == Lookup::getter0MemberData))
+ // sort the better getter first
+ qSwap(l1, l2);
+ }
l->classList[0] = l1.classList[0];
l->classList[1] = l1.classList[1];
@@ -324,8 +333,22 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const
l->index = l1.index;
l->index2 = l2.index;
- if (l1.getter == Lookup::getter0) {
- l->getter = (l2.getter == Lookup::getter0) ? Lookup::getter0getter0 : Lookup::getter0getter1;
+ if (l1.getter == Lookup::getter0Inline) {
+ if (l2.getter == Lookup::getter0Inline)
+ l->getter = Lookup::getter0Inlinegetter0Inline;
+ else if (l2.getter == Lookup::getter0MemberData)
+ l->getter = Lookup::getter0Inlinegetter0MemberData;
+ else if (l2.getter == Lookup::getter1)
+ l->getter = Lookup::getter0Inlinegetter1;
+ else
+ Q_UNREACHABLE();
+ } else if (l1.getter == Lookup::getter0MemberData) {
+ if (l2.getter == Lookup::getter0MemberData)
+ l->getter = Lookup::getter0MemberDatagetter0MemberData;
+ else if (l2.getter == Lookup::getter1)
+ l->getter = Lookup::getter0MemberDatagetter1;
+ else
+ Q_UNREACHABLE();
} else {
Q_ASSERT(l1.getter == Lookup::getter1 && l2.getter == Lookup::getter1);
l->getter = Lookup::getter1getter1;
@@ -349,14 +372,26 @@ ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const V
return o->get(name);
}
-ReturnedValue Lookup::getter0(Lookup *l, ExecutionEngine *engine, const Value &object)
+ReturnedValue Lookup::getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object)
{
// we can safely cast to a QV4::Object here. If object is actually a string,
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass)
- return o->propertyData(l->index)->asReturnedValue();
+ return o->memberData->values.data()[l->index].asReturnedValue();
+ }
+ return getterTwoClasses(l, engine, object);
+}
+
+ReturnedValue Lookup::getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object)
+{
+ // we can safely cast to a QV4::Object here. If object is actually a string,
+ // the internal class won't match
+ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
+ if (o) {
+ if (l->classList[0] == o->internalClass)
+ return o->inlinePropertyData(l->index)->asReturnedValue();
}
return getterTwoClasses(l, engine, object);
}
@@ -367,8 +402,8 @@ ReturnedValue Lookup::getter1(Lookup *l, ExecutionEngine *engine, const Value &o
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
- if (l->classList[0] == o->internalClass && l->classList[1] == o->prototype->internalClass)
- return o->prototype->propertyData(l->index)->asReturnedValue();
+ if (l->classList[0] == o->internalClass && l->classList[1] == l->proto->internalClass)
+ return l->proto->propertyData(l->index)->asReturnedValue();
}
return getterTwoClasses(l, engine, object);
}
@@ -380,9 +415,9 @@ ReturnedValue Lookup::getter2(Lookup *l, ExecutionEngine *engine, const Value &o
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass) {
- Heap::Object *p = o->prototype;
- if (l->classList[1] == p->internalClass) {
- p = p->prototype;
+ Q_ASSERT(l->proto == o->prototype());
+ if (l->classList[1] == l->proto->internalClass) {
+ Heap::Object *p = l->proto->prototype();
if (l->classList[2] == p->internalClass)
return p->propertyData(l->index)->asReturnedValue();
}
@@ -392,31 +427,76 @@ ReturnedValue Lookup::getter2(Lookup *l, ExecutionEngine *engine, const Value &o
return getterFallback(l, engine, object);
}
-ReturnedValue Lookup::getter0getter0(Lookup *l, ExecutionEngine *engine, const Value &object)
+ReturnedValue Lookup::getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object)
{
// we can safely cast to a QV4::Object here. If object is actually a string,
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass)
- return o->propertyData(l->index)->asReturnedValue();
+ return o->inlinePropertyData(l->index)->asReturnedValue();
if (l->classList[2] == o->internalClass)
- return o->propertyData(l->index2)->asReturnedValue();
+ return o->inlinePropertyData(l->index2)->asReturnedValue();
+ }
+ l->getter = getterFallback;
+ return getterFallback(l, engine, object);
+}
+
+ReturnedValue Lookup::getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object)
+{
+ // we can safely cast to a QV4::Object here. If object is actually a string,
+ // the internal class won't match
+ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
+ if (o) {
+ if (l->classList[0] == o->internalClass)
+ return o->inlinePropertyData(l->index)->asReturnedValue();
+ if (l->classList[2] == o->internalClass)
+ return o->memberData->values.data()[l->index2].asReturnedValue();
+ }
+ l->getter = getterFallback;
+ return getterFallback(l, engine, object);
+}
+
+ReturnedValue Lookup::getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object)
+{
+ // we can safely cast to a QV4::Object here. If object is actually a string,
+ // the internal class won't match
+ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
+ if (o) {
+ if (l->classList[0] == o->internalClass)
+ return o->memberData->values.data()[l->index].asReturnedValue();
+ if (l->classList[2] == o->internalClass)
+ return o->memberData->values.data()[l->index2].asReturnedValue();
+ }
+ l->getter = getterFallback;
+ return getterFallback(l, engine, object);
+}
+
+ReturnedValue Lookup::getter0Inlinegetter1(Lookup *l, ExecutionEngine *engine, const Value &object)
+{
+ // we can safely cast to a QV4::Object here. If object is actually a string,
+ // the internal class won't match
+ Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
+ if (o) {
+ if (l->classList[0] == o->internalClass)
+ return o->inlinePropertyData(l->index)->asReturnedValue();
+ if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype()->internalClass)
+ return o->prototype()->propertyData(l->index2)->asReturnedValue();
}
l->getter = getterFallback;
return getterFallback(l, engine, object);
}
-ReturnedValue Lookup::getter0getter1(Lookup *l, ExecutionEngine *engine, const Value &object)
+ReturnedValue Lookup::getter0MemberDatagetter1(Lookup *l, ExecutionEngine *engine, const Value &object)
{
// we can safely cast to a QV4::Object here. If object is actually a string,
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass)
- return o->propertyData(l->index)->asReturnedValue();
- if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype->internalClass)
- return o->prototype->propertyData(l->index2)->asReturnedValue();
+ return o->memberData->values.data()[l->index].asReturnedValue();
+ if (l->classList[2] == o->internalClass && l->classList[3] == o->prototype()->internalClass)
+ return o->prototype()->propertyData(l->index2)->asReturnedValue();
}
l->getter = getterFallback;
return getterFallback(l, engine, object);
@@ -429,11 +509,11 @@ ReturnedValue Lookup::getter1getter1(Lookup *l, ExecutionEngine *engine, const V
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass &&
- l->classList[1] == o->prototype->internalClass)
- return o->prototype->propertyData(l->index)->asReturnedValue();
+ l->classList[1] == o->prototype()->internalClass)
+ return o->prototype()->propertyData(l->index)->asReturnedValue();
if (l->classList[2] == o->internalClass &&
- l->classList[3] == o->prototype->internalClass)
- return o->prototype->propertyData(l->index2)->asReturnedValue();
+ l->classList[3] == o->prototype()->internalClass)
+ return o->prototype()->propertyData(l->index2)->asReturnedValue();
return getterFallback(l, engine, object);
}
l->getter = getterFallback;
@@ -470,9 +550,9 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass &&
- l->classList[1] == o->prototype->internalClass) {
+ l->classList[1] == l->proto->internalClass) {
Scope scope(o->internalClass->engine);
- ScopedFunctionObject getter(scope, o->prototype->propertyData(l->index + Object::GetterOffset));
+ ScopedFunctionObject getter(scope, o->prototype()->propertyData(l->index + Object::GetterOffset));
if (!getter)
return Encode::undefined();
@@ -493,9 +573,9 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
if (l->classList[0] == o->internalClass) {
- o = o->prototype;
- if (l->classList[1] == o->internalClass) {
- o = o->prototype;
+ Q_ASSERT(o->prototype() == l->proto);
+ if (l->classList[1] == l->proto->internalClass) {
+ o = l->proto->prototype();
if (l->classList[2] == o->internalClass) {
Scope scope(o->internalClass->engine);
ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset));
@@ -514,12 +594,23 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const
return getterFallback(l, engine, object);
}
-ReturnedValue Lookup::primitiveGetter0(Lookup *l, ExecutionEngine *engine, const Value &object)
+ReturnedValue Lookup::primitiveGetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (object.type() == l->type) {
- Object *o = l->proto;
- if (l->classList[0] == o->internalClass())
- return o->propertyData(l->index)->asReturnedValue();
+ Heap::Object *o = l->proto;
+ if (l->classList[0] == o->internalClass)
+ return o->inlinePropertyData(l->index)->asReturnedValue();
+ }
+ l->getter = getterGeneric;
+ return getterGeneric(l, engine, object);
+}
+
+ReturnedValue Lookup::primitiveGetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object)
+{
+ if (object.type() == l->type) {
+ Heap::Object *o = l->proto;
+ if (l->classList[0] == o->internalClass)
+ return o->memberData->values.data()[l->index].asReturnedValue();
}
l->getter = getterGeneric;
return getterGeneric(l, engine, object);
@@ -528,8 +619,8 @@ ReturnedValue Lookup::primitiveGetter0(Lookup *l, ExecutionEngine *engine, const
ReturnedValue Lookup::primitiveGetter1(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (object.type() == l->type) {
- Object *o = l->proto;
- if (l->classList[0] == o->internalClass() &&
+ Heap::Object *o = l->proto;
+ if (l->classList[0] == o->internalClass &&
l->classList[1] == o->prototype()->internalClass)
return o->prototype()->propertyData(l->index)->asReturnedValue();
}
@@ -540,9 +631,9 @@ ReturnedValue Lookup::primitiveGetter1(Lookup *l, ExecutionEngine *engine, const
ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (object.type() == l->type) {
- Object *o = l->proto;
- if (l->classList[0] == o->internalClass()) {
- Scope scope(o->engine());
+ Heap::Object *o = l->proto;
+ if (l->classList[0] == o->internalClass) {
+ Scope scope(o->internalClass->engine);
ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset));
if (!getter)
return Encode::undefined();
@@ -560,10 +651,10 @@ ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engin
ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (object.type() == l->type) {
- Object *o = l->proto;
- if (l->classList[0] == o->internalClass() &&
+ Heap::Object *o = l->proto;
+ if (l->classList[0] == o->internalClass &&
l->classList[1] == o->prototype()->internalClass) {
- Scope scope(o->engine());
+ Scope scope(o->internalClass->engine);
ScopedFunctionObject getter(scope, o->prototype()->propertyData(l->index + Object::GetterOffset));
if (!getter)
return Encode::undefined();
@@ -604,9 +695,15 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine)
ReturnedValue v = l->lookup(o, &attrs);
if (v != Primitive::emptyValue().asReturnedValue()) {
if (attrs.isData()) {
- if (l->level == 0)
- l->globalGetter = globalGetter0;
- else if (l->level == 1)
+ if (l->level == 0) {
+ uint nInline = o->d()->vtable()->nInlineProperties;
+ if (l->index < nInline)
+ l->globalGetter = globalGetter0Inline;
+ else {
+ l->index -= nInline;
+ l->globalGetter = globalGetter0MemberData;
+ }
+ } else if (l->level == 1)
l->globalGetter = globalGetter1;
else if (l->level == 2)
l->globalGetter = globalGetter2;
@@ -626,11 +723,21 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine)
return engine->throwReferenceError(n);
}
-ReturnedValue Lookup::globalGetter0(Lookup *l, ExecutionEngine *engine)
+ReturnedValue Lookup::globalGetter0Inline(Lookup *l, ExecutionEngine *engine)
{
Object *o = engine->globalObject;
if (l->classList[0] == o->internalClass())
- return o->propertyData(l->index)->asReturnedValue();
+ return o->d()->inlinePropertyData(l->index)->asReturnedValue();
+
+ l->globalGetter = globalGetterGeneric;
+ return globalGetterGeneric(l, engine);
+}
+
+ReturnedValue Lookup::globalGetter0MemberData(Lookup *l, ExecutionEngine *engine)
+{
+ Object *o = engine->globalObject;
+ if (l->classList[0] == o->internalClass())
+ return o->d()->memberData->values.data()[l->index].asReturnedValue();
l->globalGetter = globalGetterGeneric;
return globalGetterGeneric(l, engine);
@@ -651,11 +758,11 @@ ReturnedValue Lookup::globalGetter2(Lookup *l, ExecutionEngine *engine)
{
Heap::Object *o = engine->globalObject->d();
if (l->classList[0] == o->internalClass) {
- o = o->prototype;
+ o = o->prototype();
if (l->classList[1] == o->internalClass) {
- o = o->prototype;
+ o = o->prototype();
if (l->classList[2] == o->internalClass) {
- return o->prototype->propertyData(l->index)->asReturnedValue();
+ return o->prototype()->propertyData(l->index)->asReturnedValue();
}
}
}
@@ -704,9 +811,9 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine)
{
Heap::Object *o = engine->globalObject->d();
if (l->classList[0] == o->internalClass) {
- o = o->prototype;
+ o = o->prototype();
if (l->classList[1] == o->internalClass) {
- o = o->prototype;
+ o = o->prototype();
if (l->classList[2] == o->internalClass) {
Scope scope(o->internalClass->engine);
ScopedFunctionObject getter(scope, o->propertyData(l->index + Object::GetterOffset));
@@ -746,7 +853,7 @@ void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object,
if (Object *o = object.as<Object>()) {
o->setLookup(l, value);
- if (l->setter == Lookup::setter0) {
+ if (l->setter == Lookup::setter0 || l->setter == Lookup::setter0Inline) {
l->setter = setter0setter0;
l->classList[1] = l1.classList[0];
l->index2 = l1.index;
@@ -770,7 +877,7 @@ void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, c
void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
- Object *o = object.as<Object>();
+ Object *o = static_cast<Object *>(object.managed());
if (o && o->internalClass() == l->classList[0]) {
o->setProperty(engine, l->index, value);
return;
@@ -779,15 +886,25 @@ void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Va
setterTwoClasses(l, engine, object, value);
}
+void Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
+{
+ Object *o = static_cast<Object *>(object.managed());
+ if (o && o->internalClass() == l->classList[0]) {
+ o->d()->setInlineProperty(engine, l->index, value);
+ return;
+ }
+
+ setterTwoClasses(l, engine, object, value);
+}
+
void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
- Object *o = object.as<Object>();
+ Object *o = static_cast<Object *>(object.managed());
if (o && o->internalClass() == l->classList[0]) {
- if (!o->prototype()) {
- o->setInternalClass(l->classList[3]);
- o->setProperty(l->index, value);
- return;
- }
+ Q_ASSERT(!o->prototype());
+ o->setInternalClass(l->classList[3]);
+ o->setProperty(l->index, value);
+ return;
}
l->setter = setterFallback;
@@ -796,10 +913,12 @@ void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, co
void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
- Object *o = object.as<Object>();
+ Object *o = static_cast<Object *>(object.managed());
if (o && o->internalClass() == l->classList[0]) {
Heap::Object *p = o->prototype();
- if (p && p->internalClass == l->classList[1]) {
+ Q_ASSERT(p);
+ if (p->internalClass == l->classList[1]) {
+ Q_ASSERT(!p->prototype());
o->setInternalClass(l->classList[3]);
o->setProperty(l->index, value);
return;
@@ -812,12 +931,15 @@ void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, co
void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
- Object *o = object.as<Object>();
+ Object *o = static_cast<Object *>(object.managed());
if (o && o->internalClass() == l->classList[0]) {
Heap::Object *p = o->prototype();
- if (p && p->internalClass == l->classList[1]) {
- p = p->prototype;
- if (p && p->internalClass == l->classList[2]) {
+ Q_ASSERT(p);
+ if (p->internalClass == l->classList[1]) {
+ p = p->prototype();
+ Q_ASSERT(p);
+ if (p->internalClass == l->classList[2]) {
+ Q_ASSERT(!p->prototype());
o->setInternalClass(l->classList[3]);
o->setProperty(l->index, value);
return;
@@ -831,7 +953,7 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, co
void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
- Object *o = object.as<Object>();
+ Object *o = static_cast<Object *>(object.managed());
if (o) {
if (o->internalClass() == l->classList[0]) {
o->setProperty(l->index, value);
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index daf3c71e27..ce5189a780 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -78,13 +78,14 @@ struct Lookup {
struct {
void *dummy0;
void *dummy1;
- Object *proto;
- unsigned type;
+ void *dummy2;
+ Heap::Object *proto;
};
};
union {
int level;
uint index2;
+ unsigned type;
};
uint index;
uint nameIndex;
@@ -101,17 +102,22 @@ struct Lookup {
static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object);
- static ReturnedValue getter0(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getter1(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getter2(Lookup *l, ExecutionEngine *engine, const Value &object);
- static ReturnedValue getter0getter0(Lookup *l, ExecutionEngine *engine, const Value &object);
- static ReturnedValue getter0getter1(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getter0Inlinegetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getter0Inlinegetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getter0MemberDatagetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getter0Inlinegetter1(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getter0MemberDatagetter1(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getter1getter1(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterAccessor0(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterAccessor1(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterAccessor2(Lookup *l, ExecutionEngine *engine, const Value &object);
- static ReturnedValue primitiveGetter0(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue primitiveGetter0Inline(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue primitiveGetter0MemberData(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetter1(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -119,7 +125,8 @@ struct Lookup {
static ReturnedValue arrayLengthGetter(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue globalGetterGeneric(Lookup *l, ExecutionEngine *engine);
- static ReturnedValue globalGetter0(Lookup *l, ExecutionEngine *engine);
+ static ReturnedValue globalGetter0Inline(Lookup *l, ExecutionEngine *engine);
+ static ReturnedValue globalGetter0MemberData(Lookup *l, ExecutionEngine *engine);
static ReturnedValue globalGetter1(Lookup *l, ExecutionEngine *engine);
static ReturnedValue globalGetter2(Lookup *l, ExecutionEngine *engine);
static ReturnedValue globalGetterAccessor0(Lookup *l, ExecutionEngine *engine);
@@ -130,6 +137,7 @@ struct Lookup {
static void setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static void setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static void setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+ static void setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static void setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static void setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static void setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp
index 1b43fd86e8..e00eaa0d9b 100644
--- a/src/qml/jsruntime/qv4managed.cpp
+++ b/src/qml/jsruntime/qv4managed.cpp
@@ -48,6 +48,8 @@ const VTable Managed::static_vtbl =
{
0,
0,
+ 0,
+ 0,
Managed::IsExecutionContext,
Managed::IsString,
Managed::IsObject,
diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h
index f97771831c..40dfc244ea 100644
--- a/src/qml/jsruntime/qv4managed_p.h
+++ b/src/qml/jsruntime/qv4managed_p.h
@@ -52,6 +52,7 @@
#include "qv4global_p.h"
#include "qv4value_p.h"
+#include "qv4enginebase_p.h"
#include <private/qv4heap_p.h>
#include <private/qv4writebarrier_p.h>
@@ -132,6 +133,9 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
{ \
parentVTable, \
markTable, \
+ (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
+ (sizeof(classname::Data) + (std::is_same<classname, Object>::value ? 2*sizeof(QV4::Value) : 0) + QV4::Chunk::SlotSize - 1)/QV4::Chunk::SlotSize*QV4::Chunk::SlotSize/sizeof(QV4::Value) \
+ - (sizeof(classname::Data) + sizeof(QV4::Value) - 1)/sizeof(QV4::Value), \
classname::IsExecutionContext, \
classname::IsString, \
classname::IsObject, \
@@ -151,6 +155,10 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON \
const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname, 0) \
QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
+#define V4_INTERNALCLASS(c) \
+ static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \
+ { return e->internalClasses[QV4::EngineBase::Class_##c]; }
+
struct Q_QML_PRIVATE_EXPORT Managed : Value
{
V4_MANAGED_ITSELF(Base, Managed)
@@ -193,6 +201,9 @@ public:
};
Q_MANAGED_TYPE(Invalid)
+ InternalClass *internalClass() const { return d()->internalClass; }
+ inline ExecutionEngine *engine() const { return internalClass()->engine; }
+
bool isListType() const { return d()->vtable()->type == Type_QmlSequence; }
bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; }
diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h
index fbe66757e0..bae524d088 100644
--- a/src/qml/jsruntime/qv4memberdata_p.h
+++ b/src/qml/jsruntime/qv4memberdata_p.h
@@ -72,23 +72,24 @@ V4_ASSERT_IS_TRIVIAL(MemberData)
struct MemberData : Managed
{
V4_MANAGED(MemberData, Managed)
+ V4_INTERNALCLASS(MemberData)
struct Index {
- Heap::MemberData *memberData;
- uint index;
+ Heap::Base *base;
+ Value *slot;
- void set(ExecutionEngine *e, Value newVal) {
- memberData->values.set(e, index, newVal);
+ void set(EngineBase *e, Value newVal) {
+ WriteBarrier::write(e, base, slot, newVal);
}
- const Value *operator->() const { return &memberData->values[index]; }
- const Value &operator*() const { return memberData->values[index]; }
- bool isNull() const { return !memberData; }
+ const Value *operator->() const { return slot; }
+ const Value &operator*() const { return *slot; }
+ bool isNull() const { return !slot; }
};
const Value &operator[] (uint idx) const { return d()->values[idx]; }
const Value *data() const { return d()->values.data(); }
- void set(ExecutionEngine *e, uint index, Value v) { d()->values.set(e, index, v); }
- void set(ExecutionEngine *e, uint index, Heap::Base *b) { d()->values.set(e, index, b); }
+ void set(EngineBase *e, uint index, Value v) { d()->values.set(e, index, v); }
+ void set(EngineBase *e, uint index, Heap::Base *b) { d()->values.set(e, index, b); }
inline uint size() const { return d()->values.size; }
diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h
index e18267c50c..0bc2cd8c65 100644
--- a/src/qml/jsruntime/qv4numberobject_p.h
+++ b/src/qml/jsruntime/qv4numberobject_p.h
@@ -85,6 +85,7 @@ struct NumberCtor: FunctionObject
struct NumberPrototype: NumberObject
{
+ V4_PROTOTYPE(objectPrototype)
void init(ExecutionEngine *engine, Object *ctor);
static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData);
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index d400c2ae64..94d5a74fe5 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -61,9 +61,14 @@ DEFINE_OBJECT_VTABLE(Object);
void Object::setInternalClass(InternalClass *ic)
{
d()->internalClass = ic;
+ Q_ASSERT(ic && ic->vtable);
+ uint nInline = d()->vtable()->nInlineProperties;
+ if (ic->size <= nInline)
+ return;
bool hasMD = d()->memberData != nullptr;
- if ((!hasMD && ic->size) || (hasMD && d()->memberData->values.size < ic->size))
- d()->memberData.set(engine(), MemberData::allocate(ic->engine, ic->size, d()->memberData));
+ uint requiredSize = ic->size - nInline;
+ if (!(hasMD && requiredSize) || (hasMD && d()->memberData->values.size < requiredSize))
+ d()->memberData.set(ic->engine, MemberData::allocate(ic->engine, requiredSize, d()->memberData));
}
void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) const
@@ -83,13 +88,14 @@ void Object::setProperty(uint index, const Property *p)
bool Object::setPrototype(Object *proto)
{
- Heap::Object *pp = proto ? proto->d() : 0;
+ Heap::Object *p = proto ? proto->d() : 0;
+ Heap::Object *pp = p;
while (pp) {
if (pp == d())
return false;
- pp = pp->prototype;
+ pp = pp->prototype();
}
- d()->prototype.set(engine(), proto ? proto->d() : 0);
+ setInternalClass(internalClass()->changePrototype(p));
return true;
}
@@ -150,17 +156,6 @@ void Object::defineDefaultProperty(const QString &name, const Value &value)
defineDefaultProperty(s, value);
}
-void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount)
-{
- ExecutionEngine *e = engine();
- Scope scope(e);
- ScopedString s(scope, e->newIdentifier(name));
- ExecutionContext *global = e->rootContext();
- ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code));
- function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount));
- defineDefaultProperty(s, function);
-}
-
void Object::defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount)
{
ExecutionEngine *e = engine();
@@ -172,16 +167,6 @@ void Object::defineDefaultProperty(const QString &name, void (*code)(const Built
defineDefaultProperty(s, function);
}
-void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount)
-{
- ExecutionEngine *e = engine();
- Scope scope(e);
- ExecutionContext *global = e->rootContext();
- ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code));
- function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount));
- defineDefaultProperty(name, function);
-}
-
void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount)
{
ExecutionEngine *e = engine();
@@ -192,25 +177,6 @@ void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunct
defineDefaultProperty(name, function);
}
-void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *))
-{
- ExecutionEngine *e = engine();
- Scope scope(e);
- ScopedString s(scope, e->newIdentifier(name));
- defineAccessorProperty(s, getter, setter);
-}
-
-void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *))
-{
- ExecutionEngine *v4 = engine();
- QV4::Scope scope(v4);
- ScopedProperty p(scope);
- ExecutionContext *global = v4->rootContext();
- p->setGetter(ScopedFunctionObject(scope, (getter ? BuiltinFunction::create(global, name, getter) : 0)));
- p->setSetter(ScopedFunctionObject(scope, (setter ? BuiltinFunction::create(global, name, setter) : 0)));
- insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
-}
-
void Object::defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *),
void (*setter)(const BuiltinFunction *, Scope &, CallData *))
{
@@ -258,6 +224,18 @@ void Object::defineReadonlyConfigurableProperty(String *name, const Value &value
insertMember(name, value, Attr_ReadOnly_ButConfigurable);
}
+void Object::markObjects(Heap::Base *b, MarkStack *stack)
+{
+ Heap::Object *o = static_cast<Heap::Object *>(b);
+ uint nInline = o->vtable()->nInlineProperties;
+ Value *v = reinterpret_cast<Value *>(o) + o->vtable()->inlinePropertyOffset;
+ const Value *end = v + nInline;
+ while (v < end) {
+ v->mark(stack);
+ ++v;
+ }
+}
+
void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes)
{
uint idx;
@@ -278,7 +256,10 @@ void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p
if (idx != UINT_MAX)
return getOwnProperty(idx, attrs, p);
- uint member = internalClass()->find(name);
+ name->makeIdentifier();
+ Identifier *id = name->identifier();
+
+ uint member = internalClass()->find(id);
if (member < UINT_MAX) {
*attrs = internalClass()->propertyData[member];
if (p) {
@@ -317,15 +298,18 @@ MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *att
{
Q_ASSERT(name->asArrayIndex() == UINT_MAX);
+ name->makeIdentifier();
+ Identifier *id = name->identifier();
+
Heap::Object *o = d();
while (o) {
- uint idx = o->internalClass->find(name);
+ uint idx = o->internalClass->find(id);
if (idx < UINT_MAX) {
*attrs = o->internalClass->propertyData[idx];
- return MemberData::Index{ o->memberData, attrs->isAccessor() ? idx + SetterOffset : idx };
+ return o->writablePropertyData(attrs->isAccessor() ? idx + SetterOffset : idx );
}
- o = o->prototype;
+ o = o->prototype();
}
*attrs = Attr_Invalid;
return { 0, 0 };
@@ -350,7 +334,7 @@ ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs)
return { reinterpret_cast<Heap::ArrayData *>(0x1), 0 };
}
}
- o = o->prototype;
+ o = o->prototype();
}
*attrs = Attr_Invalid;
return { 0, 0 };
@@ -394,7 +378,10 @@ bool Object::hasOwnProperty(String *name) const
if (idx != UINT_MAX)
return hasOwnProperty(idx);
- if (internalClass()->find(name) < UINT_MAX)
+ name->makeIdentifier();
+ Identifier *id = name->identifier();
+
+ if (internalClass()->find(id) < UINT_MAX)
return true;
if (!query(name).isEmpty())
return true;
@@ -451,8 +438,11 @@ PropertyAttributes Object::query(const Managed *m, String *name)
if (idx != UINT_MAX)
return queryIndexed(m, idx);
+ name->makeIdentifier();
+ Identifier *id = name->identifier();
+
const Object *o = static_cast<const Object *>(m);
- idx = o->internalClass()->find(name);
+ idx = o->internalClass()->find(id);
if (idx < UINT_MAX)
return o->internalClass()->propertyData[idx];
@@ -488,9 +478,18 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l)
PropertyAttributes attrs;
ReturnedValue v = l->lookup(o, &attrs);
if (v != Primitive::emptyValue().asReturnedValue()) {
+ l->proto = l->classList[0]->prototype;
if (attrs.isData()) {
- if (l->level == 0)
- l->getter = Lookup::getter0;
+ Q_ASSERT(l->classList[0] == o->internalClass());
+ if (l->level == 0) {
+ uint nInline = o->d()->vtable()->nInlineProperties;
+ if (l->index < nInline)
+ l->getter = Lookup::getter0Inline;
+ else {
+ l->index -= nInline;
+ l->getter = Lookup::getter0MemberData;
+ }
+ }
else if (l->level == 1)
l->getter = Lookup::getter1;
else if (l->level == 2)
@@ -525,7 +524,7 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value)
if (idx != UINT_MAX && o->internalClass()->propertyData[idx].isData() && o->internalClass()->propertyData[idx].isWritable()) {
l->classList[0] = o->internalClass();
l->index = idx;
- l->setter = Lookup::setter0;
+ l->setter = idx < o->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0;
o->setProperty(idx, value);
return;
}
@@ -642,12 +641,13 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const
if (idx != UINT_MAX)
return getIndexed(idx, hasProperty);
- Scope scope(engine());
- name->makeIdentifier(scope.engine);
+ name->makeIdentifier();
+ Identifier *id = name->identifier();
+ Scope scope(engine());
ScopedObject o(scope, this);
while (o) {
- uint idx = o->internalClass()->find(name);
+ uint idx = o->internalClass()->find(id);
if (idx < UINT_MAX) {
if (hasProperty)
*hasProperty = true;
@@ -709,14 +709,15 @@ bool Object::internalPut(String *name, const Value &value)
if (idx != UINT_MAX)
return putIndexed(idx, value);
- name->makeIdentifier(engine);
+ name->makeIdentifier();
+ Identifier *id = name->identifier();
MemberData::Index memberIndex{0, 0};
- uint member = internalClass()->find(name);
+ uint member = internalClass()->find(id);
PropertyAttributes attrs;
if (member < UINT_MAX) {
attrs = internalClass()->propertyData[member];
- memberIndex = { d()->memberData, (attrs.isAccessor() ? member + SetterOffset : member) };
+ memberIndex = d()->writablePropertyData(attrs.isAccessor() ? member + SetterOffset : member);
}
// clause 1
@@ -869,9 +870,9 @@ bool Object::internalDeleteProperty(String *name)
if (idx != UINT_MAX)
return deleteIndexedProperty(idx);
- name->makeIdentifier(engine());
+ name->makeIdentifier();
- uint memberIdx = internalClass()->find(name);
+ uint memberIdx = internalClass()->find(name->identifier());
if (memberIdx != UINT_MAX) {
if (internalClass()->propertyData[memberIdx].isConfigurable()) {
InternalClass::removeMember(this, name->identifier());
@@ -908,7 +909,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const
return __defineOwnProperty__(engine, idx, p, attrs);
Scope scope(engine);
- name->makeIdentifier(scope.engine);
+ name->makeIdentifier();
uint memberIndex;
@@ -942,7 +943,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const
}
// Clause 1
- memberIndex = internalClass()->find(name);
+ memberIndex = internalClass()->find(name->identifier());
if (memberIndex == UINT_MAX) {
// clause 3
@@ -1188,7 +1189,7 @@ ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var)
// 15.3.5.3, 4
while (v) {
// 15.3.5.3, 4, a
- v = v->prototype;
+ v = v->prototype();
// 15.3.5.3, 4, b
if (!v)
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index df9d68525d..066a93cc61 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -68,8 +68,6 @@ struct BuiltinFunction;
namespace Heap {
#define ObjectMembers(class, Member) \
- Member(class, NoMark, InternalClass *, internalClass) \
- Member(class, Pointer, Object *, prototype) \
Member(class, Pointer, MemberData *, memberData) \
Member(class, Pointer, ArrayData *, arrayData)
@@ -78,12 +76,59 @@ DECLARE_HEAP_OBJECT(Object, Base) {
void init() { Base::init(); }
void destroy() { Base::destroy(); }
- const Value *propertyData(uint index) const { return memberData->values.data() + index; }
- void setProperty(ExecutionEngine *e, uint index, Value v) const { memberData->values.set(e, index, v); }
- void setProperty(ExecutionEngine *e, uint index, Heap::Base *b) const { memberData->values.set(e, index, b); }
+ const Value *inlinePropertyData(uint index) const {
+ Q_ASSERT(index < vtable()->nInlineProperties);
+ return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index;
+ }
+ void setInlineProperty(ExecutionEngine *e, uint index, Value v) {
+ Q_ASSERT(index < vtable()->nInlineProperties);
+ Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index;
+ WriteBarrier::write(e, this, prop, v);
+ }
+ void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) {
+ Q_ASSERT(index < vtable()->nInlineProperties);
+ Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index;
+ WriteBarrier::write(e, this, prop, b);
+ }
+
+ QV4::MemberData::Index writablePropertyData(uint index) {
+ uint nInline = vtable()->nInlineProperties;
+ if (index < nInline)
+ return { this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index};
+ index -= nInline;
+ return { memberData, memberData->values.values + index };
+ }
+
+ const Value *propertyData(uint index) const {
+ uint nInline = vtable()->nInlineProperties;
+ if (index < nInline)
+ return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index;
+ index -= nInline;
+ return memberData->values.data() + index;
+ }
+ void setProperty(ExecutionEngine *e, uint index, Value v) {
+ uint nInline = vtable()->nInlineProperties;
+ if (index < nInline) {
+ setInlineProperty(e, index, v);
+ return;
+ }
+ index -= nInline;
+ memberData->values.set(e, index, v);
+ }
+ void setProperty(ExecutionEngine *e, uint index, Heap::Base *b) {
+ uint nInline = vtable()->nInlineProperties;
+ if (index < nInline) {
+ setInlineProperty(e, index, b);
+ return;
+ }
+ index -= nInline;
+ memberData->values.set(e, index, b);
+ }
+
+ Heap::Object *prototype() const { return internalClass->prototype; }
};
-Q_STATIC_ASSERT(Object::markTable == ((2 << 4) | (2 << 6) | (2 << 8)));
+Q_STATIC_ASSERT(Object::markTable == ((2 << 2) | (2 << 4)));
}
@@ -122,9 +167,6 @@ Q_STATIC_ASSERT(Object::markTable == ((2 << 4) | (2 << 6) | (2 << 8)));
V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); \
static Q_CONSTEXPR quint64 markTable = QV4::Heap::DataClass::markTable;
-#define V4_INTERNALCLASS(c) \
- static QV4::InternalClass *defaultInternalClass(QV4::ExecutionEngine *e) \
- { return e->c; }
#define V4_PROTOTYPE(p) \
static QV4::Object *defaultPrototype(QV4::ExecutionEngine *e) \
{ return e->p(); }
@@ -183,7 +225,7 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
struct Q_QML_EXPORT Object: Managed {
V4_OBJECT2(Object, Object)
Q_MANAGED_TYPE(Object)
- V4_INTERNALCLASS(emptyClass)
+ V4_INTERNALCLASS(Object)
V4_PROTOTYPE(objectPrototype)
enum {
@@ -192,7 +234,6 @@ struct Q_QML_EXPORT Object: Managed {
SetterOffset = 1
};
- InternalClass *internalClass() const { return d()->internalClass; }
void setInternalClass(InternalClass *ic);
const Value *propertyData(uint index) const { return d()->propertyData(index); }
@@ -208,7 +249,7 @@ struct Q_QML_EXPORT Object: Managed {
void setProperty(ExecutionEngine *engine, uint index, Heap::Base *b) const { d()->setProperty(engine, index, b); }
const ObjectVTable *vtable() const { return reinterpret_cast<const ObjectVTable *>(d()->vtable()); }
- Heap::Object *prototype() const { return d()->prototype; }
+ Heap::Object *prototype() const { return d()->prototype(); }
bool setPrototype(Object *proto);
void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = 0);
@@ -246,12 +287,8 @@ struct Q_QML_EXPORT Object: Managed {
insertMember(name, value, Attr_Data|Attr_NotEnumerable);
}
void defineDefaultProperty(const QString &name, const Value &value);
- void defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount = 0);
void defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0);
- void defineDefaultProperty(String *name, ReturnedValue (*code)(CallContext *), int argumentCount = 0);
void defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0);
- void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *));
- void defineAccessorProperty(String *name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *));
void defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *),
void (*setter)(const BuiltinFunction *, Scope &, CallData *));
void defineAccessorProperty(String *name, void (*getter)(const BuiltinFunction *, Scope &, CallData *),
@@ -272,8 +309,6 @@ struct Q_QML_EXPORT Object: Managed {
}
void insertMember(String *s, const Property *p, PropertyAttributes attributes);
- inline ExecutionEngine *engine() const { return internalClass()->engine; }
-
bool isExtensible() const { return d()->internalClass->extensible; }
// Array handling
@@ -424,6 +459,7 @@ protected:
static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
static uint getLength(const Managed *m);
static ReturnedValue instanceOf(const Object *typeObject, const Value &var);
+ static void markObjects(Heap::Base *, MarkStack *);
private:
ReturnedValue internalGet(String *name, bool *hasProperty) const;
@@ -498,7 +534,7 @@ struct NumberObject: Object {
struct ArrayObject: Object {
V4_OBJECT2(ArrayObject, Object)
Q_MANAGED_TYPE(ArrayObject)
- V4_INTERNALCLASS(arrayClass)
+ V4_INTERNALCLASS(ArrayObject)
V4_PROTOTYPE(arrayPrototype)
void init(ExecutionEngine *engine);
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index c08dd7baa3..61d785066f 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -297,15 +297,15 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml)
{
- Heap::ExecutionContext::init(outerContext->engine(), Heap::ExecutionContext::Type_QmlContext);
- outer.set(engine, outerContext->d());
+ Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext);
+ outer.set(internalClass->engine, outerContext->d());
strictMode = false;
callData = outer->callData;
lookups = outer->lookups;
constantTable = outer->constantTable;
compilationUnit = outer->compilationUnit;
- this->qml.set(engine, qml->d());
+ this->qml.set(internalClass->engine, qml->d());
}
Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, const QUrl &source, Value *sendFunction)
@@ -327,7 +327,8 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons
qml->QV4::Object::put(QV4::ScopedString(scope, scope.engine->newString(QStringLiteral("WorkerScript"))), api);
qml->setReadOnly(true);
- Heap::QmlContext *c = parent->d()->engine->memoryManager->alloc<QmlContext>(parent, qml);
+ Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml);
+ Q_ASSERT(c->vtable() == staticVTable());
return c;
}
@@ -336,7 +337,8 @@ Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData *
Scope scope(parent);
Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, scopeObject));
- Heap::QmlContext *c = parent->d()->engine->memoryManager->alloc<QmlContext>(parent, qml);
+ Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml);
+ Q_ASSERT(c->vtable() == staticVTable());
return c;
}
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index e4fb54f0ff..91650f16e2 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -75,10 +75,13 @@
#include <QtCore/qatomic.h>
#include <QtCore/qmetaobject.h>
#include <QtCore/qabstractitemmodel.h>
+#include <QtCore/qloggingcategory.h>
#include <vector>
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(lcBindingRemoval, "qt.qml.binding.removal", QtWarningMsg)
+
// The code in this file does not violate strict aliasing, but GCC thinks it does
// so turn off the warnings for us to have a clean build
QT_WARNING_DISABLE_GCC("-Wstrict-aliasing")
@@ -458,10 +461,23 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP
}
}
- if (newBinding)
+ if (newBinding) {
QQmlPropertyPrivate::setBinding(newBinding);
- else
+ } else {
+ if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
+ if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) {
+ Q_ASSERT(!binding->isValueTypeProxy());
+ const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
+ const auto stackFrame = engine->currentStackFrame();
+ qCInfo(lcBindingRemoval,
+ "Overwriting binding on %s::%s at %s:%d that was initially bound at %s",
+ object->metaObject()->className(), qPrintable(property->name(object)),
+ qPrintable(stackFrame.source), stackFrame.line,
+ qPrintable(qmlBinding->expressionIdentifier()));
+ }
+ }
QQmlPropertyPrivate::removeBinding(object, QQmlPropertyIndex(property->coreIndex()));
+ }
if (!newBinding && property->isVarProperty()) {
// allow assignment of "special" values (null, undefined, function) to var properties
@@ -1044,7 +1060,6 @@ void QObjectWrapper::destroyObject(bool lastCall)
}
}
- h->internalClass = 0;
h->~Data();
}
@@ -1797,7 +1812,7 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, scope->d()->engine->memoryManager->allocObject<QObjectMethod>(scope));
+ Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope));
method->d()->setObject(object);
if (QQmlData *ddata = QQmlData::get(object))
@@ -1848,7 +1863,7 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) co
result = QLatin1String("null");
}
- return ctx->d()->engine->newString(result)->asReturnedValue();
+ return ctx->engine()->newString(result)->asReturnedValue();
}
QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const
diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h
index 348af0fb14..7ab12fe245 100644
--- a/src/qml/jsruntime/qv4regexp_p.h
+++ b/src/qml/jsruntime/qv4regexp_p.h
@@ -100,6 +100,7 @@ struct RegExp : public Managed
V4_MANAGED(RegExp, Managed)
Q_MANAGED_TYPE(RegExp)
V4_NEEDS_DESTROY
+ V4_INTERNALCLASS(RegExp)
QString pattern() const { return *d()->pattern; }
JSC::Yarr::BytecodePattern *byteCode() { return d()->byteCode; }
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index 85e37ebe82..735951e085 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -356,7 +356,7 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat
}
// fill in result data
- ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->regExpExecArrayClass, scope.engine->arrayPrototype()));
+ ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses[EngineBase::Class_RegExpExecArray], scope.engine->arrayPrototype()));
int len = r->value()->captureCount();
array->arrayReserve(len);
ScopedValue v(scope);
diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h
index 0fcfe93135..6f54a4ab92 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -103,7 +103,7 @@ DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) {
struct RegExpObject: Object {
V4_OBJECT2(RegExpObject, Object)
Q_MANAGED_TYPE(RegExpObject)
- V4_INTERNALCLASS(regExpObjectClass)
+ V4_INTERNALCLASS(RegExpObject)
V4_PROTOTYPE(regExpPrototype)
// needs to be compatible with the flags in qv4jsir_p.h
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 610db8ec00..9737a18812 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -554,7 +554,7 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu
if (!sright->d()->length())
return sleft->asReturnedValue();
MemoryManager *mm = engine->memoryManager;
- return (mm->alloc<String>(mm, sleft->d(), sright->d()))->asReturnedValue();
+ return (mm->alloc<String>(sleft->d(), sright->d()))->asReturnedValue();
}
double x = RuntimeHelpers::toNumber(pleft);
double y = RuntimeHelpers::toNumber(pright);
@@ -586,7 +586,7 @@ QV4::ReturnedValue Runtime::method_addString(ExecutionEngine *engine, const Valu
if (!sright->d()->length())
return pleft->asReturnedValue();
MemoryManager *mm = engine->memoryManager;
- return (mm->alloc<String>(mm, sleft->d(), sright->d()))->asReturnedValue();
+ return (mm->alloc<String>(sleft->d(), sright->d()))->asReturnedValue();
}
void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)
@@ -599,41 +599,53 @@ void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, i
o->put(name, value);
}
-ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index)
+static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx)
{
+ Q_ASSERT(idx < UINT_MAX);
Scope scope(engine);
- uint idx = index.asArrayIndex();
ScopedObject o(scope, object);
if (!o) {
- if (idx < UINT_MAX) {
- if (const String *str = object.as<String>()) {
- if (idx >= (uint)str->toQString().length()) {
- return Encode::undefined();
- }
- const QString s = str->toQString().mid(idx, 1);
- return scope.engine->newString(s)->asReturnedValue();
+ if (const String *str = object.as<String>()) {
+ if (idx >= (uint)str->toQString().length()) {
+ return Encode::undefined();
}
+ const QString s = str->toQString().mid(idx, 1);
+ return scope.engine->newString(s)->asReturnedValue();
}
if (object.isNullOrUndefined()) {
- QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow());
+ QString message = QStringLiteral("Cannot read property '%1' of %2").arg(idx).arg(object.toQStringNoThrow());
return engine->throwTypeError(message);
}
o = RuntimeHelpers::convertToObject(scope.engine, object);
- if (!o) // type error
- return Encode::undefined();
+ Q_ASSERT(!!o); // can't fail as null/undefined is covered above
+ }
+
+ if (o->arrayData() && !o->arrayData()->attrs) {
+ ScopedValue v(scope, o->arrayData()->get(idx));
+ if (!v->isEmpty())
+ return v->asReturnedValue();
}
- if (idx < UINT_MAX) {
- if (o->arrayData() && !o->arrayData()->attrs) {
- ScopedValue v(scope, o->arrayData()->get(idx));
- if (!v->isEmpty())
- return v->asReturnedValue();
+ return o->getIndexed(idx);
+}
+
+static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, const Value &object, const Value &index)
+{
+ Q_ASSERT(index.asArrayIndex() == UINT_MAX);
+ Scope scope(engine);
+
+ ScopedObject o(scope, object);
+ if (!o) {
+ if (object.isNullOrUndefined()) {
+ QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow());
+ return engine->throwTypeError(message);
}
- return o->getIndexed(idx);
+ o = RuntimeHelpers::convertToObject(scope.engine, object);
+ Q_ASSERT(!!o); // can't fail as null/undefined is covered above
}
ScopedString name(scope, index.toString(engine));
@@ -642,18 +654,39 @@ ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &o
return o->get(name);
}
-void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
+ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index)
+{
+ uint idx;
+ if (index.asArrayIndex(idx)) {
+ if (Heap::Base *b = object.heapObject()) {
+ if (b->vtable()->isObject) {
+ Heap::Object *o = static_cast<Heap::Object *>(b);
+ if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
+ Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
+ if (idx < s->values.size)
+ if (!s->data(idx).isEmpty())
+ return s->data(idx).asReturnedValue();
+ }
+ }
+ }
+ return getElementIntFallback(engine, object, idx);
+ }
+
+ return getElementFallback(engine, object, index);
+}
+
+static Q_NEVER_INLINE void setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
{
Scope scope(engine);
ScopedObject o(scope, object.toObject(engine));
- if (scope.engine->hasException)
+ if (engine->hasException)
return;
- uint idx = index.asArrayIndex();
- if (idx < UINT_MAX) {
- if (o->arrayType() == Heap::ArrayData::Simple) {
- Heap::SimpleArrayData *s = static_cast<Heap::SimpleArrayData *>(o->arrayData());
- if (s && idx < s->values.size && !s->data(idx).isEmpty()) {
+ uint idx;
+ if (index.asArrayIndex(idx)) {
+ if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) {
+ Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>();
+ if (idx < s->values.size) {
s->setData(engine, idx, value);
return;
}
@@ -666,6 +699,27 @@ void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, co
o->put(name, value);
}
+void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)
+{
+ uint idx;
+ if (index.asArrayIndex(idx)) {
+ if (Heap::Base *b = object.heapObject()) {
+ if (b->vtable()->isObject) {
+ Heap::Object *o = static_cast<Heap::Object *>(b);
+ if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
+ Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
+ if (idx < s->values.size) {
+ s->setData(engine, idx, value);
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ return setElementFallback(engine, object, index, value);
+}
+
ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in)
{
Scope scope(engine);
@@ -1354,7 +1408,7 @@ QV4::ReturnedValue Runtime::method_setupArgumentsObject(ExecutionEngine *engine)
{
Q_ASSERT(engine->current->type == Heap::ExecutionContext::Type_CallContext);
QV4::CallContext *c = static_cast<QV4::CallContext *>(engine->currentContext);
- QV4::InternalClass *ic = c->d()->strictMode ? engine->strictArgumentsObjectClass : engine->argumentsObjectClass;
+ QV4::InternalClass *ic = engine->internalClasses[c->d()->strictMode ? EngineBase::Class_StrictArgumentsObject : EngineBase::Class_ArgumentsObject];
return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), c)->asReturnedValue();
}
@@ -1652,7 +1706,9 @@ ReturnedValue Runtime::method_div(const Value &left, const Value &right)
if (Value::integerCompatible(left, right)) {
int lval = left.integerValue();
int rval = right.integerValue();
- if (rval != 0 && (lval % rval == 0))
+ if (rval != 0 // division by zero should result in a NaN
+ && (lval % rval == 0) // fractions can't be stored in an int
+ && !(lval == 0 && rval < 0)) // 0 / -something results in -0.0
return Encode(int(lval / rval));
else
return Encode(double(lval) / rval);
diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h
index bc882bbd95..04a0c74133 100644
--- a/src/qml/jsruntime/qv4scopedvalue_p.h
+++ b/src/qml/jsruntime/qv4scopedvalue_p.h
@@ -102,7 +102,7 @@ struct ScopedValue;
struct Scope {
inline Scope(ExecutionContext *ctx)
- : engine(ctx->d()->engine)
+ : engine(ctx->engine())
, mark(engine->jsStackTop)
, result(*engine->jsAlloca(1))
{
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index dcab34d092..2b8d1ea716 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -65,6 +65,7 @@ namespace QV4 {
struct SequencePrototype : public QV4::Object
{
+ V4_PROTOTYPE(arrayPrototype)
void init();
static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData)
diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp
index 71f85c2d71..c0183a46a7 100644
--- a/src/qml/jsruntime/qv4string.cpp
+++ b/src/qml/jsruntime/qv4string.cpp
@@ -75,10 +75,9 @@ bool String::isEqualTo(Managed *t, Managed *o)
}
-void Heap::String::init(MemoryManager *mm, const QString &t)
+void Heap::String::init(const QString &t)
{
Base::init();
- this->mm = mm;
subtype = String::StringType_Unknown;
@@ -90,10 +89,9 @@ void Heap::String::init(MemoryManager *mm, const QString &t)
len = text->size;
}
-void Heap::String::init(MemoryManager *mm, String *l, String *r)
+void Heap::String::init(String *l, String *r)
{
Base::init();
- this->mm = mm;
subtype = String::StringType_Unknown;
@@ -116,7 +114,7 @@ void Heap::String::init(MemoryManager *mm, String *l, String *r)
void Heap::String::destroy() {
if (!largestSubLength) {
- mm->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar));
+ internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar));
if (!text->ref.deref())
QStringData::deallocate(text);
}
@@ -141,12 +139,12 @@ uint String::toUInt(bool *ok) const
return UINT_MAX;
}
-void String::makeIdentifierImpl(ExecutionEngine *e) const
+void String::makeIdentifierImpl() const
{
if (d()->largestSubLength)
d()->simplifyString();
Q_ASSERT(!d()->largestSubLength);
- e->identifierTable->identifier(this);
+ engine()->identifierTable->identifier(this);
}
void Heap::String::simplifyString() const
@@ -161,7 +159,7 @@ void Heap::String::simplifyString() const
text->ref.ref();
identifier = 0;
largestSubLength = 0;
- mm->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar));
+ internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar));
}
void Heap::String::append(const String *data, QChar *ch)
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index 71e55cbcd4..2f34dd6139 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -53,6 +53,7 @@
#include <QtCore/qstring.h>
#include "qv4managed_p.h"
#include <QtCore/private/qnumeric_p.h>
+#include "qv4enginebase_p.h"
QT_BEGIN_NAMESPACE
@@ -71,8 +72,8 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
};
#ifndef V4_BOOTSTRAP
- void init(MemoryManager *mm, const QString &text);
- void init(MemoryManager *mm, String *l, String *n);
+ void init(const QString &text);
+ void init(String *l, String *n);
void destroy();
void simplifyString() const;
int length() const {
@@ -125,7 +126,6 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
mutable uint stringHash;
mutable uint largestSubLength;
uint len;
- MemoryManager *mm;
private:
static void append(const String *data, QChar *ch);
#endif
@@ -138,6 +138,7 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
#ifndef V4_BOOTSTRAP
V4_MANAGED(String, Managed)
Q_MANAGED_TYPE(String)
+ V4_INTERNALCLASS(String)
V4_NEEDS_DESTROY
enum {
IsString = true
@@ -174,13 +175,13 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
}
uint toUInt(bool *ok) const;
- void makeIdentifier(ExecutionEngine *e) const {
+ void makeIdentifier() const {
if (d()->identifier)
return;
- makeIdentifierImpl(e);
+ makeIdentifierImpl();
}
- void makeIdentifierImpl(ExecutionEngine *e) const;
+ void makeIdentifierImpl() const;
static uint createHashValue(const QChar *ch, int length, uint *subtype)
{
diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h
index 5ccee3335e..ce046f4844 100644
--- a/src/qml/jsruntime/qv4stringobject_p.h
+++ b/src/qml/jsruntime/qv4stringobject_p.h
@@ -86,7 +86,7 @@ struct StringCtor : FunctionObject {
struct StringObject: Object {
V4_OBJECT2(StringObject, Object)
Q_MANAGED_TYPE(StringObject)
- V4_INTERNALCLASS(stringClass)
+ V4_INTERNALCLASS(StringObject)
V4_PROTOTYPE(stringPrototype)
Heap::String *getIndex(uint index) const {
@@ -112,6 +112,7 @@ struct StringCtor: FunctionObject
struct StringPrototype: StringObject
{
+ V4_PROTOTYPE(objectPrototype)
void init(ExecutionEngine *engine, Object *ctor);
static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData);
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index a34a8922e1..fe27d7c2d2 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -372,7 +372,9 @@ void Heap::TypedArray::init(Type t)
Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t)
{
- return e->memoryManager->allocObject<TypedArray>(e->emptyClass, e->typedArrayPrototype + t, t);
+ QV4::InternalClass *ic = e->internalClasses[EngineBase::Class_Empty]->changeVTable(staticVTable());
+ ic = ic->changePrototype(e->typedArrayPrototype[t].d());
+ return e->memoryManager->allocObject<TypedArray>(ic, e->typedArrayPrototype + t, t);
}
ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty)
diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h
index 96786c8231..a472dfa607 100644
--- a/src/qml/jsruntime/qv4typedarray_p.h
+++ b/src/qml/jsruntime/qv4typedarray_p.h
@@ -149,6 +149,7 @@ struct TypedArrayCtor: FunctionObject
struct TypedArrayPrototype : Object
{
V4_OBJECT2(TypedArrayPrototype, Object)
+ V4_PROTOTYPE(objectPrototype)
void init(ExecutionEngine *engine, TypedArrayCtor *ctor);
diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h
index e281602bb5..a7c6fa320c 100644
--- a/src/qml/jsruntime/qv4variantobject_p.h
+++ b/src/qml/jsruntime/qv4variantobject_p.h
@@ -105,6 +105,7 @@ struct VariantObject : Object
struct VariantPrototype : VariantObject
{
public:
+ V4_PROTOTYPE(objectPrototype)
void init();
static void method_preserve(const BuiltinFunction *, Scope &scope, CallData *callData);
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index e16df8dc60..7293630924 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -172,7 +172,7 @@ static QV4::Function *qt_v4ExtractFunction(QV4::ExecutionContext *context)
if (QV4::Function *function = context->getFunction())
return function;
else
- return context->d()->engine->globalCode;
+ return context->engine()->globalCode;
}
static void qt_v4TriggerBreakpoint(const Breakpoint &bp, QV4::Function *function)
@@ -304,7 +304,7 @@ using namespace QV4::Moth;
# define MOTH_END_INSTR(I) } \
genericInstr = reinterpret_cast<const Instr *>(code); \
- goto *genericInstr->common.code; \
+ goto *jumpTable[genericInstr->common.instructionType]; \
#else
@@ -362,11 +362,7 @@ Param traceParam(const Param &param)
if (engine->hasException) \
goto catchException
-QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
-#ifdef MOTH_THREADED_INTERPRETER
- , void ***storeJumpTable
-#endif
- )
+QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code)
{
#ifdef DO_TRACE_INSTR
qDebug("Starting VME with context=%p and code=%p", context, code);
@@ -375,15 +371,11 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
qt_v4ResolvePendingBreakpointsHook();
#ifdef MOTH_THREADED_INTERPRETER
- if (storeJumpTable) {
#define MOTH_INSTR_ADDR(I, FMT) &&op_##I,
- static void *jumpTable[] = {
- FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR)
- };
+ static void *jumpTable[] = {
+ FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR)
+ };
#undef MOTH_INSTR_ADDR
- *storeJumpTable = jumpTable;
- return QV4::Primitive::undefinedValue().asReturnedValue();
- }
#endif
QV4::Value *stack = 0;
@@ -392,7 +384,6 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
const uchar *exceptionHandler = 0;
QV4::Scope scope(engine);
- QV4::ExecutionContext *context = engine->currentContext;
engine->current->lineNumber = -1;
#ifdef DO_TRACE_INSTR
@@ -402,7 +393,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
// setup lookup scopes
int scopeDepth = 0;
{
- QV4::Heap::ExecutionContext *scope = context->d();
+ QV4::Heap::ExecutionContext *scope = engine->current;
while (scope) {
++scopeDepth;
scope = scope->outer;
@@ -415,10 +406,10 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
};
Q_ALLOCA_VAR(Scopes, scopes, sizeof(Scopes)*(2 + 2*scopeDepth));
{
- scopes[0] = { const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(context->d()->compilationUnit)->constants), 0 };
+ scopes[0] = { const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->constants), 0 };
// stack gets setup in push instruction
scopes[1] = { 0, 0 };
- QV4::Heap::ExecutionContext *scope = context->d();
+ QV4::Heap::ExecutionContext *scope = engine->current;
int i = 0;
while (scope) {
if (scope->type == QV4::Heap::ExecutionContext::Type_SimpleCallContext) {
@@ -442,7 +433,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
for (;;) {
const Instr *genericInstr = reinterpret_cast<const Instr *>(code);
#ifdef MOTH_THREADED_INTERPRETER
- goto *genericInstr->common.code;
+ goto *jumpTable[genericInstr->common.instructionType];
#else
switch (genericInstr->common.instructionType) {
#endif
@@ -461,12 +452,12 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(LoadRuntimeString)
// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData());
- VALUE(instr.result) = context->d()->compilationUnit->runtimeStrings[instr.stringId];
+ VALUE(instr.result) = engine->current->compilationUnit->runtimeStrings[instr.stringId];
MOTH_END_INSTR(LoadRuntimeString)
MOTH_BEGIN_INSTR(LoadRegExp)
// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData());
- VALUE(instr.result) = static_cast<CompiledData::CompilationUnit*>(context->d()->compilationUnit)->runtimeRegularExpressions[instr.regExpId];
+ VALUE(instr.result) = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeRegularExpressions[instr.regExpId];
MOTH_END_INSTR(LoadRegExp)
MOTH_BEGIN_INSTR(LoadClosure)
@@ -479,7 +470,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(LoadName)
MOTH_BEGIN_INSTR(GetGlobalLookup)
- QV4::Lookup *l = context->d()->lookups + instr.index;
+ QV4::Lookup *l = engine->current->lookups + instr.index;
STOREVALUE(instr.result, l->globalGetter(l, engine));
MOTH_END_INSTR(GetGlobalLookup)
@@ -494,7 +485,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(LoadElement)
MOTH_BEGIN_INSTR(LoadElementLookup)
- QV4::Lookup *l = context->d()->lookups + instr.lookup;
+ QV4::Lookup *l = engine->current->lookups + instr.lookup;
STOREVALUE(instr.result, l->indexedGetter(l, engine, VALUE(instr.base), VALUE(instr.index)));
MOTH_END_INSTR(LoadElementLookup)
@@ -504,7 +495,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(StoreElement)
MOTH_BEGIN_INSTR(StoreElementLookup)
- QV4::Lookup *l = context->d()->lookups + instr.lookup;
+ QV4::Lookup *l = engine->current->lookups + instr.lookup;
l->indexedSetter(l, engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source));
CHECK_EXCEPTION;
MOTH_END_INSTR(StoreElementLookup)
@@ -514,7 +505,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
- QV4::Lookup *l = context->d()->lookups + instr.index;
+ QV4::Lookup *l = engine->current->lookups + instr.index;
STOREVALUE(instr.result, l->getter(l, engine, VALUE(instr.base)));
MOTH_END_INSTR(GetLookup)
@@ -524,7 +515,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(StoreProperty)
MOTH_BEGIN_INSTR(SetLookup)
- QV4::Lookup *l = context->d()->lookups + instr.index;
+ QV4::Lookup *l = engine->current->lookups + instr.index;
l->setter(l, engine, VALUE(instr.base), VALUE(instr.source));
CHECK_EXCEPTION;
MOTH_END_INSTR(SetLookup)
@@ -595,7 +586,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(CallValue)
MOTH_BEGIN_INSTR(CallProperty)
- TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData());
+ TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData());
Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
callData->tag = quint32(Value::ValueTypeInternal::Integer);
@@ -614,7 +605,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(CallPropertyLookup)
MOTH_BEGIN_INSTR(CallScopeObjectProperty)
- TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData());
+ TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData());
Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
callData->tag = quint32(Value::ValueTypeInternal::Integer);
@@ -624,7 +615,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_END_INSTR(CallScopeObjectProperty)
MOTH_BEGIN_INSTR(CallContextObjectProperty)
- TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData());
+ TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData());
Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize);
QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData);
callData->tag = quint32(Value::ValueTypeInternal::Integer);
@@ -675,18 +666,15 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope)
Runtime::method_pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name);
- context = engine->currentContext;
MOTH_END_INSTR(CallBuiltinPushCatchScope)
MOTH_BEGIN_INSTR(CallBuiltinPushScope)
Runtime::method_pushWithScope(VALUE(instr.arg), static_cast<QV4::NoThrowEngine*>(engine));
- context = engine->currentContext;
CHECK_EXCEPTION;
MOTH_END_INSTR(CallBuiltinPushScope)
MOTH_BEGIN_INSTR(CallBuiltinPopScope)
Runtime::method_popScope(static_cast<QV4::NoThrowEngine*>(engine));
- context = engine->currentContext;
MOTH_END_INSTR(CallBuiltinPopScope)
MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject)
@@ -926,22 +914,22 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
#ifndef QT_NO_QML_DEBUGGER
MOTH_BEGIN_INSTR(Debug)
engine->current->lineNumber = instr.lineNumber;
- QV4::Debugging::Debugger *debugger = context->engine()->debugger();
+ QV4::Debugging::Debugger *debugger = engine->debugger();
if (debugger && debugger->pauseAtNextOpportunity())
debugger->maybeBreakAtInstruction();
if (qt_v4IsDebugging)
- qt_v4CheckForBreak(context);
+ qt_v4CheckForBreak(engine->currentContext);
MOTH_END_INSTR(Debug)
MOTH_BEGIN_INSTR(Line)
engine->current->lineNumber = instr.lineNumber;
if (qt_v4IsDebugging)
- qt_v4CheckForBreak(context);
+ qt_v4CheckForBreak(engine->currentContext);
MOTH_END_INSTR(Line)
#endif // QT_NO_QML_DEBUGGER
MOTH_BEGIN_INSTR(LoadThis)
- VALUE(instr.result) = context->thisObject();
+ VALUE(instr.result) = engine->currentContext->thisObject();
MOTH_END_INSTR(LoadThis)
MOTH_BEGIN_INSTR(LoadQmlContext)
@@ -967,25 +955,13 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code
Q_ASSERT(false);
catchException:
- Q_ASSERT(context->engine()->hasException);
+ Q_ASSERT(engine->hasException);
if (!exceptionHandler)
return QV4::Encode::undefined();
code = exceptionHandler;
}
}
-#ifdef MOTH_THREADED_INTERPRETER
-void **VME::instructionJumpTable()
-{
- static void **jumpTable = 0;
- if (!jumpTable) {
- const uchar *code = 0;
- VME().run(0, code, &jumpTable);
- }
- return jumpTable;
-}
-#endif
-
QV4::ReturnedValue VME::exec(ExecutionEngine *engine, const uchar *code)
{
VME vme;
diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h
index f8893509d9..8d46207f2b 100644
--- a/src/qml/jsruntime/qv4vme_moth_p.h
+++ b/src/qml/jsruntime/qv4vme_moth_p.h
@@ -67,16 +67,8 @@ class VME
public:
static QV4::ReturnedValue exec(QV4::ExecutionEngine *, const uchar *);
-#ifdef MOTH_THREADED_INTERPRETER
- static void **instructionJumpTable();
-#endif
-
private:
- QV4::ReturnedValue run(QV4::ExecutionEngine *, const uchar *code
-#ifdef MOTH_THREADED_INTERPRETER
- , void ***storeJumpTable = 0
-#endif
- );
+ QV4::ReturnedValue run(QV4::ExecutionEngine *, const uchar *code);
};
} // namespace Moth
diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h
index a38a938588..f00ce4283c 100644
--- a/src/qml/memory/qv4heap_p.h
+++ b/src/qml/memory/qv4heap_p.h
@@ -53,6 +53,7 @@
#include <QtCore/QString>
#include <private/qv4global_p.h>
#include <private/qv4mmdefs_p.h>
+#include <private/qv4internalclass_p.h>
#include <QSharedPointer>
// To check if Heap::Base::init is called (meaning, all subclasses did their init and called their
@@ -69,10 +70,14 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+struct InternalClass;
+
struct VTable
{
const VTable * const parent;
const quint64 markTable;
+ uint inlinePropertyOffset : 16;
+ uint nInlineProperties : 16;
uint isExecutionContext : 1;
uint isString : 1;
uint isObject : 1;
@@ -94,13 +99,12 @@ struct Q_QML_EXPORT Base {
static Q_CONSTEXPR quint64 markTable = 0;
- const VTable *vt;
+ InternalClass *internalClass;
inline ReturnedValue asReturnedValue() const;
inline void mark(QV4::MarkStack *markStack);
- void setVtable(const VTable *v) { vt = v; }
- const VTable *vtable() const { return vt; }
+ const VTable *vtable() const { return internalClass->vtable; }
inline bool isMarked() const {
const HeapItem *h = reinterpret_cast<const HeapItem *>(this);
Chunk *c = h->chunk();
@@ -175,7 +179,7 @@ V4_ASSERT_IS_TRIVIAL(Base)
// for a size/offset translation when cross-compiling between 32- and
// 64-bit.
Q_STATIC_ASSERT(std::is_standard_layout<Base>::value);
-Q_STATIC_ASSERT(offsetof(Base, vt) == 0);
+Q_STATIC_ASSERT(offsetof(Base, internalClass) == 0);
Q_STATIC_ASSERT(sizeof(Base) == QT_POINTER_SIZE);
}
diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp
index 64b07153cc..de97918fb0 100644
--- a/src/qml/memory/qv4mm.cpp
+++ b/src/qml/memory/qv4mm.cpp
@@ -348,8 +348,9 @@ static void increaseFreedCountForClass(const char *className)
(*freedObjectStatsGlobal())[className]++;
}
-void Chunk::sweep(ClassDestroyStatsCallback classCountPtr)
+bool Chunk::sweep(ClassDestroyStatsCallback classCountPtr)
{
+ bool hasUsedSlots = false;
SDUMP() << "sweeping chunk" << this;
HeapItem *o = realBase();
bool lastSlotFree = false;
@@ -395,6 +396,7 @@ void Chunk::sweep(ClassDestroyStatsCallback classCountPtr)
}
objectBitmap[i] = blackBitmap[i];
grayBitmap[i] = 0;
+ hasUsedSlots |= (blackBitmap[i] != 0);
extendsBitmap[i] = e;
lastSlotFree = !((objectBitmap[i]|extendsBitmap[i]) >> (sizeof(quintptr)*8 - 1));
SDUMP() << " new extends =" << binary(e);
@@ -403,6 +405,7 @@ void Chunk::sweep(ClassDestroyStatsCallback classCountPtr)
o += Chunk::Bits;
}
// DEBUG << "swept chunk" << this << "freed" << slotsFreed << "slots.";
+ return hasUsedSlots;
}
void Chunk::freeAll()
@@ -700,12 +703,21 @@ void BlockAllocator::sweep(ClassDestroyStatsCallback classCountPtr)
// qDebug() << "BlockAlloc: sweep";
usedSlotsAfterLastSweep = 0;
- for (auto c : chunks) {
- c->sweep(classCountPtr);
- c->sortIntoBins(freeBins, NumBins);
-// qDebug() << "used slots in chunk" << c << ":" << c->nUsedSlots();
- usedSlotsAfterLastSweep += c->nUsedSlots();
- }
+
+ auto isFree = [this, classCountPtr] (Chunk *c) {
+ bool isUsed = c->sweep(classCountPtr);
+
+ if (isUsed) {
+ c->sortIntoBins(freeBins, NumBins);
+ usedSlotsAfterLastSweep += c->nUsedSlots();
+ } else {
+ chunkAllocator->free(c);
+ }
+ return !isUsed;
+ };
+
+ auto newEnd = std::remove_if(chunks.begin(), chunks.end(), isFree);
+ chunks.erase(newEnd, chunks.end());
}
void BlockAllocator::freeAll()
@@ -922,13 +934,17 @@ Heap::Base *MemoryManager::allocData(std::size_t size)
return *m;
}
-Heap::Object *MemoryManager::allocObjectWithMemberData(std::size_t size, uint nMembers)
+Heap::Object *MemoryManager::allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers)
{
+ uint size = (vtable->nInlineProperties + vtable->inlinePropertyOffset)*sizeof(Value);
+ Q_ASSERT(!(size % sizeof(HeapItem)));
+
Heap::Object *o;
- if (!nMembers) {
+ if (nMembers <= vtable->nInlineProperties) {
o = static_cast<Heap::Object *>(allocData(size));
} else {
// Allocate both in one go through the block allocator
+ nMembers -= vtable->nInlineProperties;
std::size_t memberSize = align(sizeof(Heap::MemberData) + (nMembers - 1)*sizeof(Value));
size_t totalSize = size + memberSize;
Heap::MemberData *m;
@@ -947,7 +963,8 @@ Heap::Object *MemoryManager::allocObjectWithMemberData(std::size_t size, uint nM
Chunk::clearBit(c->extendsBitmap, index);
}
o->memberData.set(engine, m);
- m->setVtable(MemberData::staticVTable());
+ m->internalClass = engine->internalClasses[EngineBase::Class_MemberData];
+ Q_ASSERT(o->memberData->internalClass);
m->values.alloc = static_cast<uint>((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value));
m->values.size = o->memberData->values.alloc;
m->init();
@@ -1147,7 +1164,8 @@ void MemoryManager::runGC()
qDebug() << " Allocations since last GC" << allocationCount;
allocationCount = 0;
#endif
- qDebug() << "Allocated" << totalMem << "bytes in" << blockAllocator.chunks.size() << "chunks";
+ size_t oldChunks = blockAllocator.chunks.size();
+ qDebug() << "Allocated" << totalMem << "bytes in" << oldChunks << "chunks";
qDebug() << "Fragmented memory before GC" << (totalMem - usedBefore);
dumpBins(&blockAllocator);
@@ -1190,8 +1208,9 @@ void MemoryManager::runGC()
});
qDebug() << "Used memory before GC:" << usedBefore;
- qDebug() << "Used memory after GC :" << usedAfter;
- qDebug() << "Freed up bytes :" << (usedBefore - usedAfter);
+ qDebug() << "Used memory after GC:" << usedAfter;
+ qDebug() << "Freed up bytes :" << (usedBefore - usedAfter);
+ qDebug() << "Freed up chunks :" << (oldChunks - blockAllocator.chunks.size());
size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter;
if (lost)
qDebug() << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!";
diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h
index df7d09dce0..17957d0cd6 100644
--- a/src/qml/memory/qv4mm_p.h
+++ b/src/qml/memory/qv4mm_p.h
@@ -211,12 +211,13 @@ public:
Q_DECL_CONSTEXPR static inline std::size_t align(std::size_t size)
{ return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); }
- QV4::Heap::SimpleCallContext *allocSimpleCallContext(QV4::ExecutionEngine *v4)
+ QV4::Heap::CallContext *allocSimpleCallContext()
{
Heap::CallContext *ctxt = stackAllocator.allocate();
memset(ctxt, 0, sizeof(Heap::SimpleCallContext));
- ctxt->setVtable(QV4::SimpleCallContext::staticVTable());
- ctxt->init(v4);
+ ctxt->internalClass = SimpleCallContext::defaultInternalClass(engine);
+ Q_ASSERT(ctxt->internalClass && ctxt->internalClass->vtable);
+ ctxt->init();
return ctxt;
}
@@ -229,16 +230,20 @@ public:
V4_ASSERT_IS_TRIVIAL(typename ManagedType::Data)
size = align(size);
Heap::Base *o = allocData(size);
- o->setVtable(ManagedType::staticVTable());
+ InternalClass *ic = ManagedType::defaultInternalClass(engine);
+ ic = ic->changeVTable(ManagedType::staticVTable());
+ o->internalClass = ic;
+ Q_ASSERT(o->internalClass && o->internalClass->vtable);
return static_cast<typename ManagedType::Data *>(o);
}
template <typename ObjectType>
typename ObjectType::Data *allocateObject(InternalClass *ic)
{
- Heap::Object *o = allocObjectWithMemberData(align(sizeof(typename ObjectType::Data)), ic->size);
- o->setVtable(ObjectType::staticVTable());
+ Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size);
o->internalClass = ic;
+ Q_ASSERT(o->internalClass && o->internalClass->vtable);
+ Q_ASSERT(ic->vtable == ObjectType::staticVTable());
return static_cast<typename ObjectType::Data *>(o);
}
@@ -246,11 +251,12 @@ public:
typename ObjectType::Data *allocateObject()
{
InternalClass *ic = ObjectType::defaultInternalClass(engine);
- Heap::Object *o = allocObjectWithMemberData(align(sizeof(typename ObjectType::Data)), ic->size);
- o->setVtable(ObjectType::staticVTable());
- Object *prototype = ObjectType::defaultPrototype(engine);
+ ic = ic->changeVTable(ObjectType::staticVTable());
+ ic = ic->changePrototype(ObjectType::defaultPrototype(engine)->d());
+ Heap::Object *o = allocObjectWithMemberData(ObjectType::staticVTable(), ic->size);
o->internalClass = ic;
- o->prototype.set(engine, prototype->d());
+ Q_ASSERT(o->internalClass && o->internalClass->vtable);
+ Q_ASSERT(o->internalClass->prototype == ObjectType::defaultPrototype(engine)->d());
return static_cast<typename ObjectType::Data *>(o);
}
@@ -258,8 +264,9 @@ public:
typename ManagedType::Data *allocWithStringData(std::size_t unmanagedSize, Arg1 arg1)
{
typename ManagedType::Data *o = reinterpret_cast<typename ManagedType::Data *>(allocString(unmanagedSize));
- o->setVtable(ManagedType::staticVTable());
- o->init(this, arg1);
+ o->internalClass = ManagedType::defaultInternalClass(engine);
+ Q_ASSERT(o->internalClass && o->internalClass->vtable);
+ o->init(arg1);
return o;
}
@@ -277,7 +284,8 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d_unchecked()->prototype.set(engine, prototype->d());
+ Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0));
+ Q_UNUSED(prototype);
t->d_unchecked()->init();
return t->d();
}
@@ -287,7 +295,8 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d_unchecked()->prototype.set(engine, prototype->d());
+ Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0));
+ Q_UNUSED(prototype);
t->d_unchecked()->init(arg1);
return t->d();
}
@@ -297,7 +306,8 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d_unchecked()->prototype.set(engine, prototype->d());
+ Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0));
+ Q_UNUSED(prototype);
t->d_unchecked()->init(arg1, arg2);
return t->d();
}
@@ -307,7 +317,8 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d_unchecked()->prototype.set(engine, prototype->d());
+ Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0));
+ Q_UNUSED(prototype);
t->d_unchecked()->init(arg1, arg2, arg3);
return t->d();
}
@@ -317,7 +328,8 @@ public:
{
Scope scope(engine);
Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic));
- t->d_unchecked()->prototype.set(engine, prototype->d());
+ Q_ASSERT(t->internalClass()->prototype == (prototype ? prototype->d() : 0));
+ Q_UNUSED(prototype);
t->d_unchecked()->init(arg1, arg2, arg3, arg4);
return t->d();
}
@@ -437,7 +449,7 @@ protected:
/// expects size to be aligned
Heap::Base *allocString(std::size_t unmanagedSize);
Heap::Base *allocData(std::size_t size);
- Heap::Object *allocObjectWithMemberData(std::size_t size, uint nMembers);
+ Heap::Object *allocObjectWithMemberData(const QV4::VTable *vtable, uint nMembers);
#ifdef DETAILED_MM_STATS
void willAllocate(std::size_t size);
diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h
index bf29b44a2c..328797fb5e 100644
--- a/src/qml/memory/qv4mmdefs_p.h
+++ b/src/qml/memory/qv4mmdefs_p.h
@@ -186,7 +186,7 @@ struct Chunk {
return usedSlots;
}
- void sweep(ClassDestroyStatsCallback classCountPtr);
+ bool sweep(ClassDestroyStatsCallback classCountPtr);
void freeAll();
void resetBlackBits();
void collectGrayItems(QV4::MarkStack *markStack);
@@ -269,7 +269,7 @@ Q_STATIC_ASSERT(sizeof(HeapItem) == Chunk::SlotSize);
Q_STATIC_ASSERT(QT_POINTER_SIZE*8 == Chunk::Bits);
Q_STATIC_ASSERT((1 << Chunk::BitShift) == Chunk::Bits);
-struct MarkStack {\
+struct MarkStack {
MarkStack(ExecutionEngine *engine);
Heap::Base **top = 0;
Heap::Base **base = 0;
@@ -287,35 +287,6 @@ struct MarkStack {\
};
-// Base class for the execution engine
-
-#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
-#pragma pack(push, 1)
-#endif
-struct EngineBase {
- Heap::ExecutionContext *current = 0;
-
- Value *jsStackTop = 0;
- quint8 hasException = false;
- quint8 writeBarrierActive = false;
- quint16 unused = 0;
-#if QT_POINTER_SIZE == 8
- quint8 padding[4];
-#endif
- MemoryManager *memoryManager = 0;
- Runtime runtime;
-};
-#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
-#pragma pack(pop)
-#endif
-
-Q_STATIC_ASSERT(std::is_standard_layout<EngineBase>::value);
-Q_STATIC_ASSERT(offsetof(EngineBase, current) == 0);
-Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current) + QT_POINTER_SIZE);
-Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE);
-Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + QT_POINTER_SIZE);
-Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE);
-
// Some helper classes and macros to automate the generation of our
// tables used for marking objects
diff --git a/src/qml/memory/qv4writebarrier_p.h b/src/qml/memory/qv4writebarrier_p.h
index e36ea0749a..86fd28000d 100644
--- a/src/qml/memory/qv4writebarrier_p.h
+++ b/src/qml/memory/qv4writebarrier_p.h
@@ -123,7 +123,7 @@ struct Pointer {
return base;
}
- void set(ExecutionEngine *e, T newVal) {
+ void set(EngineBase *e, T newVal) {
WriteBarrier::write(e, base(), reinterpret_cast<Heap::Base **>(&ptr), reinterpret_cast<Heap::Base *>(newVal));
}
@@ -146,9 +146,12 @@ struct HeapValue : Value {
return base;
}
- void set(ExecutionEngine *e, const Value &newVal) {
+ void set(EngineBase *e, const Value &newVal) {
WriteBarrier::write(e, base(), this, newVal);
}
+ void set(EngineBase *e, Heap::Base *b) {
+ WriteBarrier::write(e, base(), this, b);
+ }
};
template <size_t offset>
@@ -163,10 +166,10 @@ struct ValueArray {
return base;
}
- void set(ExecutionEngine *e, uint index, Value v) {
+ void set(EngineBase *e, uint index, Value v) {
WriteBarrier::write(e, base(), values + index, v);
}
- void set(ExecutionEngine *e, uint index, Heap::Base *b) {
+ void set(EngineBase *e, uint index, Heap::Base *b) {
WriteBarrier::write(e, base(), values + index, b);
}
inline const Value &operator[] (uint index) const {
@@ -177,13 +180,13 @@ struct ValueArray {
return values;
}
- void insertData(ExecutionEngine *e, uint index, Value v) {
+ void insertData(EngineBase *e, uint index, Value v) {
for (uint i = size - 1; i > index; --i) {
values[i] = values[i - 1];
}
set(e, index, v);
}
- void removeData(ExecutionEngine *e, uint index, int n = 1) {
+ void removeData(EngineBase *e, uint index, int n = 1) {
Q_UNUSED(e);
for (uint i = index; i < size - n; ++i) {
values[i] = values[i + n];
diff --git a/src/qml/parser/qqmljslexer.cpp b/src/qml/parser/qqmljslexer.cpp
index 66f9eac126..53e67fde03 100644
--- a/src/qml/parser/qqmljslexer.cpp
+++ b/src/qml/parser/qqmljslexer.cpp
@@ -724,6 +724,11 @@ again:
return multilineStringLiteral ? T_MULTILINE_STRING_LITERAL : T_STRING_LITERAL;
} else if (_char == QLatin1Char('\\')) {
scanChar();
+ if (_codePtr > _endPtr) {
+ _errorCode = IllegalEscapeSequence;
+ _errorMessage = QCoreApplication::translate("QQmlParser", "End of file reached at escape sequence");
+ return T_ERROR;
+ }
QChar u;
diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp
index 4a21acb050..fd516c9815 100644
--- a/src/qml/qml/qqmlengine.cpp
+++ b/src/qml/qml/qqmlengine.cpp
@@ -449,6 +449,12 @@ The following functions are also on the Qt object.
\li \c "windows" - Windows
\li \c "winrt" - WinRT / UWP
\endlist
+
+ \row
+ \li \c platform.pluginName
+ \li This is the name of the platform set on the QGuiApplication instance
+ as returned by \l QGuiApplication::platformName()
+
\endtable
*/
@@ -590,7 +596,7 @@ The following functions are also on the Qt object.
\li application.font
\endlist
- \sa Screen, Window, Window.screen
+ \sa Screen, Window, {QtQuick::Window::screen}{Window.screen}
*/
/*!
diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp
index 4e4db086b0..93c3e8e00c 100644
--- a/src/qml/qml/qqmlfile.cpp
+++ b/src/qml/qml/qqmlfile.cpp
@@ -603,6 +603,12 @@ empty string.
*/
QString QQmlFile::urlToLocalFileOrQrc(const QString& url)
{
+ if (url.startsWith(QLatin1String("qrc://"), Qt::CaseInsensitive)) {
+ if (url.length() > 6)
+ return QLatin1Char(':') + url.midRef(6);
+ return QString();
+ }
+
if (url.startsWith(QLatin1String("qrc:"), Qt::CaseInsensitive)) {
if (url.length() > 4)
return QLatin1Char(':') + url.midRef(4);
diff --git a/src/qml/qml/qqmlglobal.cpp b/src/qml/qml/qqmlglobal.cpp
index 7939656107..6418812bae 100644
--- a/src/qml/qml/qqmlglobal.cpp
+++ b/src/qml/qml/qqmlglobal.cpp
@@ -343,6 +343,8 @@ QObject *QQmlGuiProvider::styleHints()
return o;
}
+QString QQmlGuiProvider::pluginName() const { return QString(); }
+
static QQmlGuiProvider *guiProvider = 0;
Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *newProvider)
diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h
index 707814e781..a6c113f5a7 100644
--- a/src/qml/qml/qqmlglobal_p.h
+++ b/src/qml/qml/qqmlglobal_p.h
@@ -311,6 +311,7 @@ public:
virtual QObject *styleHints();
virtual QStringList fontFamilies();
virtual bool openUrlExternally(QUrl &);
+ virtual QString pluginName() const;
};
Q_QML_PRIVATE_EXPORT QQmlGuiProvider *QQml_setGuiProvider(QQmlGuiProvider *);
diff --git a/src/qml/qml/qqmlplatform.cpp b/src/qml/qml/qqmlplatform.cpp
index 165cde5eb3..a8c402af2e 100644
--- a/src/qml/qml/qqmlplatform.cpp
+++ b/src/qml/qml/qqmlplatform.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "qqmlplatform_p.h"
+#include "qqmlglobal_p.h"
QT_BEGIN_NAMESPACE
@@ -78,6 +79,11 @@ QString QQmlPlatform::os()
#endif
}
+QString QQmlPlatform::pluginName() const
+{
+ return QQml_guiProvider()->pluginName();
+}
+
QT_END_NAMESPACE
#include "moc_qqmlplatform_p.cpp"
diff --git a/src/qml/qml/qqmlplatform_p.h b/src/qml/qml/qqmlplatform_p.h
index 6887720adb..6246ca7105 100644
--- a/src/qml/qml/qqmlplatform_p.h
+++ b/src/qml/qml/qqmlplatform_p.h
@@ -61,12 +61,14 @@ class Q_QML_PRIVATE_EXPORT QQmlPlatform : public QObject
{
Q_OBJECT
Q_PROPERTY(QString os READ os CONSTANT)
+ Q_PROPERTY(QString pluginName READ pluginName CONSTANT)
public:
explicit QQmlPlatform(QObject *parent = 0);
virtual ~QQmlPlatform();
static QString os();
+ QString pluginName() const;
private:
Q_DISABLE_COPY(QQmlPlatform)
diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp
index 88ce2fa1b9..d18159841c 100644
--- a/src/qml/qml/qqmlpropertycache.cpp
+++ b/src/qml/qml/qqmlpropertycache.cpp
@@ -970,8 +970,11 @@ int QQmlPropertyCache::originalClone(QObject *object, int index)
QQmlData *data = QQmlData::get(object, false);
if (data && data->propertyCache) {
QQmlPropertyCache *cache = data->propertyCache;
- while (cache->signal(index)->isCloned())
+ QQmlPropertyData *sig = cache->signal(index);
+ while (sig && sig->isCloned()) {
--index;
+ sig = cache->signal(index);
+ }
} else {
while (QMetaObjectPrivate::signal(object->metaObject(), index).attributes() & QMetaMethod::Cloned)
--index;
diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp
index 40bd2e5020..e4293596d8 100644
--- a/src/qml/qml/qqmltypeloader.cpp
+++ b/src/qml/qml/qqmltypeloader.cpp
@@ -1320,8 +1320,8 @@ bool QQmlTypeLoader::Blob::fetchQmldir(const QUrl &url, const QV4::CompiledData:
{
QQmlQmldirData *data = typeLoader()->getQmldir(url);
- data->setImport(import);
- data->setPriority(priority);
+ data->setImport(this, import);
+ data->setPriority(this, priority);
if (data->status() == Error) {
// This qmldir must not exist - which is not an error
@@ -1349,7 +1349,7 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile
QHash<const QV4::CompiledData::Import *, int>::iterator it = m_unresolvedImports.find(import);
if (it != m_unresolvedImports.end()) {
- *it = data->priority();
+ *it = data->priority(this);
}
// Release this reference at destruction
@@ -1482,7 +1482,7 @@ void QQmlTypeLoader::Blob::dependencyComplete(QQmlDataBlob *blob)
if (blob->type() == QQmlDataBlob::QmldirFile) {
QQmlQmldirData *data = static_cast<QQmlQmldirData *>(blob);
- const QV4::CompiledData::Import *import = data->import();
+ const QV4::CompiledData::Import *import = data->import(this);
QList<QQmlError> errors;
if (!qmldirDataAvailable(data, &errors)) {
@@ -1506,11 +1506,11 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE
{
bool resolve = true;
- const QV4::CompiledData::Import *import = data->import();
- data->setImport(0);
+ const QV4::CompiledData::Import *import = data->import(this);
+ data->setImport(this, 0);
- int priority = data->priority();
- data->setPriority(0);
+ int priority = data->priority(this);
+ data->setPriority(this, 0);
if (import) {
// Do we need to resolve this import?
@@ -3069,7 +3069,7 @@ void QQmlScriptBlob::initializeFromCompilationUnit(QV4::CompiledData::Compilatio
}
QQmlQmldirData::QQmlQmldirData(const QUrl &url, QQmlTypeLoader *loader)
-: QQmlTypeLoader::Blob(url, QmldirFile, loader), m_import(0), m_priority(0)
+: QQmlTypeLoader::Blob(url, QmldirFile, loader)
{
}
@@ -3078,24 +3078,31 @@ const QString &QQmlQmldirData::content() const
return m_content;
}
-const QV4::CompiledData::Import *QQmlQmldirData::import() const
+const QV4::CompiledData::Import *QQmlQmldirData::import(QQmlTypeLoader::Blob *blob) const
{
- return m_import;
+ QHash<QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *>::const_iterator it =
+ m_imports.find(blob);
+ if (it == m_imports.end())
+ return 0;
+ return *it;
}
-void QQmlQmldirData::setImport(const QV4::CompiledData::Import *import)
+void QQmlQmldirData::setImport(QQmlTypeLoader::Blob *blob, const QV4::CompiledData::Import *import)
{
- m_import = import;
+ m_imports[blob] = import;
}
-int QQmlQmldirData::priority() const
+int QQmlQmldirData::priority(QQmlTypeLoader::Blob *blob) const
{
- return m_priority;
+ QHash<QQmlTypeLoader::Blob *, int>::const_iterator it = m_priorities.find(blob);
+ if (it == m_priorities.end())
+ return 0;
+ return *it;
}
-void QQmlQmldirData::setPriority(int priority)
+void QQmlQmldirData::setPriority(QQmlTypeLoader::Blob *blob, int priority)
{
- m_priority = priority;
+ m_priorities[blob] = priority;
}
void QQmlQmldirData::dataReceived(const SourceCodeData &data)
diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h
index 48e7d5cba4..f367fa6f58 100644
--- a/src/qml/qml/qqmltypeloader_p.h
+++ b/src/qml/qml/qqmltypeloader_p.h
@@ -572,11 +572,11 @@ private:
public:
const QString &content() const;
- const QV4::CompiledData::Import *import() const;
- void setImport(const QV4::CompiledData::Import *);
+ const QV4::CompiledData::Import *import(QQmlTypeLoader::Blob *) const;
+ void setImport(QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *);
- int priority() const;
- void setPriority(int);
+ int priority(QQmlTypeLoader::Blob *) const;
+ void setPriority(QQmlTypeLoader::Blob *, int);
protected:
void dataReceived(const SourceCodeData &) override;
@@ -584,8 +584,8 @@ protected:
private:
QString m_content;
- const QV4::CompiledData::Import *m_import;
- int m_priority;
+ QHash<QQmlTypeLoader::Blob *, const QV4::CompiledData::Import *> m_imports;
+ QHash<QQmlTypeLoader::Blob *, int> m_priorities;
};
diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp
index d262b230e2..ce47ab9fa9 100644
--- a/src/qml/qml/qqmlvaluetypewrapper.cpp
+++ b/src/qml/qml/qqmlvaluetypewrapper.cpp
@@ -51,9 +51,11 @@
#include <private/qv4alloca_p.h>
#include <private/qv4objectiterator_p.h>
#include <private/qv4qobjectwrapper_p.h>
+#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
+Q_DECLARE_LOGGING_CATEGORY(lcBindingRemoval)
DEFINE_OBJECT_VTABLE(QV4::QQmlValueTypeWrapper);
@@ -438,6 +440,9 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
if (reference) {
QV4::ScopedFunctionObject f(scope, value);
+ const QQmlQPointer<QObject> &referenceObject = reference->d()->object;
+ const int referencePropertyIndex = reference->d()->property;
+
if (f) {
if (!f->isBinding()) {
// assigning a JS function to a non-var-property is not allowed.
@@ -452,18 +457,31 @@ bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value)
QQmlPropertyData cacheData;
cacheData.setWritable(true);
cacheData.setPropType(writeBackPropertyType);
- cacheData.setCoreIndex(reference->d()->property);
+ cacheData.setCoreIndex(referencePropertyIndex);
QV4::Scoped<QQmlBindingFunction> bindingFunction(scope, (const Value &)f);
QV4::ScopedContext ctx(scope, bindingFunction->scope());
- QQmlBinding *newBinding = QQmlBinding::create(&cacheData, bindingFunction->function(), reference->d()->object, context, ctx);
+ QQmlBinding *newBinding = QQmlBinding::create(&cacheData, bindingFunction->function(), referenceObject, context, ctx);
newBinding->setSourceLocation(bindingFunction->currentLocation());
- newBinding->setTarget(reference->d()->object, cacheData, pd);
+ newBinding->setTarget(referenceObject, cacheData, pd);
QQmlPropertyPrivate::setBinding(newBinding);
return true;
} else {
- QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex()));
+ if (Q_UNLIKELY(lcBindingRemoval().isInfoEnabled())) {
+ if (auto binding = QQmlPropertyPrivate::binding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex()))) {
+ Q_ASSERT(!binding->isValueTypeProxy());
+ const auto qmlBinding = static_cast<const QQmlBinding*>(binding);
+ const auto stackFrame = v4->currentStackFrame();
+ qCInfo(lcBindingRemoval,
+ "Overwriting binding on %s::%s which was initially bound at %s by setting \"%s\" at %s:%d",
+ referenceObject->metaObject()->className(), referenceObject->metaObject()->property(referencePropertyIndex).name(),
+ qPrintable(qmlBinding->expressionIdentifier()),
+ metaObject->property(pd->coreIndex()).name(),
+ qPrintable(stackFrame.source), stackFrame.line);
+ }
+ }
+ QQmlPropertyPrivate::removeBinding(referenceObject, QQmlPropertyIndex(referencePropertyIndex, pd->coreIndex()));
}
}
diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp
index 9f86d1cae9..a2ab9bdfed 100644
--- a/src/qml/qml/qqmlvmemetaobject.cpp
+++ b/src/qml/qml/qqmlvmemetaobject.cpp
@@ -105,7 +105,7 @@ void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *)
QV4::Scope scope(v4);
QV4::Scoped<QV4::MemberData> sp(scope, m_target->propertyAndMethodStorage.value());
if (sp) {
- QV4::MemberData::Index index{ sp->d(), static_cast<uint>(m_index) };
+ QV4::MemberData::Index index{ sp->d(), sp->d()->values.values + m_index };
index.set(v4, QV4::Primitive::nullValue());
}
}
diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp
index b18904fc73..9e8735cbc6 100644
--- a/src/qml/qml/qqmlxmlhttprequest.cpp
+++ b/src/qml/qml/qqmlxmlhttprequest.cpp
@@ -895,7 +895,7 @@ ReturnedValue NamedNodeMap::get(const Managed *m, String *name, bool *hasPropert
const NamedNodeMap *r = static_cast<const NamedNodeMap *>(m);
QV4::ExecutionEngine *v4 = r->engine();
- name->makeIdentifier(v4);
+ name->makeIdentifier();
if (name->equals(v4->id_length()))
return Primitive::fromInt32(r->d()->list().count()).asReturnedValue();
@@ -940,7 +940,7 @@ ReturnedValue NodeList::get(const Managed *m, String *name, bool *hasProperty)
const NodeList *r = static_cast<const NodeList *>(m);
QV4::ExecutionEngine *v4 = r->engine();
- name->makeIdentifier(v4);
+ name->makeIdentifier();
if (name->equals(v4->id_length()))
return Primitive::fromInt32(r->d()->d->children.count()).asReturnedValue();
diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h
index 6704e55b52..387c28a945 100644
--- a/src/qml/qtqmlglobal.h
+++ b/src/qml/qtqmlglobal.h
@@ -40,18 +40,20 @@
#ifndef QTQMLGLOBAL_H
#define QTQMLGLOBAL_H
+#if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB)
+# define QT_QML_BOOTSTRAPPED
+#endif
+
#include <QtCore/qglobal.h>
-#include <QtQml/qtqml-config.h>
-#if QT_CONFIG(qml_network)
-#include <QtNetwork/qtnetworkglobal.h>
+#ifndef QT_QML_BOOTSTRAPPED
+# include <QtQml/qtqml-config.h>
+# if QT_CONFIG(qml_network)
+# include <QtNetwork/qtnetworkglobal.h>
+# endif
#endif
QT_BEGIN_NAMESPACE
-#if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB)
-# define QT_QML_BOOTSTRAPPED
-#endif
-
#if !defined(QT_QML_BOOTSTRAPPED) && !defined(QT_STATIC)
# if defined(QT_BUILD_QML_LIB)
# define Q_QML_EXPORT Q_DECL_EXPORT
diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h
index 6547274d09..4c0ba338d8 100644
--- a/src/qml/qtqmlglobal_p.h
+++ b/src/qml/qtqmlglobal_p.h
@@ -52,49 +52,17 @@
//
#include <QtCore/private/qglobal_p.h>
-#include <QtQml/private/qtqml-config_p.h>
#include <QtQml/qtqmlglobal.h>
+#ifndef QT_QML_BOOTSTRAPPED
+# include <QtQml/private/qtqml-config_p.h>
+#endif
-// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing
-// the occurrences of alloca() in case it's not supported.
-// Q_ALLOCA_DECLARE and Q_ALLOCA_ASSIGN macros separate
-// memory allocation from the declaration and RAII.
-#define Q_ALLOCA_VAR(type, name, size) \
- Q_ALLOCA_DECLARE(type, name); \
- Q_ALLOCA_ASSIGN(type, name, size)
-
-#if defined(QT_BOOTSTRAPPED) || QT_CONFIG(alloca)
-
-#define Q_ALLOCA_DECLARE(type, name) \
- type *name = 0
-
-#define Q_ALLOCA_ASSIGN(type, name, size) \
- name = static_cast<type*>(alloca(size))
+#define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT
+#if !defined(QT_QMLDEVTOOLS_LIB) && !defined(QT_BUILD_QMLDEVTOOLS_LIB)
+# define Q_QML_AUTOTEST_EXPORT Q_AUTOTEST_EXPORT
#else
-QT_BEGIN_NAMESPACE
-class Qt_AllocaWrapper
-{
-public:
- Qt_AllocaWrapper() { m_data = 0; }
- ~Qt_AllocaWrapper() { free(m_data); }
- void *data() { return m_data; }
- void allocate(int size) { m_data = malloc(size); }
-private:
- void *m_data;
-};
-QT_END_NAMESPACE
-
-#define Q_ALLOCA_DECLARE(type, name) \
- Qt_AllocaWrapper _qt_alloca_##name; \
- type *name = 0
-
-#define Q_ALLOCA_ASSIGN(type, name, size) \
- _qt_alloca_##name.allocate(size); \
- name = static_cast<type*>(_qt_alloca_##name.data())
-
+# define Q_QML_AUTOTEST_EXPORT
#endif
-#define Q_QML_PRIVATE_EXPORT Q_QML_EXPORT
-
#endif // QTQMLGLOBAL_P_H
diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp
index 6e9cb5f1fe..e218cdcfe4 100644
--- a/src/qml/types/qqmlconnections.cpp
+++ b/src/qml/types/qqmlconnections.cpp
@@ -286,6 +286,7 @@ void QQmlConnections::connectSignals()
int signalIndex = QQmlPropertyPrivate::get(prop)->signalIndex();
QQmlBoundSignal *signal =
new QQmlBoundSignal(target, signalIndex, this, qmlEngine(this));
+ signal->setEnabled(d->enabled);
QQmlBoundSignalExpression *expression = ctxtdata ?
new QQmlBoundSignalExpression(target, signalIndex,
diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp
index eebf0f74d8..517411fa47 100644
--- a/src/qml/types/qqmllistmodel.cpp
+++ b/src/qml/types/qqmllistmodel.cpp
@@ -1076,6 +1076,7 @@ void ListElement::sync(ListElement *src, ListLayout *srcLayout, ListElement *tar
QVariant v = src->getProperty(srcRole, 0, 0);
target->setVariantProperty(targetRole, v);
}
+ break;
case ListLayout::Role::VariantMap:
{
QVariantMap *map = src->getVariantMapProperty(srcRole);
@@ -1992,12 +1993,12 @@ void QQmlListModel::setDynamicRoles(bool enableDynamicRoles)
if (m_mainThread && m_agent == 0) {
if (enableDynamicRoles) {
if (m_layout->roleCount())
- qmlWarning(this) << tr("unable to enable dynamic roles as this model is not empty!");
+ qmlWarning(this) << tr("unable to enable dynamic roles as this model is not empty");
else
m_dynamicRoles = true;
} else {
if (m_roles.count()) {
- qmlWarning(this) << tr("unable to enable static roles as this model is not empty!");
+ qmlWarning(this) << tr("unable to enable static roles as this model is not empty");
} else {
m_dynamicRoles = false;
}
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
index c77495a1a7..b9f6ea461a 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -61,14 +61,13 @@ public:
V4_DEFINE_EXTENSION(QQmlAdaptorModelEngineData, engineData)
-static QV4::ReturnedValue get_index(QV4::CallContext *ctx)
+static void get_index(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>());
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
if (!o)
- return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")));
- return QV4::Encode(o->d()->item->index);
+ RETURN_RESULT(QV4::Encode(o->d()->item->index));
}
template <typename T, typename M> static void setModelDataType(QMetaObjectBuilder *builder, M *metaType)
@@ -195,19 +194,18 @@ public:
dataType->watchedRoles += newRoles;
}
- static QV4::ReturnedValue get_hasModelChildren(QV4::CallContext *ctx)
+ static void get_hasModelChildren(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>());
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
if (!o)
- return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")));
const QQmlAdaptorModel *const model = static_cast<QQmlDMCachedModelData *>(o->d()->item)->type->model;
if (o->d()->item->index >= 0 && *model) {
const QAbstractItemModel * const aim = model->aim();
- return QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex)));
+ RETURN_RESULT(QV4::Encode(aim->hasChildren(aim->index(o->d()->item->index, 0, model->rootIndex))));
} else {
- return QV4::Encode(false);
+ RETURN_RESULT(QV4::Encode(false));
}
}
@@ -583,27 +581,25 @@ public:
}
}
- static QV4::ReturnedValue get_modelData(QV4::CallContext *ctx)
+ static void get_modelData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>());
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
if (!o)
- return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object"));
+ RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")));
- return scope.engine->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData);
+ RETURN_RESULT(scope.engine->fromVariant(static_cast<QQmlDMListAccessorData *>(o->d()->item)->cachedData));
}
- static QV4::ReturnedValue set_modelData(QV4::CallContext *ctx)
+ static void set_modelData(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData)
{
- QV4::Scope scope(ctx);
- QV4::Scoped<QQmlDelegateModelItemObject> o(scope, ctx->thisObject().as<QQmlDelegateModelItemObject>());
+ QV4::Scoped<QQmlDelegateModelItemObject> o(scope, callData->thisObject.as<QQmlDelegateModelItemObject>());
if (!o)
- return ctx->engine()->throwTypeError(QStringLiteral("Not a valid VisualData object"));
- if (!ctx->argc())
- return ctx->engine()->throwTypeError();
+ RETURN_RESULT(scope.engine->throwTypeError(QStringLiteral("Not a valid VisualData object")));
+ if (!callData->argc)
+ RETURN_RESULT(scope.engine->throwTypeError());
- static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(scope.engine->toVariant(ctx->args()[0], QVariant::Invalid));
- return QV4::Encode::undefined();
+ static_cast<QQmlDMListAccessorData *>(o->d()->item)->setModelData(scope.engine->toVariant(callData->args[0], QVariant::Invalid));
+ RETURN_RESULT(QV4::Encode::undefined());
}
QV4::ReturnedValue get() override
diff --git a/src/qmltest/quicktestresult.cpp b/src/qmltest/quicktestresult.cpp
index dc6caf505b..2a567fac8d 100644
--- a/src/qmltest/quicktestresult.cpp
+++ b/src/qmltest/quicktestresult.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2017 Crimson AS <info@crimson.no>
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
@@ -61,7 +62,10 @@
#include <QtCore/QDir>
#include <QtQuick/qquickwindow.h>
#include <QtGui/qvector3d.h>
+#include <QtGui/qimagewriter.h>
#include <QtQml/private/qqmlglobal_p.h>
+#include <QtQml/QQmlEngine>
+#include <QtQml/QQmlContext>
#include <private/qv4qobjectwrapper_p.h>
#include <algorithm>
@@ -77,6 +81,11 @@ extern bool qWaitForSignal(QObject *obj, const char* signal, int timeout = 5000)
class Q_QUICK_TEST_EXPORT QuickTestImageObject : public QObject
{
Q_OBJECT
+
+ Q_PROPERTY(int width READ width CONSTANT)
+ Q_PROPERTY(int height READ height CONSTANT)
+ Q_PROPERTY(QSize size READ size CONSTANT)
+
public:
QuickTestImageObject(const QImage& img, QObject *parent = 0)
: QObject(parent)
@@ -127,6 +136,33 @@ public Q_SLOTS:
return m_image == other->m_image;
}
+
+ void save(const QString &filePath)
+ {
+ QImageWriter writer(filePath);
+ if (!writer.write(m_image)) {
+ QQmlEngine *engine = qmlContext(this)->engine();
+ QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine->handle());
+ v4->throwError(QStringLiteral("Can't save to %1: %2").arg(filePath, writer.errorString()));
+ }
+ }
+
+public:
+ int width() const
+ {
+ return m_image.width();
+ }
+
+ int height() const
+ {
+ return m_image.height();
+ }
+
+ QSize size() const
+ {
+ return m_image.size();
+ }
+
private:
QImage m_image;
};
@@ -512,6 +548,12 @@ void QuickTestResult::stringify(QQmlV4Function *args)
result = QString::fromLatin1("Qt.vector3d(%1, %2, %3)").arg(v3d.x()).arg(v3d.y()).arg(v3d.z());
break;
}
+ case QVariant::Url:
+ {
+ QUrl url = v.value<QUrl>();
+ result = QString::fromLatin1("Qt.url(%1)").arg(url.toString());
+ break;
+ }
default:
result = v.toString();
}
@@ -706,7 +748,9 @@ QObject *QuickTestResult::grabImage(QQuickItem *item)
QImage grabbed = window->grabWindow();
QRectF rf(item->x(), item->y(), item->width(), item->height());
rf = rf.intersected(QRectF(0, 0, grabbed.width(), grabbed.height()));
- return new QuickTestImageObject(grabbed.copy(rf.toAlignedRect()));
+ QObject *o = new QuickTestImageObject(grabbed.copy(rf.toAlignedRect()));
+ QQmlEngine::setContextForObject(o, qmlContext(this));
+ return o;
}
return 0;
}
diff --git a/src/quick/configure.json b/src/quick/configure.json
index 047fa8c948..65ad5b810b 100644
--- a/src/quick/configure.json
+++ b/src/quick/configure.json
@@ -34,7 +34,8 @@
"features": {
"d3d12": {
"label": "Direct3D 12",
- "purpose": "Provides a Direct3D 12 backend for the Qt Quick Scenegraph",
+ "purpose": "Provides a Direct3D 12 backend for the scenegraph.",
+ "section": "Qt Quick",
"condition": "tests.d3d12",
"output": [
"publicFeature"
@@ -42,7 +43,8 @@
},
"quick-animatedimage": {
"label": "AnimatedImage item",
- "purpose": "Provides the Qt Quick AnimatedImage Item",
+ "purpose": "Provides the AnimatedImage item.",
+ "section": "Qt Quick",
"condition": "features.movie",
"output": [
"privateFeature"
@@ -50,29 +52,33 @@
},
"quick-canvas": {
"label": "Canvas item",
- "purpose": "Provides the Qt Quick Canvas Item",
+ "purpose": "Provides the Canvas item.",
+ "section": "Qt Quick",
"condition": "features.quick-path",
"output": [
"privateFeature"
]
},
"quick-designer": {
- "label": "Support for Quick Designer",
- "purpose": "Provides support for the Qt Quick Designer in Qt Creator",
+ "label": "Support for Qt Quick Designer",
+ "purpose": "Provides support for the Qt Quick Designer in Qt Creator.",
+ "section": "Qt Quick",
"output": [
"privateFeature"
]
},
"quick-flipable": {
"label": "Flipable item",
- "purpose": "Provides the Qt Quick Flipable Item",
+ "purpose": "Provides the Flipable item.",
+ "section": "Qt Quick",
"output": [
"privateFeature"
]
},
"quick-gridview": {
"label": "GridView item",
- "purpose": "Provides the Qt Quick GridView item",
+ "purpose": "Provides the GridView item.",
+ "section": "Qt Quick",
"output": [
"privateFeature"
]
@@ -93,14 +99,16 @@
},
"quick-listview": {
"label": "ListView item",
- "purpose": "Provides the Qt Quick ListView item",
+ "purpose": "Provides the ListView item.",
+ "section": "Qt Quick",
"output": [
"privateFeature"
]
},
"quick-particles": {
"label": "Particle support",
- "purpose": "Provides a particle system for Qt Quick",
+ "purpose": "Provides a particle system.",
+ "section": "Qt Quick",
"condition": "features.quick-shadereffect && features.quick-sprite && features.opengl",
"output": [
"privateFeature"
@@ -108,14 +116,16 @@
},
"quick-path": {
"label": "Path support",
- "purpose": "Provides Path elements in Qt Quick",
+ "purpose": "Provides Path elements.",
+ "section": "Qt Quick",
"output": [
"privateFeature"
]
},
"quick-pathview": {
"label": "PathView item",
- "purpose": "Provides the Qt Quick PathView item",
+ "purpose": "Provides the PathView item.",
+ "section": "Qt Quick",
"condition": "features.quick-path",
"output": [
"privateFeature"
@@ -123,21 +133,24 @@
},
"quick-positioners": {
"label": "Positioner items",
- "purpose": "Provides Positioner items in Qt Quick",
+ "purpose": "Provides Positioner items.",
+ "section": "Qt Quick",
"output": [
"privateFeature"
]
},
"quick-shadereffect": {
"label": "ShaderEffect item",
- "purpose": "Provides Shader effects in Qt Quick",
+ "purpose": "Provides Shader effects.",
+ "section": "Qt Quick",
"output": [
"privateFeature"
]
},
"quick-sprite": {
"label": "Sprite item",
- "purpose": "Provides the Qt Quick Sprite Item",
+ "purpose": "Provides the Sprite item.",
+ "section": "Qt Quick",
"output": [
"privateFeature"
]
diff --git a/src/quick/designer/qquickdesignersupportitems.cpp b/src/quick/designer/qquickdesignersupportitems.cpp
index 2003b484ad..874faed0af 100644
--- a/src/quick/designer/qquickdesignersupportitems.cpp
+++ b/src/quick/designer/qquickdesignersupportitems.cpp
@@ -47,6 +47,7 @@
#include <private/qquicktextinput_p.h>
#include <private/qquicktextedit_p.h>
#include <private/qquicktransition_p.h>
+#include <private/qquickloader_p.h>
#include <private/qquickanimation_p.h>
#include <private/qqmlmetatype_p.h>
@@ -79,6 +80,12 @@ static void stopAnimation(QObject *object)
}
}
+static void makeLoaderSynchronous(QObject *object)
+{
+ if (QQuickLoader *loader = qobject_cast<QQuickLoader*>(object))
+ loader->setAsynchronous(false);
+}
+
static void allSubObjects(QObject *object, QObjectList &objectList)
{
// don't add null pointer and stop if the object is already in the list
@@ -137,6 +144,7 @@ void QQuickDesignerSupportItems::tweakObjects(QObject *object)
allSubObjects(object, objectList);
for (QObject* childObject : qAsConst(objectList)) {
stopAnimation(childObject);
+ makeLoaderSynchronous(childObject);
if (fixResourcePathsForObjectCallBack)
fixResourcePathsForObjectCallBack(childObject);
}
diff --git a/src/quick/doc/images/declarative-arcrotation.png b/src/quick/doc/images/declarative-arcrotation.png
new file mode 100644
index 0000000000..03f009bc12
--- /dev/null
+++ b/src/quick/doc/images/declarative-arcrotation.png
Binary files differ
diff --git a/src/quick/doc/images/pathitem-code-example.png b/src/quick/doc/images/pathitem-code-example.png
new file mode 100644
index 0000000000..25dbe8b311
--- /dev/null
+++ b/src/quick/doc/images/pathitem-code-example.png
Binary files differ
diff --git a/src/quick/doc/images/visualpath-code-example.png b/src/quick/doc/images/visualpath-code-example.png
new file mode 100644
index 0000000000..429e85aa32
--- /dev/null
+++ b/src/quick/doc/images/visualpath-code-example.png
Binary files differ
diff --git a/src/quick/doc/snippets/qml/layerblending.qml b/src/quick/doc/snippets/qml/layerblending.qml
index a922f7896c..3e75a607a6 100644
--- a/src/quick/doc/snippets/qml/layerblending.qml
+++ b/src/quick/doc/snippets/qml/layerblending.qml
@@ -62,7 +62,7 @@ Item {
uniform highp vec2 pixelSize;
varying highp vec2 qt_TexCoord0;
void main() {
- highp vec2 tc = sign(sin(3.14152 * qt_TexCoord0 * pixelSize));
+ highp vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * pixelSize));
if (tc.x != tc.y)
gl_FragColor = color1;
else
diff --git a/src/quick/doc/snippets/qml/opacitymask.qml b/src/quick/doc/snippets/qml/opacitymask.qml
index beffb633d6..beb5cc6f67 100644
--- a/src/quick/doc/snippets/qml/opacitymask.qml
+++ b/src/quick/doc/snippets/qml/opacitymask.qml
@@ -63,7 +63,7 @@ Item {
uniform highp vec2 pixelSize;
varying highp vec2 qt_TexCoord0;
void main() {
- highp vec2 tc = sign(sin(3.14152 * qt_TexCoord0 * pixelSize));
+ highp vec2 tc = sign(sin(3.14159265358979323846 * qt_TexCoord0 * pixelSize));
if (tc.x != tc.y)
gl_FragColor = color1;
else
diff --git a/src/quick/doc/snippets/qml/path/arcrotation.qml b/src/quick/doc/snippets/qml/path/arcrotation.qml
new file mode 100644
index 0000000000..c73d67ff17
--- /dev/null
+++ b/src/quick/doc/snippets/qml/path/arcrotation.qml
@@ -0,0 +1,52 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.9
+//![0]
+Path {
+ startX: 50; startY: 100
+
+ PathArc {
+ x: 150; y: 100
+ radiusX: 50; radiusY: 20
+ xAxisRotation: 45
+ }
+}
+//![0]
diff --git a/src/quick/doc/snippets/qml/regexp.qml b/src/quick/doc/snippets/qml/regexp.qml
new file mode 100644
index 0000000000..c30336d418
--- /dev/null
+++ b/src/quick/doc/snippets/qml/regexp.qml
@@ -0,0 +1,46 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+import QtQuick 2.0
+//![0]
+TextInput {
+ id: hexNumber
+ validator: RegExpValidator { regExp: /[0-9A-F]+/ }
+}
+//![0]
diff --git a/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc b/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc
index 324fc9750f..47dcd6d98c 100644
--- a/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc
+++ b/src/quick/doc/src/concepts/modelviewsdata/modelview.qdoc
@@ -87,6 +87,7 @@ To visualize data, bind the view's \c model property to a model and the
The club may decorate the members list by binding visual objects to the \c
header and \c footer properties. The visual object may be defined inline, in
another file, or in a \l {Component} type.
+
\snippet qml/listview-decorations.qml decorations
\image listview-decorations.png
@@ -102,7 +103,6 @@ To visualize data, bind the view's \c model property to a model and the
will always ensure that the \c currentIndex is within the highlight range
specified.
-
\section2 ListView Sections
\l {ListView} contents may be grouped into \e sections, where related list
@@ -195,7 +195,7 @@ To visualize data, bind the view's \c model property to a model and the
Positioning of items from a model can be achieved using a \l{Repeater}.
- \section2 ListModel
+ \section2 List Model
ListModel is a simple hierarchy of types specified in QML. The
available roles are specified by the \l ListElement properties.
@@ -222,7 +222,7 @@ To visualize data, bind the view's \c model property to a model and the
using the model. To reset the roles available in the model, call ListModel::clear().
- \section2 XmlListModel
+ \section2 XML Model
XmlListModel allows construction of a model from an XML data source. The roles
are specified via the \l XmlRole type. The type needs to be imported.
@@ -244,23 +244,44 @@ To visualize data, bind the view's \c model property to a model and the
}
\endqml
+ The \c query property specifies that the XmlListModel generates a model item
+ for each \c <item> in the XML document.
+
The \l{Qt Quick Demo - RSS News}{RSS News demo} shows how XmlListModel can
be used to display an RSS feed.
- \section2 VisualItemModel
+ \section2 Object Model
+
+ ObjectModel contains the visual items to be used in a view. When an ObjectModel
+ is used in a view, the view does not require a delegate because the ObjectModel
+ already contains the visual delegate (items).
- VisualItemModel allows QML items to be provided as a model.
+ The example below places three colored rectangles in a ListView.
- This model contains both the data and delegate; the child items of a
- VisualItemModel provide the contents of the delegate. The model
- does not provide any roles.
+ \code
+ import QtQuick 2.0
+ import QtQml.Models 2.1
+
+ Rectangle {
+ ObjectModel {
+ id: itemModel
+ Rectangle { height: 30; width: 80; color: "red" }
+ Rectangle { height: 30; width: 80; color: "green" }
+ Rectangle { height: 30; width: 80; color: "blue" }
+ }
- \snippet qml/models/visual-model-and-view.qml visual model and view
+ ListView {
+ anchors.fill: parent
+ model: itemModel
+ }
+ }
+ \endcode
- Note that in the above example there is no delegate required.
- The items of the model itself provide the visual types that
- will be positioned by the view.
+ \note VisualItemModel can also be used, but it is only provided for compatibility
+ reasons. VisualItemModel allows a QML item to be provided as a model. This model
+ contains both the data and delegate; the child items of a VisualItemModel
+ provide the contents of the delegate. The model does not provide any roles.
\section2 Integers as Models
@@ -357,8 +378,18 @@ rectangles for the Grid item to position in a 5 by 5 arrangement.
The number of items created by a Repeater is held by its \l{Repeater::}{count}
property. It is not possible to set this property to determine the number of
items to be created. Instead, as in the above example, we use an integer as
-the model. This is explained in the \l{qtquick-modelviewsdata-modelview.html#integers-as-models}{QML Data Models}
-document.
+the model.
+
+For more details, see the \l{qtquick-modelviewsdata-modelview.html#integers-as-models}{QML Data Models} document.
+
+If the model is a string list, the delegate is also exposed to a read-only
+\c modelData property that holds the string. For example:
+
+\table
+ \row
+ \li \snippet qml/repeaters/repeater.qml modeldata
+ \li \image repeater-modeldata.png
+\endtable
It is also possible to use a delegate as the template for the items created
by a Repeater. This is specified using the \l{Repeater::}{delegate} property.
diff --git a/src/quick/doc/src/concepts/positioning/layouts.qdoc b/src/quick/doc/src/concepts/positioning/layouts.qdoc
index 47ed2563f8..3824d17559 100644
--- a/src/quick/doc/src/concepts/positioning/layouts.qdoc
+++ b/src/quick/doc/src/concepts/positioning/layouts.qdoc
@@ -133,6 +133,5 @@ control of spacing between items and between lines of items.
There are several other ways to position items in a user interface. In addition
to the basic technique of specifying their coordinates directly, they can be
positioned relative to other items with \l{anchor-layout}{anchors}, or used
-with \l{QML Data Models} such as
-\l{QML Data Models#VisualItemModel}{VisualItemModel}.
+with \l{QML Data Models} such as \l{QML Data Models#Object Model}{Object Model}.
*/
diff --git a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
index 76f863d07f..bcfdf311af 100644
--- a/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/adaptations.qdoc
@@ -73,7 +73,7 @@ adaptations.
The default adaptation capable of providing the full Qt Quick 2 feature
set is the OpenGL adaptation. All of the details of the OpenGL
-adpatation can are available here:
+adaptation can are available here:
\l{qtquick-visualcanvas-scenegraph-renderer.html}{OpenGL Adaptation}
\section1 Software Adaptation
diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
index 2e41c85873..cb14c72b04 100644
--- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc
@@ -32,7 +32,7 @@
\section1 The Scene Graph in Qt Quick
Qt Quick 2 makes use of a dedicated scene graph based and a series of
-adpatations of which the default uses OpenGL ES 2.0 or OpenGL 2.0 for
+adaptations of which the default uses OpenGL ES 2.0 or OpenGL 2.0 for
its rendering. Using a scene graph for graphics rather than the
traditional imperative painting systems (QPainter and
similar), means the scene to be rendered can be retained between
diff --git a/src/quick/doc/src/concepts/visualcanvas/topic.qdoc b/src/quick/doc/src/concepts/visualcanvas/topic.qdoc
index 168c616d06..f6b4024f7a 100644
--- a/src/quick/doc/src/concepts/visualcanvas/topic.qdoc
+++ b/src/quick/doc/src/concepts/visualcanvas/topic.qdoc
@@ -57,7 +57,7 @@ See the documentation about the \l{qtquick-visualcanvas-visualparent.html}
Modern computer systems and devices use graphics processing units or GPUs to
render graphics. Qt Quick can leverage this graphics hardware by using graphics
-APIs like OpenGL. The default graphics adpatation for Qt Quick requires OpenGL and
+APIs like OpenGL. The default graphics adaptation for Qt Quick requires OpenGL and
it is used to display applications developed with Qt Quick in QML. In particular,
Qt Quick defines a scene graph which is then rendered. See the documentation about the
\l{qtquick-visualcanvas-scenegraph.html}{Scene Graph} for in-depth information about
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
index ebfa6deb6f..9ac7422a39 100644
--- a/src/quick/items/context2d/qquickcontext2d.cpp
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -40,6 +40,7 @@
#include "qquickcontext2d_p.h"
#include "qquickcontext2dcommandbuffer_p.h"
#include "qquickcanvasitem_p.h"
+#include <private/qtquickglobal_p.h>
#include <private/qquickcontext2dtexture_p.h>
#include <private/qquickitem_p.h>
#if QT_CONFIG(quick_shadereffect)
@@ -136,7 +137,7 @@ Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
THROW_GENERIC_ERROR("Not a Context2D object");
#define qClamp(val, min, max) qMin(qMax(val, min), max)
#define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
-QColor qt_color_from_string(const QV4::Value &name)
+Q_QUICK_PRIVATE_EXPORT QColor qt_color_from_string(const QV4::Value &name)
{
QByteArray str = name.toQString().toUtf8();
diff --git a/src/quick/items/items.qrc b/src/quick/items/items.qrc
index 6aaf757c29..da9bf0c828 100644
--- a/src/quick/items/items.qrc
+++ b/src/quick/items/items.qrc
@@ -8,5 +8,9 @@
<file>shaders/shadereffect_core.vert</file>
<file>shaders/shadereffectfallback_core.frag</file>
<file>shaders/shadereffectfallback_core.vert</file>
+ <file>shaders/lineargradient.vert</file>
+ <file>shaders/lineargradient.frag</file>
+ <file>shaders/lineargradient_core.vert</file>
+ <file>shaders/lineargradient_core.frag</file>
</qresource>
</RCC>
diff --git a/src/quick/items/qquickanimatedimage_p.h b/src/quick/items/qquickanimatedimage_p.h
index 143fe8904d..54da093259 100644
--- a/src/quick/items/qquickanimatedimage_p.h
+++ b/src/quick/items/qquickanimatedimage_p.h
@@ -97,7 +97,6 @@ Q_SIGNALS:
void playingChanged();
void pausedChanged();
void frameChanged();
- void sourceSizeChanged();
private Q_SLOTS:
void movieUpdate();
diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp
index 830adc50b7..1e0d268f93 100644
--- a/src/quick/items/qquickevents.cpp
+++ b/src/quick/items/qquickevents.cpp
@@ -1293,8 +1293,8 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i
}
parent = parent->parentItem();
}
- bool filterRelevant = isFiltering && grabberIsChild;
+ bool filterRelevant = isFiltering && grabberIsChild;
if (!(isGrabber || (isInside && (!hasAnotherGrabber || isFiltering)) || filterRelevant))
continue;
if ((p->state() == QQuickEventPoint::Pressed || p->state() == QQuickEventPoint::Released) && isInside)
diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h
index 19b2a6221e..b39e81d6e8 100644
--- a/src/quick/items/qquickevents_p_p.h
+++ b/src/quick/items/qquickevents_p_p.h
@@ -386,9 +386,9 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerEvent : public QObject
Q_PROPERTY(Qt::MouseButtons buttons READ buttons)
public:
- QQuickPointerEvent(QObject *parent = nullptr)
+ QQuickPointerEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
: QObject(parent)
- , m_device(nullptr)
+ , m_device(device)
, m_event(nullptr)
, m_button(Qt::NoButton)
, m_pressedButtons(Qt::NoButton)
@@ -446,8 +446,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerMouseEvent : public QQuickPointerEvent
{
Q_OBJECT
public:
- QQuickPointerMouseEvent(QObject *parent = nullptr)
- : QQuickPointerEvent(parent), m_mousePoint(new QQuickEventPoint(this)) { }
+ QQuickPointerMouseEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickPointerEvent(parent, device), m_mousePoint(new QQuickEventPoint(this)) { }
QQuickPointerEvent *reset(QEvent *) override;
void localize(QQuickItem *target) override;
@@ -479,8 +479,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPointerTouchEvent : public QQuickPointerEvent
{
Q_OBJECT
public:
- QQuickPointerTouchEvent(QObject *parent = nullptr)
- : QQuickPointerEvent(parent)
+ QQuickPointerTouchEvent(QObject *parent = nullptr, QQuickPointerDevice *device = nullptr)
+ : QQuickPointerEvent(parent, device)
, m_pointCount(0)
, m_synthMouseEvent(QEvent::MouseMove, QPointF(), Qt::NoButton, Qt::NoButton, Qt::NoModifier)
{ }
@@ -579,7 +579,6 @@ public:
int buttonCount() const { return m_buttonCount; }
QString name() const { return m_name; }
QPointingDeviceUniqueId uniqueId() const { return m_uniqueId; }
- QQuickPointerEvent *pointerEvent() const { return m_event; }
static QQuickPointerDevice *touchDevice(QTouchDevice *d);
static QList<QQuickPointerDevice *> touchDevices();
@@ -592,17 +591,10 @@ private:
QQuickPointerDevice(DeviceType devType, PointerType pType, Capabilities caps, int maxPoints, int buttonCount, const QString &name, qint64 uniqueId = 0)
: m_deviceType(devType), m_pointerType(pType), m_capabilities(caps)
, m_maximumTouchPoints(maxPoints), m_buttonCount(buttonCount), m_name(name)
- , m_uniqueId(QPointingDeviceUniqueId::fromNumericId(uniqueId)), m_event(nullptr)
+ , m_uniqueId(QPointingDeviceUniqueId::fromNumericId(uniqueId))
{
- if (m_deviceType == Mouse) {
- m_event = new QQuickPointerMouseEvent;
- } else if (m_deviceType == TouchScreen || m_deviceType == TouchPad) {
- m_event = new QQuickPointerTouchEvent;
- } else {
- Q_ASSERT(false);
- }
}
- ~QQuickPointerDevice() { delete m_event; }
+ ~QQuickPointerDevice() { }
private:
DeviceType m_deviceType;
@@ -612,8 +604,6 @@ private:
int m_buttonCount;
QString m_name;
QPointingDeviceUniqueId m_uniqueId;
- // the device-specific event instance which is reused during event delivery
- QQuickPointerEvent *m_event;
QVector<QQuickPointerHandler *> m_eventDeliveryTargets; // during delivery, handlers which have already seen the event
Q_DISABLE_COPY(QQuickPointerDevice)
diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp
index fd78c46a16..c570b95a21 100644
--- a/src/quick/items/qquickgridview.cpp
+++ b/src/quick/items/qquickgridview.cpp
@@ -495,9 +495,7 @@ bool QQuickGridViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
// We've jumped more than a page. Estimate which items are now
// visible and fill from there.
int count = (fillFrom - (rowPos + rowSize())) / (rowSize()) * columns;
- for (FxViewItem *item : qAsConst(visibleItems))
- releaseItem(item);
- visibleItems.clear();
+ releaseVisibleItems();
modelIndex += count;
if (modelIndex >= model->count())
modelIndex = model->count() - 1;
diff --git a/src/quick/items/qquickimplicitsizeitem.cpp b/src/quick/items/qquickimplicitsizeitem.cpp
index 1996fb9489..2569b2a224 100644
--- a/src/quick/items/qquickimplicitsizeitem.cpp
+++ b/src/quick/items/qquickimplicitsizeitem.cpp
@@ -45,29 +45,11 @@ QT_BEGIN_NAMESPACE
/*!
\internal
- The purpose of QQuickImplicitSizeItem is not immediately clear, as both
- the implicit size properties and signals exist on QQuickItem. However,
- for some items - where the implicit size has an underlying meaning (such as
- Image, where the implicit size represents the real size of the image)
- having implicit size writable is an undesirable thing.
-
- QQuickImplicitSizeItem redefines the properties as being readonly.
- Unfortunately, this also means they need to redefine the change signals.
- See QTBUG-30258 for more information.
+ QQuickImplicitSizeItem redefines the implicitWidth and implicitHeight
+ properties as readonly, as some items (e.g. Image, where the implicit size
+ represents the real size of the image) should not be able to have their
+ implicit size modified.
*/
-void QQuickImplicitSizeItemPrivate::implicitWidthChanged()
-{
- Q_Q(QQuickImplicitSizeItem);
- QQuickItemPrivate::implicitWidthChanged();
- emit q->implicitWidthChanged2();
-}
-
-void QQuickImplicitSizeItemPrivate::implicitHeightChanged()
-{
- Q_Q(QQuickImplicitSizeItem);
- QQuickItemPrivate::implicitHeightChanged();
- emit q->implicitHeightChanged2();
-}
QQuickImplicitSizeItem::QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent)
: QQuickItem(dd, parent)
diff --git a/src/quick/items/qquickimplicitsizeitem_p.h b/src/quick/items/qquickimplicitsizeitem_p.h
index 75b04449f8..8ae8f9f447 100644
--- a/src/quick/items/qquickimplicitsizeitem_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p.h
@@ -60,16 +60,12 @@ class QQuickImplicitSizeItemPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickImplicitSizeItem : public QQuickItem
{
Q_OBJECT
- Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged2)
- Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged2)
+ Q_PROPERTY(qreal implicitWidth READ implicitWidth NOTIFY implicitWidthChanged)
+ Q_PROPERTY(qreal implicitHeight READ implicitHeight NOTIFY implicitHeightChanged)
protected:
QQuickImplicitSizeItem(QQuickImplicitSizeItemPrivate &dd, QQuickItem *parent);
-Q_SIGNALS:
- Q_REVISION(1) void implicitWidthChanged2();
- Q_REVISION(1) void implicitHeightChanged2();
-
private:
Q_DISABLE_COPY(QQuickImplicitSizeItem)
Q_DECLARE_PRIVATE(QQuickImplicitSizeItem)
diff --git a/src/quick/items/qquickimplicitsizeitem_p_p.h b/src/quick/items/qquickimplicitsizeitem_p_p.h
index 2c42fa62f2..0495cf87e1 100644
--- a/src/quick/items/qquickimplicitsizeitem_p_p.h
+++ b/src/quick/items/qquickimplicitsizeitem_p_p.h
@@ -65,9 +65,6 @@ public:
QQuickImplicitSizeItemPrivate()
{
}
-
- void implicitWidthChanged() Q_DECL_OVERRIDE;
- void implicitHeightChanged() Q_DECL_OVERRIDE;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp
index e959b51bc3..d923db6d68 100644
--- a/src/quick/items/qquickitem.cpp
+++ b/src/quick/items/qquickitem.cpp
@@ -147,8 +147,8 @@ QQuickTransform::QQuickTransform(QQuickTransformPrivate &dd, QObject *parent)
QQuickTransform::~QQuickTransform()
{
Q_D(QQuickTransform);
- for (QQuickItem *item : qAsConst(d->items)) {
- QQuickItemPrivate *p = QQuickItemPrivate::get(item);
+ for (int ii = 0; ii < d->items.count(); ++ii) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii));
p->transforms.removeOne(this);
p->dirty(QQuickItemPrivate::Transform);
}
@@ -157,8 +157,8 @@ QQuickTransform::~QQuickTransform()
void QQuickTransform::update()
{
Q_D(QQuickTransform);
- for (QQuickItem *item : qAsConst(d->items)) {
- QQuickItemPrivate *p = QQuickItemPrivate::get(item);
+ for (int ii = 0; ii < d->items.count(); ++ii) {
+ QQuickItemPrivate *p = QQuickItemPrivate::get(d->items.at(ii));
p->dirty(QQuickItemPrivate::Transform);
}
}
@@ -170,7 +170,9 @@ QQuickContents::QQuickContents(QQuickItem *item)
QQuickContents::~QQuickContents()
{
- for (QQuickItem *child : m_item->childItems()) {
+ QList<QQuickItem *> children = m_item->childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ QQuickItem *child = children.at(i);
QQuickItemPrivate::get(child)->removeItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
}
}
@@ -193,8 +195,9 @@ bool QQuickContents::calcHeight(QQuickItem *changed)
} else {
qreal top = std::numeric_limits<qreal>::max();
qreal bottom = -std::numeric_limits<qreal>::max();
- const QList<QQuickItem*> children = m_item->childItems();
- for (QQuickItem *child : qAsConst(children)) {
+ QList<QQuickItem *> children = m_item->childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ QQuickItem *child = children.at(i);
qreal y = child->y();
if (y + child->height() > bottom)
bottom = y + child->height();
@@ -227,8 +230,9 @@ bool QQuickContents::calcWidth(QQuickItem *changed)
} else {
qreal left = std::numeric_limits<qreal>::max();
qreal right = -std::numeric_limits<qreal>::max();
- const QList<QQuickItem*> children = m_item->childItems();
- for (QQuickItem *child : qAsConst(children)) {
+ QList<QQuickItem *> children = m_item->childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ QQuickItem *child = children.at(i);
qreal x = child->x();
if (x + child->width() > right)
right = x + child->width();
@@ -247,7 +251,9 @@ void QQuickContents::complete()
{
QQuickItemPrivate::get(m_item)->addItemChangeListener(this, QQuickItemPrivate::Children);
- for (QQuickItem *child : m_item->childItems()) {
+ QList<QQuickItem *> children = m_item->childItems();
+ for (int i = 0; i < children.count(); ++i) {
+ QQuickItem *child = children.at(i);
QQuickItemPrivate::get(child)->addItemChangeListener(this, QQuickItemPrivate::Geometry | QQuickItemPrivate::Destroyed);
//###what about changes to visibility?
}
@@ -1348,7 +1354,8 @@ void QQuickKeysAttached::componentComplete()
#if QT_CONFIG(im)
Q_D(QQuickKeysAttached);
if (d->item) {
- for (QQuickItem *targetItem : qAsConst(d->targets)) {
+ for (int ii = 0; ii < d->targets.count(); ++ii) {
+ QQuickItem *targetItem = d->targets.at(ii);
if (targetItem && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) {
d->item->setFlag(QQuickItem::ItemAcceptsInputMethod);
break;
@@ -1370,10 +1377,11 @@ void QQuickKeysAttached::keyPressed(QKeyEvent *event, bool post)
// first process forwards
if (d->item && d->item->window()) {
d->inPress = true;
- for (QQuickItem *targetItem : qAsConst(d->targets)) {
- if (targetItem && targetItem->isVisible()) {
+ for (int ii = 0; ii < d->targets.count(); ++ii) {
+ QQuickItem *i = d->targets.at(ii);
+ if (i && i->isVisible()) {
event->accept();
- QCoreApplication::sendEvent(targetItem, event);
+ QCoreApplication::sendEvent(i, event);
if (event->isAccepted()) {
d->inPress = false;
return;
@@ -1413,10 +1421,11 @@ void QQuickKeysAttached::keyReleased(QKeyEvent *event, bool post)
if (d->item && d->item->window()) {
d->inRelease = true;
- for (QQuickItem *targetItem : qAsConst(d->targets)) {
- if (targetItem && targetItem->isVisible()) {
+ for (int ii = 0; ii < d->targets.count(); ++ii) {
+ QQuickItem *i = d->targets.at(ii);
+ if (i && i->isVisible()) {
event->accept();
- QCoreApplication::sendEvent(targetItem, event);
+ QCoreApplication::sendEvent(i, event);
if (event->isAccepted()) {
d->inRelease = false;
return;
@@ -1440,7 +1449,8 @@ void QQuickKeysAttached::inputMethodEvent(QInputMethodEvent *event, bool post)
Q_D(QQuickKeysAttached);
if (post == m_processPost && d->item && !d->inIM && d->item->window()) {
d->inIM = true;
- for (QQuickItem *targetItem : qAsConst(d->targets)) {
+ for (int ii = 0; ii < d->targets.count(); ++ii) {
+ QQuickItem *targetItem = d->targets.at(ii);
if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod)) {
QCoreApplication::sendEvent(targetItem, event);
if (event->isAccepted()) {
@@ -1459,12 +1469,13 @@ QVariant QQuickKeysAttached::inputMethodQuery(Qt::InputMethodQuery query) const
{
Q_D(const QQuickKeysAttached);
if (d->item) {
- for (QQuickItem *targetItem : qAsConst(d->targets)) {
- if (targetItem && targetItem->isVisible() && (targetItem->flags() & QQuickItem::ItemAcceptsInputMethod) && targetItem == d->imeItem) {
- //### how robust is targetItem == d->imeItem check?
- QVariant v = targetItem->inputMethodQuery(query);
+ for (int ii = 0; ii < d->targets.count(); ++ii) {
+ QQuickItem *i = d->targets.at(ii);
+ if (i && i->isVisible() && (i->flags() & QQuickItem::ItemAcceptsInputMethod) && i == d->imeItem) {
+ //### how robust is i == d->imeItem check?
+ QVariant v = i->inputMethodQuery(query);
if (v.userType() == QVariant::RectF)
- v = d->item->mapRectFromItem(targetItem, v.toRectF()); //### cost?
+ v = d->item->mapRectFromItem(i, v.toRectF()); //### cost?
return v;
}
}
@@ -1638,9 +1649,11 @@ void QQuickItemPrivate::setImplicitLayoutMirror(bool mirror, bool inherit)
if (isMirrorImplicit)
setLayoutMirror(inherit ? inheritedLayoutMirror : false);
- for (QQuickItem *child : qAsConst(childItems)) {
- QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
- childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent);
+ for (int i = 0; i < childItems.count(); ++i) {
+ if (QQuickItem *child = qmlobject_cast<QQuickItem *>(childItems.at(i))) {
+ QQuickItemPrivate *childPrivate = QQuickItemPrivate::get(child);
+ childPrivate->setImplicitLayoutMirror(inheritedLayoutMirror, inheritMirrorFromParent);
+ }
}
}
@@ -2396,7 +2409,8 @@ QQuickItem::~QQuickItem()
remove themselves from our list of transforms when that list has already
been destroyed after ~QQuickItem() has run.
*/
- for (QQuickTransform *t : qAsConst(d->transforms)) {
+ for (int ii = 0; ii < d->transforms.count(); ++ii) {
+ QQuickTransform *t = d->transforms.at(ii);
QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t);
tp->items.removeOne(this);
}
@@ -2887,8 +2901,8 @@ QList<QQuickItem *> QQuickItemPrivate::paintOrderChildItems() const
// If none of the items have set Z then the paint order list is the same as
// the childItems list. This is by far the most common case.
bool haveZ = false;
- for (QQuickItem *childItem : qAsConst(childItems)) {
- if (QQuickItemPrivate::get(childItem)->z() != 0.) {
+ for (int i = 0; i < childItems.count(); ++i) {
+ if (QQuickItemPrivate::get(childItems.at(i))->z() != 0.) {
haveZ = true;
break;
}
@@ -2989,7 +3003,8 @@ void QQuickItemPrivate::refWindow(QQuickWindow *c)
if (!parentItem)
QQuickWindowPrivate::get(window)->parentlessItems.insert(q);
- for (QQuickItem *child : qAsConst(childItems)) {
+ for (int ii = 0; ii < childItems.count(); ++ii) {
+ QQuickItem *child = childItems.at(ii);
QQuickItemPrivate::get(child)->refWindow(c);
}
@@ -3041,7 +3056,8 @@ void QQuickItemPrivate::derefWindow()
paintNode = 0;
- for (QQuickItem *child : qAsConst(childItems)) {
+ for (int ii = 0; ii < childItems.count(); ++ii) {
+ QQuickItem *child = childItems.at(ii);
QQuickItemPrivate::get(child)->derefWindow();
}
@@ -3498,7 +3514,8 @@ void QQuickItemPrivate::transform_clear(QQmlListProperty<QQuickTransform> *prop)
QQuickItem *that = static_cast<QQuickItem *>(prop->object);
QQuickItemPrivate *p = QQuickItemPrivate::get(that);
- for (QQuickTransform *t : qAsConst(p->transforms)) {
+ for (int ii = 0; ii < p->transforms.count(); ++ii) {
+ QQuickTransform *t = p->transforms.at(ii);
QQuickTransformPrivate *tp = QQuickTransformPrivate::get(t);
tp->items.removeOne(that);
}
@@ -5885,9 +5902,8 @@ bool QQuickItemPrivate::setEffectiveVisibleRecur(bool newEffectiveVisible)
}
bool childVisibilityChanged = false;
- for (QQuickItem *childItem : qAsConst(childItems)) {
- childVisibilityChanged |= QQuickItemPrivate::get(childItem)->setEffectiveVisibleRecur(newEffectiveVisible);
- }
+ for (int ii = 0; ii < childItems.count(); ++ii)
+ childVisibilityChanged |= QQuickItemPrivate::get(childItems.at(ii))->setEffectiveVisibleRecur(newEffectiveVisible);
itemChange(QQuickItem::ItemVisibleHasChanged, effectiveVisible);
#if QT_CONFIG(accessibility)
@@ -5936,8 +5952,8 @@ void QQuickItemPrivate::setEffectiveEnableRecur(QQuickItem *scope, bool newEffec
}
}
- for (QQuickItem *childItem : qAsConst(childItems)) {
- QQuickItemPrivate::get(childItem)->setEffectiveEnableRecur(
+ for (int ii = 0; ii < childItems.count(); ++ii) {
+ QQuickItemPrivate::get(childItems.at(ii))->setEffectiveEnableRecur(
(flags & QQuickItem::ItemIsFocusScope) && scope ? q : scope, newEffectiveEnable);
}
@@ -6378,6 +6394,7 @@ void QQuickItem::setFlags(Flags flags)
\qmlproperty real QtQuick::Item::height
Defines the item's position and size.
+ The default value is \c 0.
The (x,y) position is relative to the \l parent.
diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp
index 13f23c918a..e6321e9365 100644
--- a/src/quick/items/qquickitemsmodule.cpp
+++ b/src/quick/items/qquickitemsmodule.cpp
@@ -376,6 +376,12 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickFlickable, 9>(uri, 2, 9, "Flickable");
qmlRegisterType<QQuickMouseArea, 9>(uri, 2, 9, "MouseArea");
+
+#if QT_CONFIG(quick_path)
+ qmlRegisterType<QQuickPathArc, 2>(uri, 2, 9, "PathArc");
+ qmlRegisterType<QQuickPathMove>(uri, 2, 9, "PathMove");
+#endif
+
qmlRegisterType<QQuickText, 9>(uri, 2, 9, "Text");
qmlRegisterType<QQuickTextInput, 9>(uri, 2, 9, "TextInput");
qmlRegisterType<QQuickTouchPoint>(uri, 2, 9, "TouchPoint");
diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp
index 555db03962..084b1f197a 100644
--- a/src/quick/items/qquickitemview.cpp
+++ b/src/quick/items/qquickitemview.cpp
@@ -380,9 +380,7 @@ void QQuickItemView::setDelegate(QQmlComponent *delegate)
int oldCount = dataModel->count();
dataModel->setDelegate(delegate);
if (isComponentComplete()) {
- for (FxViewItem *item : qAsConst(d->visibleItems))
- d->releaseItem(item);
- d->visibleItems.clear();
+ d->releaseVisibleItems();
d->releaseItem(d->currentItem);
d->currentItem = 0;
d->updateSectionCriteria();
@@ -1743,9 +1741,7 @@ void QQuickItemViewPrivate::clear()
currentChanges.reset();
timeline.clear();
- for (FxViewItem *item : qAsConst(visibleItems))
- releaseItem(item);
- visibleItems.clear();
+ releaseVisibleItems();
visibleIndex = 0;
for (FxViewItem *item : qAsConst(releasePendingTransition)) {
diff --git a/src/quick/items/qquickitemview_p_p.h b/src/quick/items/qquickitemview_p_p.h
index 3087682ac7..b6353246e8 100644
--- a/src/quick/items/qquickitemview_p_p.h
+++ b/src/quick/items/qquickitemview_p_p.h
@@ -269,6 +269,15 @@ public:
q->polish();
}
+ void releaseVisibleItems() {
+ // make a copy and clear the visibleItems first to avoid destroyed
+ // items being accessed during the loop (QTBUG-61294)
+ const QList<FxViewItem *> oldVisible = visibleItems;
+ visibleItems.clear();
+ for (FxViewItem *item : oldVisible)
+ releaseItem(item);
+ }
+
QPointer<QQmlInstanceModel> model;
QVariant modelVariant;
int itemCount;
diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp
index f739115e6b..18f9b8512d 100644
--- a/src/quick/items/qquicklistview.cpp
+++ b/src/quick/items/qquicklistview.cpp
@@ -660,9 +660,7 @@ bool QQuickListViewPrivate::addVisibleItems(qreal fillFrom, qreal fillTo, qreal
int newModelIdx = qBound(0, modelIndex + count, model->count());
count = newModelIdx - modelIndex;
if (count) {
- for (FxViewItem *item : qAsConst(visibleItems))
- releaseItem(item);
- visibleItems.clear();
+ releaseVisibleItems();
modelIndex = newModelIdx;
visibleIndex = modelIndex;
visiblePos = itemEnd + count * (averageSize + spacing);
diff --git a/src/quick/items/qquickopenglshadereffect.cpp b/src/quick/items/qquickopenglshadereffect.cpp
index 4fcfe04b55..c3e0ba05bd 100644
--- a/src/quick/items/qquickopenglshadereffect.cpp
+++ b/src/quick/items/qquickopenglshadereffect.cpp
@@ -230,7 +230,7 @@ void QQuickOpenGLShaderEffectCommon::disconnectPropertySignals(QQuickItem *item,
auto mapper = signalMappers[shaderType].at(i);
void *a = mapper;
QObjectPrivate::disconnect(item, mapper->signalIndex(), &a);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source) {
if (item->window())
@@ -272,7 +272,7 @@ void QQuickOpenGLShaderEffectCommon::connectPropertySignals(QQuickItem *item,
qWarning("QQuickOpenGLShaderEffect: '%s' does not have a matching property!", d.name.constData());
}
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source) {
if (item->window())
@@ -335,6 +335,7 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item,
Q_ASSERT(decl == UniformQualifier);
const int sampLen = sizeof("sampler2D") - 1;
+ const int sampExtLen = sizeof("samplerExternalOES") - 1;
const int opLen = sizeof("qt_Opacity") - 1;
const int matLen = sizeof("qt_Matrix") - 1;
const int srLen = sizeof("qt_SubRect_") - 1;
@@ -357,8 +358,12 @@ void QQuickOpenGLShaderEffectCommon::lookThroughShaderCode(QQuickItem *item,
mapper = new QtPrivate::MappedSlotObject([this, mappedId](){
this->mappedPropertyChanged(mappedId);
});
- bool sampler = typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0;
- d.specialType = sampler ? UniformData::Sampler : UniformData::None;
+ if (typeLength == sampLen && qstrncmp("sampler2D", s + typeIndex, sampLen) == 0)
+ d.specialType = UniformData::Sampler;
+ else if (typeLength == sampExtLen && qstrncmp("samplerExternalOES", s + typeIndex, sampExtLen) == 0)
+ d.specialType = UniformData::SamplerExternal;
+ else
+ d.specialType = UniformData::None;
d.setValueFromProperty(item, itemMetaObject);
}
uniformData[shaderType].append(d);
@@ -451,7 +456,8 @@ void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode
int textureProviderCount = 0;
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
- if (uniformData[shaderType].at(i).specialType == UniformData::Sampler)
+ if (uniformData[shaderType].at(i).specialType == UniformData::Sampler ||
+ uniformData[shaderType].at(i).specialType == UniformData::SamplerExternal)
++textureProviderCount;
}
material->uniforms[shaderType] = uniformData[shaderType];
@@ -474,7 +480,7 @@ void QQuickOpenGLShaderEffectCommon::updateMaterial(QQuickOpenGLShaderEffectNode
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType != UniformData::Sampler)
+ if (d.specialType != UniformData::Sampler && d.specialType != UniformData::SamplerExternal)
continue;
QSGTextureProvider *oldProvider = material->textureProviders.at(index);
QSGTextureProvider *newProvider = 0;
@@ -513,7 +519,7 @@ void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window)
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source)
QQuickItemPrivate::get(source)->refWindow(window);
@@ -524,7 +530,7 @@ void QQuickOpenGLShaderEffectCommon::updateWindow(QQuickWindow *window)
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
const UniformData &d = uniformData[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source)
QQuickItemPrivate::get(source)->derefWindow();
@@ -539,7 +545,7 @@ void QQuickOpenGLShaderEffectCommon::sourceDestroyed(QObject *object)
for (int shaderType = 0; shaderType < Key::ShaderTypeCount; ++shaderType) {
for (int i = 0; i < uniformData[shaderType].size(); ++i) {
UniformData &d = uniformData[shaderType][i];
- if (d.specialType == UniformData::Sampler && d.value.canConvert<QObject *>()) {
+ if ((d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) && d.value.canConvert<QObject *>()) {
if (qvariant_cast<QObject *>(d.value) == object)
d.value = QVariant();
}
@@ -554,7 +560,7 @@ static bool qquick_uniqueInUniformData(QQuickItem *source, const QVector<QQuickO
if (s == typeToSkip && i == indexToSkip)
continue;
const QQuickOpenGLShaderEffectMaterial::UniformData &d = uniformData[s][i];
- if (d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler && qvariant_cast<QObject *>(d.value) == source)
+ if ((d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::Sampler || d.specialType == QQuickOpenGLShaderEffectMaterial::UniformData::SamplerExternal) && qvariant_cast<QObject *>(d.value) == source)
return false;
}
}
@@ -568,7 +574,7 @@ void QQuickOpenGLShaderEffectCommon::propertyChanged(QQuickItem *item,
Key::ShaderType shaderType = Key::ShaderType(mappedId >> 16);
int index = mappedId & 0xffff;
UniformData &d = uniformData[shaderType][index];
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
QQuickItem *source = qobject_cast<QQuickItem *>(qvariant_cast<QObject *>(d.value));
if (source) {
if (item->window())
diff --git a/src/quick/items/qquickopenglshadereffectnode.cpp b/src/quick/items/qquickopenglshadereffectnode.cpp
index e1ea98641d..5dbfee73cb 100644
--- a/src/quick/items/qquickopenglshadereffectnode.cpp
+++ b/src/quick/items/qquickopenglshadereffectnode.cpp
@@ -47,6 +47,10 @@
#include <QtCore/qmutex.h>
#include <QtGui/qopenglfunctions.h>
+#ifndef GL_TEXTURE_EXTERNAL_OES
+#define GL_TEXTURE_EXTERNAL_OES 0x8D65
+#endif
+
QT_BEGIN_NAMESPACE
static bool hasAtlasTexture(const QVector<QSGTextureProvider *> &textureProviders)
@@ -124,7 +128,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
const UniformData &d = material->uniforms[shaderType].at(i);
QByteArray name = d.name;
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
program()->setUniformValue(d.name.constData(), textureProviderIndex++);
// We don't need to store the sampler uniform locations, since their values
// only need to be set once. Look for the "qt_SubRect_" uniforms instead.
@@ -143,7 +147,7 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
for (int i = 0; i < material->uniforms[shaderType].size(); ++i) {
const UniformData &d = material->uniforms[shaderType].at(i);
int loc = m_uniformLocs[shaderType].at(i);
- if (d.specialType == UniformData::Sampler) {
+ if (d.specialType == UniformData::Sampler || d.specialType == UniformData::SamplerExternal) {
int idx = textureProviderIndex++;
functions->glActiveTexture(GL_TEXTURE0 + idx);
if (QSGTextureProvider *provider = material->textureProviders.at(idx)) {
@@ -164,7 +168,10 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri
continue;
}
}
- functions->glBindTexture(GL_TEXTURE_2D, 0);
+ if (d.specialType == UniformData::Sampler)
+ functions->glBindTexture(GL_TEXTURE_2D, 0);
+ else if (d.specialType == UniformData::SamplerExternal)
+ functions->glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);
} else if (d.specialType == UniformData::Opacity) {
program()->setUniformValue(loc, state.opacity());
} else if (d.specialType == UniformData::Matrix) {
@@ -396,7 +403,7 @@ bool QQuickOpenGLShaderEffectMaterial::UniformData::operator == (const UniformDa
if (name != other.name)
return false;
- if (specialType == UniformData::Sampler) {
+ if (specialType == UniformData::Sampler || specialType == UniformData::SamplerExternal) {
// We can't check the source objects as these live in the GUI thread,
// so return true here and rely on the textureProvider check for
// equality of these..
diff --git a/src/quick/items/qquickopenglshadereffectnode_p.h b/src/quick/items/qquickopenglshadereffectnode_p.h
index aea28e6612..784294d9eb 100644
--- a/src/quick/items/qquickopenglshadereffectnode_p.h
+++ b/src/quick/items/qquickopenglshadereffectnode_p.h
@@ -90,7 +90,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickOpenGLShaderEffectMaterial : public QSGMateri
public:
struct UniformData
{
- enum SpecialType { None, Sampler, SubRect, Opacity, Matrix };
+ enum SpecialType { None, Sampler, SamplerExternal, SubRect, Opacity, Matrix };
QByteArray name;
QVariant value;
diff --git a/src/quick/items/qquickrectangle.cpp b/src/quick/items/qquickrectangle.cpp
index 5ecca3c91d..9308553a79 100644
--- a/src/quick/items/qquickrectangle.cpp
+++ b/src/quick/items/qquickrectangle.cpp
@@ -1,6 +1,5 @@
/****************************************************************************
**
-** Copyright (C) 2017 Crimson AS <info@crimson.no>
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
@@ -270,9 +269,11 @@ QGradientStops QQuickGradient::gradientStops() const
void QQuickGradient::doUpdate()
{
- static_cast<QQuickItem*>(parent())->update();
+ emit updated();
}
+int QQuickRectanglePrivate::doUpdateSlotIdx = -1;
+
/*!
\qmltype Rectangle
\instantiates QQuickRectangle
@@ -329,6 +330,11 @@ QQuickRectangle::QQuickRectangle(QQuickItem *parent)
#endif
}
+void QQuickRectangle::doUpdate()
+{
+ update();
+}
+
/*!
\qmlproperty bool QtQuick::Rectangle::antialiasing
@@ -393,7 +399,16 @@ void QQuickRectangle::setGradient(QQuickGradient *gradient)
Q_D(QQuickRectangle);
if (d->gradient == gradient)
return;
+ static int updatedSignalIdx = -1;
+ if (updatedSignalIdx < 0)
+ updatedSignalIdx = QMetaMethod::fromSignal(&QQuickGradient::updated).methodIndex();
+ if (d->doUpdateSlotIdx < 0)
+ d->doUpdateSlotIdx = QQuickRectangle::staticMetaObject.indexOfSlot("doUpdate()");
+ if (d->gradient)
+ QMetaObject::disconnect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
d->gradient = gradient;
+ if (d->gradient)
+ QMetaObject::connect(d->gradient, updatedSignalIdx, this, d->doUpdateSlotIdx);
update();
}
diff --git a/src/quick/items/qquickrectangle_p.h b/src/quick/items/qquickrectangle_p.h
index 627f778e44..724a06013c 100644
--- a/src/quick/items/qquickrectangle_p.h
+++ b/src/quick/items/qquickrectangle_p.h
@@ -129,6 +129,9 @@ public:
QGradientStops gradientStops() const;
+Q_SIGNALS:
+ void updated();
+
private:
void doUpdate();
@@ -169,6 +172,9 @@ Q_SIGNALS:
protected:
QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE;
+private Q_SLOTS:
+ void doUpdate();
+
private:
Q_DISABLE_COPY(QQuickRectangle)
Q_DECLARE_PRIVATE(QQuickRectangle)
diff --git a/src/quick/items/qquickrectangle_p_p.h b/src/quick/items/qquickrectangle_p_p.h
index e771beec87..3c1aaf7661 100644
--- a/src/quick/items/qquickrectangle_p_p.h
+++ b/src/quick/items/qquickrectangle_p_p.h
@@ -76,6 +76,7 @@ public:
QQuickGradient *gradient;
QQuickPen *pen;
qreal radius;
+ static int doUpdateSlotIdx;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquickrepeater.cpp b/src/quick/items/qquickrepeater.cpp
index 9ad7d27b18..6fc4c0553a 100644
--- a/src/quick/items/qquickrepeater.cpp
+++ b/src/quick/items/qquickrepeater.cpp
@@ -314,7 +314,11 @@ void QQuickRepeater::setDelegate(QQmlComponent *delegate)
/*!
\qmlproperty int QtQuick::Repeater::count
- This property holds the number of items in the repeater.
+ This property holds the number of items in the model.
+
+ \note The number of items in the model as reported by count may differ from
+ the number of created delegates if the Repeater is in the process of
+ instantiating delegates or is incorrectly set up.
*/
int QQuickRepeater::count() const
{
diff --git a/src/quick/items/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp
index 9b54b7fba9..6a3eab957e 100644
--- a/src/quick/items/qquickscreen.cpp
+++ b/src/quick/items/qquickscreen.cpp
@@ -100,6 +100,27 @@ QT_BEGIN_NAMESPACE
The y coordinate of the screen within the virtual desktop.
*/
/*!
+ \qmlattachedproperty string Screen::manufacturer
+ \readonly
+ \since 5.10
+
+ The manufacturer of the screen.
+*/
+/*!
+ \qmlattachedproperty string Screen::model
+ \readonly
+ \since 5.10
+
+ The model of the screen.
+*/
+/*!
+ \qmlattachedproperty string Screen::serialNumber
+ \readonly
+ \since 5.10
+
+ The serial number of the screen.
+*/
+/*!
\qmlattachedproperty int Screen::width
\readonly
@@ -234,6 +255,27 @@ QString QQuickScreenInfo::name() const
return m_screen->name();
}
+QString QQuickScreenInfo::manufacturer() const
+{
+ if (!m_screen)
+ return QString();
+ return m_screen->manufacturer();
+}
+
+QString QQuickScreenInfo::model() const
+{
+ if (!m_screen)
+ return QString();
+ return m_screen->model();
+}
+
+QString QQuickScreenInfo::serialNumber() const
+{
+ if (!m_screen)
+ return QString();
+ return m_screen->serialNumber();
+}
+
int QQuickScreenInfo::width() const
{
if (!m_screen)
@@ -335,6 +377,12 @@ void QQuickScreenInfo::setWrappedScreen(QScreen *screen)
}
if (!oldScreen || screen->name() != oldScreen->name())
emit nameChanged();
+ if (!oldScreen || screen->manufacturer() != oldScreen->manufacturer())
+ emit manufacturerChanged();
+ if (!oldScreen || screen->model() != oldScreen->model())
+ emit modelChanged();
+ if (!oldScreen || screen->serialNumber() != oldScreen->serialNumber())
+ emit serialNumberChanged();
if (!oldScreen || screen->orientation() != oldScreen->orientation())
emit orientationChanged();
if (!oldScreen || screen->primaryOrientation() != oldScreen->primaryOrientation())
diff --git a/src/quick/items/qquickscreen_p.h b/src/quick/items/qquickscreen_p.h
index 99e1466631..e9db07d14c 100644
--- a/src/quick/items/qquickscreen_p.h
+++ b/src/quick/items/qquickscreen_p.h
@@ -68,6 +68,9 @@ class Q_AUTOTEST_EXPORT QQuickScreenInfo : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+ Q_PROPERTY(QString manufacturer READ manufacturer NOTIFY manufacturerChanged REVISION 10)
+ Q_PROPERTY(QString model READ model NOTIFY modelChanged REVISION 10)
+ Q_PROPERTY(QString serialNumber READ serialNumber NOTIFY serialNumberChanged REVISION 10)
Q_PROPERTY(int width READ width NOTIFY widthChanged)
Q_PROPERTY(int height READ height NOTIFY heightChanged)
Q_PROPERTY(int desktopAvailableWidth READ desktopAvailableWidth NOTIFY desktopGeometryChanged)
@@ -87,6 +90,9 @@ public:
QQuickScreenInfo(QObject *parent = nullptr, QScreen *wrappedScreen = nullptr);
QString name() const;
+ QString manufacturer() const;
+ QString model() const;
+ QString serialNumber() const;
int width() const;
int height() const;
int desktopAvailableWidth() const;
@@ -104,6 +110,9 @@ public:
Q_SIGNALS:
void nameChanged();
+ Q_REVISION(10) void manufacturerChanged();
+ Q_REVISION(10) void modelChanged();
+ Q_REVISION(10) void serialNumberChanged();
void widthChanged();
void heightChanged();
void desktopGeometryChanged();
diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp
index 09371db66d..080cc9412e 100644
--- a/src/quick/items/qquicktext.cpp
+++ b/src/quick/items/qquicktext.cpp
@@ -384,6 +384,7 @@ void QQuickTextPrivate::updateSize()
updateBaseline(fm.ascent(), q->height() - fontHeight - vPadding);
q->setImplicitSize(hPadding, fontHeight + vPadding);
layedOutTextRect = QRectF(0, 0, 0, fontHeight);
+ advance = QSizeF();
emit q->contentSizeChanged();
updateType = UpdatePaintNode;
q->update();
@@ -457,8 +458,26 @@ void QQuickTextPrivate::updateSize()
if (iWidth == -1)
q->setImplicitHeight(size.height() + vPadding);
+
+ QTextBlock firstBlock = extra->doc->firstBlock();
+ while (firstBlock.layout()->lineCount() == 0)
+ firstBlock = firstBlock.next();
+
+ QTextBlock lastBlock = extra->doc->lastBlock();
+ while (lastBlock.layout()->lineCount() == 0)
+ lastBlock = lastBlock.previous();
+
+ if (firstBlock.lineCount() > 0 && lastBlock.lineCount() > 0) {
+ QTextLine firstLine = firstBlock.layout()->lineAt(0);
+ QTextLine lastLine = lastBlock.layout()->lineAt(lastBlock.layout()->lineCount() - 1);
+ advance = QSizeF(lastLine.horizontalAdvance(),
+ (lastLine.y() + lastBlock.layout()->position().y()) - (firstLine.y() + firstBlock.layout()->position().y()));
+ } else {
+ advance = QSizeF();
+ }
}
+
if (layedOutTextRect.size() != previousSize)
emit q->contentSizeChanged();
updateType = UpdatePaintNode;
@@ -968,6 +987,16 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline)
br.moveTop(0);
+ // Find the advance of the text layout
+ if (layout.lineCount() > 0) {
+ QTextLine firstLine = layout.lineAt(0);
+ QTextLine lastLine = layout.lineAt(layout.lineCount() - 1);
+ advance = QSizeF(lastLine.horizontalAdvance(),
+ lastLine.y() - firstLine.y());
+ } else {
+ advance = QSizeF();
+ }
+
if (!horizontalFit && !verticalFit)
break;
@@ -3055,6 +3084,24 @@ QJSValue QQuickText::fontInfo() const
return value;
}
+/*!
+ \qmlproperty size QtQuick::Text::advance
+ \since 5.10
+
+ The distance, in pixels, from the baseline origin of the first
+ character of the text item, to the baseline origin of the first
+ character in a text item occurring directly after this one
+ in a text flow.
+
+ Note that the advance can be negative if the text flows from
+ the right to the left.
+*/
+QSizeF QQuickText::advance() const
+{
+ Q_D(const QQuickText);
+ return d->advance;
+}
+
QT_END_NAMESPACE
#include "moc_qquicktext_p.cpp"
diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h
index b190738cfb..a56bcdb87b 100644
--- a/src/quick/items/qquicktext_p.h
+++ b/src/quick/items/qquicktext_p.h
@@ -99,6 +99,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem
Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged REVISION 6)
Q_PROPERTY(QJSValue fontInfo READ fontInfo NOTIFY fontInfoChanged REVISION 9)
+ Q_PROPERTY(QSizeF advance READ advance NOTIFY contentSizeChanged REVISION 10)
public:
QQuickText(QQuickItem *parent=0);
@@ -251,6 +252,7 @@ public:
void resetBottomPadding();
QJSValue fontInfo() const;
+ QSizeF advance() const;
Q_SIGNALS:
void textChanged(const QString &text);
diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h
index 6456750359..fde07eaf2e 100644
--- a/src/quick/items/qquicktext_p_p.h
+++ b/src/quick/items/qquicktext_p_p.h
@@ -89,6 +89,7 @@ public:
void processHoverEvent(QHoverEvent *event);
QRectF layedOutTextRect;
+ QSizeF advance;
struct ExtraData {
ExtraData();
diff --git a/src/quick/items/qquickview.cpp b/src/quick/items/qquickview.cpp
index 8313b53a7d..fca1805fc9 100644
--- a/src/quick/items/qquickview.cpp
+++ b/src/quick/items/qquickview.cpp
@@ -496,6 +496,7 @@ void QQuickViewPrivate::setRootObject(QObject *obj)
if (QQuickItem *sgItem = qobject_cast<QQuickItem *>(obj)) {
root = sgItem;
sgItem->setParentItem(q->QQuickWindow::contentItem());
+ QQml_setParent_noEvent(sgItem, q->QQuickWindow::contentItem());
} else if (qobject_cast<QWindow *>(obj)) {
qWarning() << "QQuickView does not support using windows as a root item." << endl
<< endl
diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp
index 713df4c500..b8b32b82be 100644
--- a/src/quick/items/qquickwindow.cpp
+++ b/src/quick/items/qquickwindow.cpp
@@ -529,6 +529,7 @@ void QQuickWindowPrivate::init(QQuickWindow *c, QQuickRenderControl *control)
Q_Q(QQuickWindow);
contentItem = new QQuickRootItem;
+ QQml_setParent_noEvent(contentItem, c);
QQmlEngine::setObjectOwnership(contentItem, QQmlEngine::CppOwnership);
QQuickItemPrivate *contentItemPrivate = QQuickItemPrivate::get(contentItem);
contentItemPrivate->window = q;
@@ -754,9 +755,9 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber)
if (grabber && touchMouseId != -1 && touchMouseDevice) {
// update the touch item for mouse touch id to the new grabber
qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << touchMouseId << "->" << q->mouseGrabberItem();
- auto point = touchMouseDevice->pointerEvent()->pointById(touchMouseId);
+ auto point = pointerEventInstance(touchMouseDevice)->pointById(touchMouseId);
if (point) {
- auto originalEvent = point->pointerEvent()->device()->pointerEvent();
+ auto originalEvent = pointerEventInstance(point->pointerEvent()->device());
for (int i = 0; i < originalEvent->pointCount(); ++i)
originalEvent->point(i)->cancelExclusiveGrab();
point->setGrabberItem(grabber);
@@ -764,7 +765,7 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber)
point->cancelPassiveGrab(handler);
}
} else {
- QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent();
+ QQuickPointerEvent *event = pointerEventInstance(QQuickPointerDevice::genericMouseDevice());
Q_ASSERT(event->pointCount() == 1);
auto point = event->point(0);
point->setGrabberItem(grabber);
@@ -790,7 +791,7 @@ void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &
continue;
}
if (id == touchMouseId) {
- auto point = touchMouseDevice->pointerEvent()->pointById(id);
+ auto point = pointerEventInstance(touchMouseDevice)->pointById(id);
auto touchMouseGrabber = point->grabberItem();
if (touchMouseGrabber) {
point->setExclusiveGrabber(nullptr);
@@ -804,7 +805,7 @@ void QQuickWindowPrivate::grabTouchPoints(QObject *grabber, const QVector<int> &
const auto touchDevices = QQuickPointerDevice::touchDevices();
for (auto device : touchDevices) {
- auto point = device->pointerEvent()->pointById(id);
+ auto point = pointerEventInstance(device)->pointById(id);
if (!point)
continue;
QObject *oldGrabber = point->exclusiveGrabber();
@@ -835,7 +836,7 @@ void QQuickWindowPrivate::removeGrabber(QQuickItem *grabber, bool mouse, bool to
bool ungrab = false;
const auto touchDevices = QQuickPointerDevice::touchDevices();
for (auto device : touchDevices) {
- auto pointerEvent = device->pointerEvent();
+ auto pointerEvent = pointerEventInstance(device);
for (int i = 0; i < pointerEvent->pointCount(); ++i) {
if (pointerEvent->point(i)->exclusiveGrabber() == grabber) {
pointerEvent->point(i)->setGrabberItem(nullptr);
@@ -1505,13 +1506,13 @@ QQuickItem *QQuickWindow::mouseGrabberItem() const
Q_D(const QQuickWindow);
if (d->touchMouseId != -1 && d->touchMouseDevice) {
- QQuickPointerEvent *event = d->touchMouseDevice->pointerEvent();
+ QQuickPointerEvent *event = d->pointerEventInstance(d->touchMouseDevice);
auto point = event->pointById(d->touchMouseId);
Q_ASSERT(point);
return point->grabberItem();
}
- QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent();
+ QQuickPointerEvent *event = d->pointerEventInstance(QQuickPointerDevice::genericMouseDevice());
Q_ASSERT(event->pointCount());
return event->point(0)->grabberItem();
}
@@ -1934,7 +1935,7 @@ bool QQuickWindowPrivate::deliverTouchCancelEvent(QTouchEvent *event)
// A TouchCancel event will typically not contain any points.
// Deliver it to all items and handlers that have active touches.
- QQuickPointerEvent *pointerEvent = QQuickPointerDevice::touchDevice(event->device())->pointerEvent();
+ QQuickPointerEvent *pointerEvent = pointerEventInstance(QQuickPointerDevice::touchDevice(event->device()));
for (int i = 0; i < pointerEvent->pointCount(); ++i)
pointerEvent->point(i)->cancelExclusiveGrabImpl(event);
touchMouseId = -1;
@@ -2027,8 +2028,14 @@ bool QQuickWindowPrivate::compressTouchEvent(QTouchEvent *event)
void QQuickWindowPrivate::handleTouchEvent(QTouchEvent *event)
{
translateTouchEvent(event);
- if (event->touchPoints().size())
- lastMousePosition = event->touchPoints().at(0).pos();
+ if (event->touchPoints().size()) {
+ auto point = event->touchPoints().at(0);
+ if (point.state() == Qt::TouchPointReleased) {
+ lastMousePosition = QPointF();
+ } else {
+ lastMousePosition = point.pos();
+ }
+ }
qCDebug(DBG_TOUCH) << event;
@@ -2106,7 +2113,7 @@ void QQuickWindowPrivate::handleMouseEvent(QMouseEvent *event)
updateCursor(event->windowPos());
#endif
- if (!QQuickPointerDevice::genericMouseDevice()->pointerEvent()->point(0)->exclusiveGrabber()) {
+ if (!pointerEventInstance(QQuickPointerDevice::genericMouseDevice())->point(0)->exclusiveGrabber()) {
QPointF last = lastMousePosition.isNull() ? event->windowPos() : lastMousePosition;
lastMousePosition = event->windowPos();
@@ -2153,6 +2160,32 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents()
}
}
+QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QQuickPointerDevice *device) const
+{
+ // the list of devices should be very small so a linear search should be ok
+ for (QQuickPointerEvent *e: pointerEventInstances) {
+ if (e->device() == device)
+ return e;
+ }
+
+ QQuickPointerEvent *ev = nullptr;
+ QQuickWindow *q = const_cast<QQuickWindow*>(q_func());
+ switch (device->type()) {
+ case QQuickPointerDevice::Mouse:
+ ev = new QQuickPointerMouseEvent(q, device);
+ break;
+ case QQuickPointerDevice::TouchPad:
+ case QQuickPointerDevice::TouchScreen:
+ ev = new QQuickPointerTouchEvent(q, device);
+ break;
+ default:
+ // TODO tablet event types
+ break;
+ }
+ pointerEventInstances << ev;
+ return ev;
+}
+
/*!
\internal
Returns a QQuickPointerEvent instance suitable for wrapping and delivering \a event.
@@ -2163,25 +2196,29 @@ void QQuickWindowPrivate::flushFrameSynchronousEvents()
QQuickPointerEvent *QQuickWindowPrivate::pointerEventInstance(QEvent *event) const
{
QQuickPointerDevice *dev = nullptr;
+ QQuickPointerEvent *ev = nullptr;
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseButtonDblClick:
case QEvent::MouseMove:
dev = QQuickPointerDevice::genericMouseDevice();
+ ev = pointerEventInstance(dev);
break;
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
case QEvent::TouchCancel:
dev = QQuickPointerDevice::touchDevice(static_cast<QTouchEvent *>(event)->device());
+ ev = pointerEventInstance(dev);
break;
// TODO tablet event types
default:
break;
}
- Q_ASSERT(dev);
- return dev->pointerEvent()->reset(event);
+
+ Q_ASSERT(ev);
+ return ev->reset(event);
}
void QQuickWindowPrivate::deliverPointerEvent(QQuickPointerEvent *event)
@@ -2784,7 +2821,7 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ
qCDebug(DBG_TOUCH) << "touch event intercepted as synth mouse event by childMouseEventFilter of " << filteringParent;
if (t != QEvent::MouseButtonRelease) {
qCDebug(DBG_TOUCH_TARGET) << "TP (mouse)" << hex << tp.id() << "->" << filteringParent;
- touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabberItem(filteringParent);
+ pointerEventInstance(touchMouseDevice)->pointById(tp.id())->setGrabberItem(filteringParent);
touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set
filteringParent->grabMouse();
auto pointerEventPoint = pte->pointById(tp.id());
@@ -4042,7 +4079,7 @@ QSGTexture *QQuickWindow::createTextureFromImage(const QImage &image, CreateText
initialized or OpenGL is not in use.
\note This function only has an effect when using the default OpenGL scene graph
- adpation.
+ adaptation.
\sa sceneGraphInitialized(), QSGTexture
*/
@@ -4150,7 +4187,7 @@ void QQuickWindow::setDefaultAlphaBuffer(bool useAlpha)
graph renderer. Clear these manually on demand.
\note This function only has an effect when using the default OpenGL scene graph
- adpation.
+ adaptation.
\sa QQuickWindow::beforeRendering()
*/
diff --git a/src/quick/items/qquickwindow_p.h b/src/quick/items/qquickwindow_p.h
index a40301bcfa..c2d2a7a8a4 100644
--- a/src/quick/items/qquickwindow_p.h
+++ b/src/quick/items/qquickwindow_p.h
@@ -163,6 +163,10 @@ public:
void flushFrameSynchronousEvents();
void deliverDelayedTouchEvent();
+ // the device-specific event instances which are reused during event delivery
+ mutable QVector<QQuickPointerEvent *> pointerEventInstances;
+ QQuickPointerEvent *pointerEventInstance(QQuickPointerDevice *device) const;
+
// delivery of pointer events:
QQuickPointerEvent *pointerEventInstance(QEvent *ev) const;
void deliverPointerEvent(QQuickPointerEvent *);
diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp
index a5234d4f77..a7f45534c4 100644
--- a/src/quick/items/qquickwindowmodule.cpp
+++ b/src/quick/items/qquickwindowmodule.cpp
@@ -200,6 +200,7 @@ void QQuickWindowModule::defineModule()
qmlRegisterUncreatableType<QQuickScreen>(uri, 2, 0, "Screen", QStringLiteral("Screen can only be used via the attached property."));
qmlRegisterUncreatableType<QQuickScreen,1>(uri, 2, 3, "Screen", QStringLiteral("Screen can only be used via the attached property."));
qmlRegisterUncreatableType<QQuickScreenInfo,2>(uri, 2, 3, "ScreenInfo", QStringLiteral("ScreenInfo can only be used via the attached property."));
+ qmlRegisterUncreatableType<QQuickScreenInfo,10>(uri, 2, 10, "ScreenInfo", QStringLiteral("ScreenInfo can only be used via the attached property."));
}
QT_END_NAMESPACE
diff --git a/src/quick/items/shaders/lineargradient.frag b/src/quick/items/shaders/lineargradient.frag
new file mode 100644
index 0000000000..7f4a739109
--- /dev/null
+++ b/src/quick/items/shaders/lineargradient.frag
@@ -0,0 +1,9 @@
+uniform sampler2D gradTabTexture;
+uniform highp float opacity;
+
+varying highp float gradTabIndex;
+
+void main()
+{
+ gl_FragColor = texture2D(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity;
+}
diff --git a/src/quick/items/shaders/lineargradient.vert b/src/quick/items/shaders/lineargradient.vert
new file mode 100644
index 0000000000..eb21b8886b
--- /dev/null
+++ b/src/quick/items/shaders/lineargradient.vert
@@ -0,0 +1,15 @@
+attribute vec4 vertexCoord;
+attribute vec4 vertexColor;
+
+uniform mat4 matrix;
+uniform vec2 gradStart;
+uniform vec2 gradEnd;
+
+varying float gradTabIndex;
+
+void main()
+{
+ vec2 gradVec = gradEnd - gradStart;
+ gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);
+ gl_Position = matrix * vertexCoord;
+}
diff --git a/src/quick/items/shaders/lineargradient_core.frag b/src/quick/items/shaders/lineargradient_core.frag
new file mode 100644
index 0000000000..5908acfa67
--- /dev/null
+++ b/src/quick/items/shaders/lineargradient_core.frag
@@ -0,0 +1,12 @@
+#version 150 core
+
+uniform sampler2D gradTabTexture;
+uniform float opacity;
+
+in float gradTabIndex;
+out vec4 fragColor;
+
+void main()
+{
+ fragColor = texture(gradTabTexture, vec2(gradTabIndex, 0.5)) * opacity;
+}
diff --git a/src/quick/items/shaders/lineargradient_core.vert b/src/quick/items/shaders/lineargradient_core.vert
new file mode 100644
index 0000000000..60b56f38e3
--- /dev/null
+++ b/src/quick/items/shaders/lineargradient_core.vert
@@ -0,0 +1,17 @@
+#version 150 core
+
+in vec4 vertexCoord;
+in vec4 vertexColor;
+
+uniform mat4 matrix;
+uniform vec2 gradStart;
+uniform vec2 gradEnd;
+
+out float gradTabIndex;
+
+void main()
+{
+ vec2 gradVec = gradEnd - gradStart;
+ gradTabIndex = dot(gradVec, vertexCoord.xy - gradStart) / (gradVec.x * gradVec.x + gradVec.y * gradVec.y);
+ gl_Position = matrix * vertexCoord;
+}
diff --git a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
index 2ff180ea99..02cf8209d1 100644
--- a/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgabstractsoftwarerenderer.cpp
@@ -165,12 +165,12 @@ QRegion QSGAbstractSoftwareRenderer::optimizeRenderList()
// Keep up with obscured regions
if (node->isOpaque()) {
- m_obscuredRegion += QRegion(node->boundingRect());
+ m_obscuredRegion += node->boundingRectMin();
}
if (node->isDirty()) {
// Don't paint things outside of the rendering area
- if (!m_background->rect().toRect().contains(node->boundingRect(), /*proper*/ true)) {
+ if (!m_background->rect().toRect().contains(node->boundingRectMax(), /*proper*/ true)) {
// Some part(s) of node is(are) outside of the rendering area
QRegion renderArea(m_background->rect().toRect());
QRegion outsideRegions = node->dirtyRegion().subtracted(renderArea);
@@ -181,7 +181,7 @@ QRegion QSGAbstractSoftwareRenderer::optimizeRenderList()
// Get the dirty region's to pass to the next nodes
if (node->isOpaque()) {
// if isOpaque, subtract node's dirty rect from m_dirtyRegion
- m_dirtyRegion -= node->dirtyRegion();
+ m_dirtyRegion -= node->boundingRectMin();
} else {
// if isAlpha, add node's dirty rect to m_dirtyRegion
m_dirtyRegion += node->dirtyRegion();
@@ -264,7 +264,7 @@ void QSGAbstractSoftwareRenderer::nodeRemoved(QSGNode *node)
// Need to mark this region dirty in the other nodes
QRegion dirtyRegion = renderable->previousDirtyRegion(true);
if (dirtyRegion.isEmpty())
- dirtyRegion = renderable->boundingRect();
+ dirtyRegion = renderable->boundingRectMax();
m_dirtyRegion += dirtyRegion;
m_nodes.remove(node);
delete renderable;
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
index d754089ce4..77d21ec042 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp
@@ -74,6 +74,9 @@ QSGSoftwareImageNode::~QSGSoftwareImageNode()
void QSGSoftwareImageNode::setTexture(QSGTexture *texture)
{
+ if (m_owns)
+ delete m_texture;
+
m_texture = texture; markDirty(DirtyMaterial);
m_cachedMirroredPixmapIsDirty = true;
}
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
index 52984a4310..cecc6c21ca 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp
@@ -54,10 +54,28 @@
#include <private/qsgrendernode_p.h>
#include <private/qsgtexture_p.h>
+#include <qmath.h>
+
Q_LOGGING_CATEGORY(lcRenderable, "qt.scenegraph.softwarecontext.renderable")
QT_BEGIN_NAMESPACE
+// Largest subrectangle with integer coordinates
+inline QRect toRectMin(const QRectF & r)
+{
+ int x1 = qCeil(r.left());
+ int x2 = qFloor(r.right());
+ int y1 = qCeil(r.top());
+ int y2 = qFloor(r.bottom());
+ return QRect(x1, y1, x2 - x1, y2 - y1);
+}
+
+// Smallest superrectangle with integer coordinates
+inline QRect toRectMax(const QRectF & r)
+{
+ return r.toAlignedRect();
+}
+
QSGSoftwareRenderableNode::QSGSoftwareRenderableNode(NodeType type, QSGNode *node)
: m_nodeType(type)
, m_isOpaque(true)
@@ -117,7 +135,7 @@ void QSGSoftwareRenderableNode::update()
// Update the Node properties
m_isDirty = true;
- QRect boundingRect;
+ QRectF boundingRect;
switch (m_nodeType) {
case QSGSoftwareRenderableNode::SimpleRect:
@@ -126,7 +144,7 @@ void QSGSoftwareRenderableNode::update()
else
m_isOpaque = false;
- boundingRect = m_handle.simpleRectNode->rect().toRect();
+ boundingRect = m_handle.simpleRectNode->rect();
break;
case QSGSoftwareRenderableNode::SimpleTexture:
if (!m_handle.simpleTextureNode->texture()->hasAlphaChannel() && !m_transform.isRotating())
@@ -134,7 +152,7 @@ void QSGSoftwareRenderableNode::update()
else
m_isOpaque = false;
- boundingRect = m_handle.simpleTextureNode->rect().toRect();
+ boundingRect = m_handle.simpleTextureNode->rect();
break;
case QSGSoftwareRenderableNode::Image:
// There isn't a way to tell, so assume it's not
@@ -148,7 +166,7 @@ void QSGSoftwareRenderableNode::update()
else
m_isOpaque = false;
- boundingRect = QRect(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height());
+ boundingRect = QRectF(0, 0, m_handle.painterNode->size().width(), m_handle.painterNode->size().height());
break;
case QSGSoftwareRenderableNode::Rectangle:
if (m_handle.rectangleNode->isOpaque() && !m_transform.isRotating())
@@ -156,19 +174,19 @@ void QSGSoftwareRenderableNode::update()
else
m_isOpaque = false;
- boundingRect = m_handle.rectangleNode->rect().toRect();
+ boundingRect = m_handle.rectangleNode->rect();
break;
case QSGSoftwareRenderableNode::Glyph:
// Always has alpha
m_isOpaque = false;
- boundingRect = m_handle.glpyhNode->boundingRect().toAlignedRect();
+ boundingRect = m_handle.glpyhNode->boundingRect();
break;
case QSGSoftwareRenderableNode::NinePatch:
// Difficult to tell, assume non-opaque
m_isOpaque = false;
- boundingRect = m_handle.ninePatchNode->bounds().toRect();
+ boundingRect = m_handle.ninePatchNode->bounds();
break;
case QSGSoftwareRenderableNode::SimpleRectangle:
if (m_handle.simpleRectangleNode->color().alpha() == 255 && !m_transform.isRotating())
@@ -176,7 +194,7 @@ void QSGSoftwareRenderableNode::update()
else
m_isOpaque = false;
- boundingRect = m_handle.simpleRectangleNode->rect().toRect();
+ boundingRect = m_handle.simpleRectangleNode->rect();
break;
case QSGSoftwareRenderableNode::SimpleImage:
if (!m_handle.simpleImageNode->texture()->hasAlphaChannel() && !m_transform.isRotating())
@@ -184,12 +202,12 @@ void QSGSoftwareRenderableNode::update()
else
m_isOpaque = false;
- boundingRect = m_handle.simpleImageNode->rect().toRect();
+ boundingRect = m_handle.simpleImageNode->rect();
break;
#if QT_CONFIG(quick_sprite)
case QSGSoftwareRenderableNode::SpriteNode:
m_isOpaque = m_handle.spriteNode->isOpaque();
- boundingRect = m_handle.spriteNode->rect().toRect();
+ boundingRect = m_handle.spriteNode->rect();
break;
#endif
case QSGSoftwareRenderableNode::RenderNode:
@@ -198,27 +216,32 @@ void QSGSoftwareRenderableNode::update()
else
m_isOpaque = false;
- boundingRect = m_handle.renderNode->rect().toRect();
+ boundingRect = m_handle.renderNode->rect();
break;
default:
break;
}
- m_boundingRect = m_transform.mapRect(boundingRect);
+ const QRectF transformedRect = m_transform.mapRect(boundingRect);
+ m_boundingRectMin = toRectMin(transformedRect);
+ m_boundingRectMax = toRectMax(transformedRect);
if (m_hasClipRegion && m_clipRegion.rectCount() <= 1) {
// If there is a clipRegion, and it is empty, the item wont be rendered
- if (m_clipRegion.isEmpty())
- m_boundingRect = QRect();
- else
- m_boundingRect = m_boundingRect.intersected(m_clipRegion.rects().first());
+ if (m_clipRegion.isEmpty()) {
+ m_boundingRectMin = QRect();
+ m_boundingRectMax = QRect();
+ } else {
+ m_boundingRectMin = m_boundingRectMin.intersected(m_clipRegion.rects().constFirst());
+ m_boundingRectMax = m_boundingRectMax.intersected(m_clipRegion.rects().constFirst());
+ }
}
// Overrides
if (m_opacity < 1.0f)
m_isOpaque = false;
- m_dirtyRegion = QRegion(m_boundingRect);
+ m_dirtyRegion = QRegion(m_boundingRectMax);
}
struct RenderNodeState : public QSGRenderNode::RenderState
@@ -254,18 +277,21 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu
QMatrix4x4 m = m_transform;
rd->m_matrix = &m;
rd->m_opacity = m_opacity;
- RenderNodeState rs;
- rs.cr = m_clipRegion;
- const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
- ? m_boundingRect :
- QRect(0, 0, painter->device()->width(), painter->device()->height());
+ // all the clip region below is in world coordinates, taking m_transform into account already
+ QRegion cr = m_dirtyRegion;
+ if (m_clipRegion.rectCount() > 1)
+ cr &= m_clipRegion;
painter->save();
- painter->setClipRegion(br, Qt::ReplaceClip);
+ RenderNodeState rs;
+ rs.cr = cr;
m_handle.renderNode->render(&rs);
painter->restore();
+ const QRect br = m_handle.renderNode->flags().testFlag(QSGRenderNode::BoundedRectRendering)
+ ? m_boundingRectMax // already mapped to world
+ : QRect(0, 0, painter->device()->width(), painter->device()->height());
m_previousDirtyRegion = QRegion(br);
m_isDirty = false;
m_dirtyRegion = QRegion();
@@ -276,7 +302,7 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu
painter->save();
painter->setOpacity(m_opacity);
- // Set clipRegion to m_dirtyRegion (in world coordinates)
+ // Set clipRegion to m_dirtyRegion (in world coordinates, so must be done before the setTransform below)
// as m_dirtyRegion already accounts for clipRegion
painter->setClipRegion(m_dirtyRegion, Qt::ReplaceClip);
if (m_clipRegion.rectCount() > 1)
@@ -335,19 +361,13 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu
painter->restore();
QRegion areaToBeFlushed = m_dirtyRegion;
- m_previousDirtyRegion = QRegion(m_boundingRect);
+ m_previousDirtyRegion = QRegion(m_boundingRectMax);
m_isDirty = false;
m_dirtyRegion = QRegion();
return areaToBeFlushed;
}
-QRect QSGSoftwareRenderableNode::boundingRect() const
-{
- // This returns the bounding area of a renderable node in world coordinates
- return m_boundingRect;
-}
-
bool QSGSoftwareRenderableNode::isDirtyRegionEmpty() const
{
return m_dirtyRegion.isEmpty();
@@ -392,12 +412,12 @@ void QSGSoftwareRenderableNode::markMaterialDirty()
void QSGSoftwareRenderableNode::addDirtyRegion(const QRegion &dirtyRegion, bool forceDirty)
{
- // Check if the dirty region applys to this node
+ // Check if the dirty region applies to this node
QRegion prev = m_dirtyRegion;
- if (dirtyRegion.intersects(boundingRect())) {
+ if (dirtyRegion.intersects(m_boundingRectMax)) {
if (forceDirty)
m_isDirty = true;
- m_dirtyRegion += dirtyRegion.intersected(boundingRect());
+ m_dirtyRegion += dirtyRegion.intersected(m_boundingRectMax);
}
qCDebug(lcRenderable) << "addDirtyRegion: " << dirtyRegion << "old dirtyRegion: " << prev << "new dirtyRegion: " << m_dirtyRegion;
}
@@ -407,7 +427,7 @@ void QSGSoftwareRenderableNode::subtractDirtyRegion(const QRegion &dirtyRegion)
QRegion prev = m_dirtyRegion;
if (m_isDirty) {
// Check if this rect concerns us
- if (dirtyRegion.intersects(QRegion(boundingRect()))) {
+ if (dirtyRegion.intersects(m_boundingRectMax)) {
m_dirtyRegion -= dirtyRegion;
if (m_dirtyRegion.isEmpty())
m_isDirty = false;
@@ -423,7 +443,7 @@ QRegion QSGSoftwareRenderableNode::previousDirtyRegion(bool wasRemoved) const
if (wasRemoved)
return m_previousDirtyRegion;
- return m_previousDirtyRegion.subtracted(QRegion(m_boundingRect));
+ return m_previousDirtyRegion.subtracted(QRegion(m_boundingRectMax));
}
QRegion QSGSoftwareRenderableNode::dirtyRegion() const
diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
index 473578c185..8fc87db179 100644
--- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
+++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode_p.h
@@ -98,7 +98,8 @@ public:
void update();
QRegion renderNode(QPainter *painter, bool forceOpaquePainting = false);
- QRect boundingRect() const;
+ QRect boundingRectMin() const { return m_boundingRectMin; }
+ QRect boundingRectMax() const { return m_boundingRectMax; }
NodeType type() const { return m_nodeType; }
bool isOpaque() const { return m_isOpaque; }
bool isDirty() const { return m_isDirty; }
@@ -149,7 +150,8 @@ private:
bool m_hasClipRegion;
float m_opacity;
- QRect m_boundingRect;
+ QRect m_boundingRectMin;
+ QRect m_boundingRectMax;
};
QT_END_NAMESPACE
diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
index b8ebeaca63..78f2c86f6c 100644
--- a/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer.cpp
@@ -2966,7 +2966,11 @@ void Renderer::visualizeBatch(Batch *b)
for (int ds=0; ds<b->drawSets.size(); ++ds) {
const DrawSet &set = b->drawSets.at(ds);
glVertexAttribPointer(a.position, 2, a.type, false, g->sizeOfVertex(), (void *) (qintptr) (set.vertices));
+#ifdef QSG_SEPARATE_INDEX_BUFFER
+ glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (b->ibo.data + set.indices));
+#else
glDrawElements(g->drawingMode(), set.indexCount, GL_UNSIGNED_SHORT, (void *) (qintptr) (b->vbo.data + set.indices));
+#endif
}
} else {
Element *e = b->first;
diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp
index 7ac3914023..e400928d4e 100644
--- a/src/quick/scenegraph/coreapi/qsgnode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgnode.cpp
@@ -792,7 +792,7 @@ QSGBasicGeometryNode::~QSGBasicGeometryNode()
If the node has the flag QSGNode::OwnsGeometry set, it will also delete the
geometry object it is pointing to. This flag is not set by default.
- If the geometry is changed whitout calling setGeometry() again, the user
+ If the geometry is changed without calling setGeometry() again, the user
must also mark the geometry as dirty using QSGNode::markDirty().
\sa markDirty()
@@ -845,7 +845,7 @@ void QSGBasicGeometryNode::setGeometry(QSGGeometry *geometry)
The geometry node supports two types of materials, the opaqueMaterial and the normal
material. The opaqueMaterial is used when the accumulated scene graph opacity at the
- time of rendering is 1. The primary usecase is to special case opaque rendering
+ time of rendering is 1. The primary use case is to special case opaque rendering
to avoid an extra operation in the fragment shader can have significant performance
impact on embedded graphics chips. The opaque material is optional.
@@ -887,7 +887,7 @@ QSGGeometryNode::QSGGeometryNode(QSGGeometryNodePrivate &dd)
Deletes this geometry node.
The flags QSGNode::OwnsMaterial, QSGNode::OwnsOpaqueMaterial and
- QSGNode::OwnsGeometry decides weither the geometry node should also
+ QSGNode::OwnsGeometry decides whether the geometry node should also
delete the materials and geometry. By default, these flags are disabled.
*/
@@ -961,7 +961,7 @@ void QSGGeometryNode::setRenderOrder(int order)
Geometry nodes must have a material before they can be added to the
scene graph.
- If the material is changed whitout calling setMaterial() again, the user
+ If the material is changed without calling setMaterial() again, the user
must also mark the material as dirty using QSGNode::markDirty().
*/
@@ -991,7 +991,7 @@ void QSGGeometryNode::setMaterial(QSGMaterial *material)
allowed to set QSGMaterial::Blending to true and draw transparent
pixels.
- If the material is changed whitout calling setOpaqueMaterial()
+ If the material is changed without calling setOpaqueMaterial()
again, the user must also mark the opaque material as dirty using
QSGNode::markDirty().
@@ -1371,7 +1371,7 @@ void QSGOpacityNode::setOpacity(qreal opacity)
Returns this node's accumulated opacity.
- This vaule is calculated during rendering and only stored
+ This value is calculated during rendering and only stored
in the opacity node temporarily.
\internal
diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
index bb2671f6c3..8fbd402a2f 100644
--- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp
@@ -99,7 +99,7 @@ void QSGBindableFboId::bind() const
#endif
/*!
\class QSGRenderer
- \brief The renderer class is the abstract baseclass use for rendering the
+ \brief The renderer class is the abstract baseclass used for rendering the
QML scene graph.
The renderer is not tied to any particular surface. It expects a context to
@@ -295,7 +295,7 @@ void QSGRenderer::preprocess()
// We need to take a copy here, in case any of the preprocess calls deletes a node that
// is in the preprocess list and thus, changes the m_nodes_to_preprocess behind our backs
- // For the default case, when this does not happen, the cost is neglishible.
+ // For the default case, when this does not happen, the cost is negligible.
QSet<QSGNode *> items = m_nodes_to_preprocess;
for (QSet<QSGNode *>::const_iterator it = items.constBegin();
diff --git a/src/quick/scenegraph/coreapi/qsgrendernode.cpp b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
index 1bc0210b72..a8954848d6 100644
--- a/src/quick/scenegraph/coreapi/qsgrendernode.cpp
+++ b/src/quick/scenegraph/coreapi/qsgrendernode.cpp
@@ -267,6 +267,10 @@ QSGRenderNode::RenderingFlags QSGRenderNode::flags() const
For rendernodes covering the entire area of a corresponding QQuickItem the
return value will be (0, 0, item->width(), item->height()).
+ \note Nodes are also free to render outside the boundaries specified by the
+ item's width and height, since the scenegraph nodes are not bounded by the
+ QQuickItem geometry, as long as this is reported correctly from this function.
+
\sa flags()
*/
QRectF QSGRenderNode::rect() const
@@ -359,7 +363,10 @@ QSGRenderNode::RenderState::~RenderState()
of the render state is not in use. However, the clip region that can be set
on the QPainter still has to be communicated since reconstructing this
manually in render() is not reasonable. It can therefore be queried via
- this function.
+ this function. The region is in world coordinates and can be passed
+ to QPainter::setClipRegion() with Qt::ReplaceClip. This must be done before
+ calling QPainter::setTransform() since the clip region is already mapped to
+ the transform provided in QSGRenderNode::matrix().
*/
/*!
diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp
index 2be84f4aec..d460794573 100644
--- a/src/quick/scenegraph/qsgcontext.cpp
+++ b/src/quick/scenegraph/qsgcontext.cpp
@@ -228,14 +228,6 @@ public:
int m_good;
};
-class QSGTextureCleanupEvent : public QEvent
-{
-public:
- QSGTextureCleanupEvent(QSGTexture *t) : QEvent(QEvent::User), texture(t) { }
- ~QSGTextureCleanupEvent() { delete texture; }
- QSGTexture *texture;
-};
-
/*!
\class QSGContext
diff --git a/src/quick/scenegraph/qsgcontextplugin.cpp b/src/quick/scenegraph/qsgcontextplugin.cpp
index b8b5141957..bd311d3b46 100644
--- a/src/quick/scenegraph/qsgcontextplugin.cpp
+++ b/src/quick/scenegraph/qsgcontextplugin.cpp
@@ -49,6 +49,9 @@
#include <QtQuick/private/qsgdefaultcontext_p.h>
#endif
+#include <QtGui/private/qguiapplication_p.h>
+#include <QtGui/qpa/qplatformintegration.h>
+
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_INFO)
@@ -128,12 +131,12 @@ QSGAdaptationBackendData *contextFactory()
if (requestedBackend.isEmpty() && qEnvironmentVariableIsSet("QT_QUICK_BACKEND"))
requestedBackend = QString::fromLocal8Bit(qgetenv("QT_QUICK_BACKEND"));
-#if !QT_CONFIG(opengl)
- // If this is a build without OpenGL, and no backend has been set
+ // If this platform does not support OpenGL, and no backend has been set
// default to the software renderer
- if (requestedBackend.isEmpty())
+ if (requestedBackend.isEmpty()
+ && !QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::OpenGL)) {
requestedBackend = QString::fromLocal8Bit("software");
-#endif
+ }
if (!requestedBackend.isEmpty()) {
qCDebug(QSG_LOG_INFO) << "Loading backend" << requestedBackend;
diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
index ba25172d2f..7789ef8fb1 100644
--- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
+++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp
@@ -71,12 +71,15 @@ QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLCont
, m_coreFuncs(0)
#endif
{
- m_blitBuffer.create();
- m_blitBuffer.bind();
- static GLfloat buffer[16] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f,
- 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
- m_blitBuffer.allocate(buffer, sizeof(buffer));
- m_blitBuffer.release();
+ if (Q_LIKELY(m_blitBuffer.create())) {
+ m_blitBuffer.bind();
+ static const GLfloat buffer[16] = {-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f};
+ m_blitBuffer.allocate(buffer, sizeof(buffer));
+ m_blitBuffer.release();
+ } else {
+ qWarning("Buffer creation failed");
+ }
m_areaAllocator = new QSGAreaAllocator(QSize(maxTextureSize(), m_maxTextureCount * maxTextureSize()));
}
diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
index bffffc36ad..29600ef0ca 100644
--- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp
+++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp
@@ -259,7 +259,7 @@ void QSGDefaultRenderContext::compileShader(QSGMaterialShader *shader, QSGMateri
if (vertexCode || fragmentCode) {
Q_ASSERT_X((material->flags() & QSGMaterial::CustomCompileStep) == 0,
"QSGRenderContext::compile()",
- "materials with custom compile step cannot have custom vertex/fragment code");
+ "materials with custom compile step cannot have modified vertex or fragment code");
QOpenGLShaderProgram *p = shader->program();
p->addCacheableShaderFromSourceCode(QOpenGLShader::Vertex, vertexCode ? vertexCode : shader->vertexShader());
p->addCacheableShaderFromSourceCode(QOpenGLShader::Fragment, fragmentCode ? fragmentCode : shader->fragmentShader());
diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp
index 1c19876a76..4f11d95e70 100644
--- a/src/quick/scenegraph/util/qsgtexture.cpp
+++ b/src/quick/scenegraph/util/qsgtexture.cpp
@@ -256,13 +256,15 @@ static void qt_debug_remove_texture(QSGTexture* texture)
Specifies how the texture should treat texture coordinates.
- \note Texture wrapping needs to be handled explicitly for atlas textures.
-
- \value Repeat Only the factional part of the texture coordiante is
+ \value Repeat Only the fractional part of the texture coordinate is
used, causing values above 1 and below 0 to repeat.
\value ClampToEdge Values above 1 are clamped to 1 and values
below 0 are clamped to 0.
+
+ \value MirroredRepeat When the texture coordinate is even, only the
+ fractional part is used. When odd, the texture coordinate is set to
+ \c{1 - fractional part}. This value has been introduced in Qt 5.10.
*/
/*!
@@ -607,7 +609,9 @@ void QSGTexture::updateBindOptions(bool force)
if (force || d->wrapChanged) {
#ifndef QT_NO_DEBUG
- if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat) {
+ if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat
+ || d->horizontalWrap == MirroredRepeat || d->verticalWrap == MirroredRepeat)
+ {
bool npotSupported = QOpenGLFunctions(QOpenGLContext::currentContext()).hasOpenGLFeature(QOpenGLFunctions::NPOTTextures);
QSize size = textureSize();
bool isNpot = !isPowerOfTwo(size.width()) || !isPowerOfTwo(size.height());
@@ -615,8 +619,18 @@ void QSGTexture::updateBindOptions(bool force)
qWarning("Scene Graph: This system does not support the REPEAT wrap mode for non-power-of-two textures.");
}
#endif
- funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, d->horizontalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
- funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, d->verticalWrap == Repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
+ GLenum wrapS = GL_CLAMP_TO_EDGE;
+ if (d->horizontalWrap == Repeat)
+ wrapS = GL_REPEAT;
+ else if (d->horizontalWrap == MirroredRepeat)
+ wrapS = GL_MIRRORED_REPEAT;
+ GLenum wrapT = GL_CLAMP_TO_EDGE;
+ if (d->verticalWrap == Repeat)
+ wrapT = GL_REPEAT;
+ else if (d->verticalWrap == MirroredRepeat)
+ wrapT = GL_MIRRORED_REPEAT;
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS);
+ funcs->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapT);
d->wrapChanged = false;
}
#else
diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/util/qsgtexture.h
index 035acc02b1..032129434e 100644
--- a/src/quick/scenegraph/util/qsgtexture.h
+++ b/src/quick/scenegraph/util/qsgtexture.h
@@ -58,7 +58,8 @@ public:
enum WrapMode {
Repeat,
- ClampToEdge
+ ClampToEdge,
+ MirroredRepeat
};
enum Filtering {
diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h
index 36f9b802ba..52dc6db2d0 100644
--- a/src/quick/scenegraph/util/qsgtexture_p.h
+++ b/src/quick/scenegraph/util/qsgtexture_p.h
@@ -71,8 +71,8 @@ public:
uint filteringChanged : 1;
uint anisotropyChanged : 1;
- uint horizontalWrap : 1;
- uint verticalWrap : 1;
+ uint horizontalWrap : 2;
+ uint verticalWrap : 2;
uint mipmapMode : 2;
uint filterMode : 2;
uint anisotropyLevel: 3;
diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp
index c536445e82..fbc8f27a63 100644
--- a/src/quick/scenegraph/util/qsgtexturematerial.cpp
+++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp
@@ -280,7 +280,7 @@ void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture)
Returns this material's horizontal wrap mode.
- The default horizontal wrap mode is \c QSGTexutre::ClampToEdge.
+ The default horizontal wrap mode is \c QSGTexture::ClampToEdge.
*/
@@ -301,7 +301,7 @@ void QSGOpaqueTextureMaterial::setTexture(QSGTexture *texture)
Returns this material's vertical wrap mode.
- The default vertical wrap mode is \c QSGTexutre::ClampToEdge.
+ The default vertical wrap mode is \c QSGTexture::ClampToEdge.
*/
diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp
index 2070fd7ff0..1d2f3de1df 100644
--- a/src/quick/util/qquickglobal.cpp
+++ b/src/quick/util/qquickglobal.cpp
@@ -814,6 +814,11 @@ public:
return false;
#endif
}
+
+ QString pluginName() const override
+ {
+ return QGuiApplication::platformName();
+ }
};
diff --git a/src/quick/util/qquickpath.cpp b/src/quick/util/qquickpath.cpp
index 8abb9377a6..15defdc01b 100644
--- a/src/quick/util/qquickpath.cpp
+++ b/src/quick/util/qquickpath.cpp
@@ -68,7 +68,7 @@ QT_BEGIN_NAMESPACE
\instantiates QQuickPath
\inqmlmodule QtQuick
\ingroup qtquick-animation-paths
- \brief Defines a path for use by \l PathView
+ \brief Defines a path for use by \l PathView and \l Shape
A Path is composed of one or more path segments - PathLine, PathQuad,
PathCubic, PathArc, PathCurve, PathSvg.
@@ -79,13 +79,85 @@ QT_BEGIN_NAMESPACE
PathAttribute allows named attributes with values to be defined
along the path.
- \sa PathView, PathAttribute, PathPercent, PathLine, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
+ Path and the other types for specifying path elements are shared between
+ \l PathView and \l Shape. The following table provides an overview of the
+ applicability of the various path elements:
+
+ \table
+ \header
+ \li Element
+ \li PathView
+ \li Shape
+ \li Shape, GL_NV_path_rendering
+ \li Shape, software
+ \row
+ \li PathMove
+ \li N/A
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathLine
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathQuad
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathCubic
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathArc
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathSvg
+ \li Yes
+ \li Yes
+ \li Yes
+ \li Yes
+ \row
+ \li PathAttribute
+ \li Yes
+ \li N/A
+ \li N/A
+ \li N/A
+ \row
+ \li PathPercent
+ \li Yes
+ \li N/A
+ \li N/A
+ \li N/A
+ \row
+ \li PathCurve
+ \li Yes
+ \li No
+ \li No
+ \li No
+ \endtable
+
+ \sa PathView, Shape, PathAttribute, PathPercent, PathLine, PathMove, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
*/
QQuickPath::QQuickPath(QObject *parent)
: QObject(*(new QQuickPathPrivate), parent)
{
}
+QQuickPath::QQuickPath(QQuickPathPrivate &dd, QObject *parent)
+ : QObject(dd, parent)
+{
+}
+
QQuickPath::~QQuickPath()
{
}
@@ -1003,7 +1075,7 @@ void QQuickPathAttribute::setValue(qreal value)
}
\endqml
- \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg
+ \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathMove
*/
/*!
@@ -1046,6 +1118,64 @@ void QQuickPathLine::addToPath(QPainterPath &path, const QQuickPathData &data)
/****************************************************************************/
/*!
+ \qmltype PathMove
+ \instantiates QQuickPathMove
+ \inqmlmodule QtQuick
+ \ingroup qtquick-animation-paths
+ \brief Moves the Path's position
+
+ The example below creates a path consisting of two horizontal lines with
+ some empty space between them. All three segments have a width of 100:
+
+ \qml
+ Path {
+ startX: 0; startY: 100
+ PathLine { relativeX: 100; y: 100 }
+ PathMove { relativeX: 100; y: 100 }
+ PathLine { relativeX: 100; y: 100 }
+ }
+ \endqml
+
+ \note PathMove should not be used in a Path associated with a PathView. Use
+ PathLine instead. For ShapePath however it is important to distinguish
+ between the operations of drawing a straight line and moving the path
+ position without drawing anything.
+
+ \sa Path, PathQuad, PathCubic, PathArc, PathCurve, PathSvg, PathLine
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathMove::x
+ \qmlproperty real QtQuick::PathMove::y
+
+ Defines the position to move to.
+
+ \sa relativeX, relativeY
+*/
+
+/*!
+ \qmlproperty real QtQuick::PathMove::relativeX
+ \qmlproperty real QtQuick::PathMove::relativeY
+
+ Defines the position to move to relative to its start.
+
+ If both a relative and absolute end position are specified for a single axis, the relative
+ position will be used.
+
+ Relative and absolute positions can be mixed, for example it is valid to set a relative x
+ and an absolute y.
+
+ \sa x, y
+*/
+
+void QQuickPathMove::addToPath(QPainterPath &path, const QQuickPathData &data)
+{
+ path.moveTo(positionForCurve(data, path.currentPosition()));
+}
+
+/****************************************************************************/
+
+/*!
\qmltype PathQuad
\instantiates QQuickPathQuad
\inqmlmodule QtQuick
@@ -1641,6 +1771,7 @@ void QQuickPathArc::setRadiusX(qreal radius)
_radiusX = radius;
emit radiusXChanged();
+ emit changed();
}
qreal QQuickPathArc::radiusY() const
@@ -1655,6 +1786,7 @@ void QQuickPathArc::setRadiusY(qreal radius)
_radiusY = radius;
emit radiusYChanged();
+ emit changed();
}
/*!
@@ -1688,6 +1820,7 @@ void QQuickPathArc::setUseLargeArc(bool largeArc)
_useLargeArc = largeArc;
emit useLargeArcChanged();
+ emit changed();
}
/*!
@@ -1719,6 +1852,43 @@ void QQuickPathArc::setDirection(ArcDirection direction)
_direction = direction;
emit directionChanged();
+ emit changed();
+}
+
+/*!
+ \qmlproperty real QtQuick::PathArc::xAxisRotation
+
+ Defines the rotation of the arc, in degrees. The default value is 0.
+
+ An arc is a section of circles or ellipses. Given the radius and the start
+ and end points, there are two ellipses that connect the points. This
+ property defines the rotation of the X axis of these ellipses.
+
+ \note The value is only useful when the x and y radius differ, meaning the
+ arc is a section of ellipses.
+
+ The following QML demonstrates how different radius values can be used to change
+ the shape of the arc:
+ \table
+ \row
+ \li \image declarative-arcrotation.png
+ \li \snippet qml/path/arcrotation.qml 0
+ \endtable
+*/
+
+qreal QQuickPathArc::xAxisRotation() const
+{
+ return _xAxisRotation;
+}
+
+void QQuickPathArc::setXAxisRotation(qreal rotation)
+{
+ if (_xAxisRotation == rotation)
+ return;
+
+ _xAxisRotation = rotation;
+ emit xAxisRotationChanged();
+ emit changed();
}
void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
@@ -1728,7 +1898,7 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
QQuickSvgParser::pathArc(path,
_radiusX,
_radiusY,
- 0, //xAxisRotation
+ _xAxisRotation,
_useLargeArc,
_direction == Clockwise ? 1 : 0,
endPoint.x(),
@@ -1758,6 +1928,11 @@ void QQuickPathArc::addToPath(QPainterPath &path, const QQuickPathData &data)
\endqml
\endtable
+ \note Mixing PathSvg with other type of elements is not always supported.
+ For example, when \l Shape is backed by \c{GL_NV_path_rendering}, a
+ ShapePath can contain one or more PathSvg elements, or one or more other
+ type of elements, but not both.
+
\sa Path, PathLine, PathQuad, PathCubic, PathArc, PathCurve
*/
@@ -1782,6 +1957,7 @@ void QQuickPathSvg::setPath(const QString &path)
_path = path;
emit pathChanged();
+ emit changed();
}
void QQuickPathSvg::addToPath(QPainterPath &path, const QQuickPathData &)
diff --git a/src/quick/util/qquickpath_p.h b/src/quick/util/qquickpath_p.h
index c0a96cad4f..b7fde5c272 100644
--- a/src/quick/util/qquickpath_p.h
+++ b/src/quick/util/qquickpath_p.h
@@ -159,6 +159,15 @@ public:
void addToPath(QPainterPath &path, const QQuickPathData &) override;
};
+class Q_QUICK_PRIVATE_EXPORT QQuickPathMove : public QQuickCurve
+{
+ Q_OBJECT
+public:
+ QQuickPathMove(QObject *parent=0) : QQuickCurve(parent) {}
+
+ void addToPath(QPainterPath &path, const QQuickPathData &) override;
+};
+
class Q_QUICK_PRIVATE_EXPORT QQuickPathQuad : public QQuickCurve
{
Q_OBJECT
@@ -281,10 +290,11 @@ class Q_QUICK_PRIVATE_EXPORT QQuickPathArc : public QQuickCurve
Q_PROPERTY(qreal radiusY READ radiusY WRITE setRadiusY NOTIFY radiusYChanged)
Q_PROPERTY(bool useLargeArc READ useLargeArc WRITE setUseLargeArc NOTIFY useLargeArcChanged)
Q_PROPERTY(ArcDirection direction READ direction WRITE setDirection NOTIFY directionChanged)
+ Q_PROPERTY(qreal xAxisRotation READ xAxisRotation WRITE setXAxisRotation NOTIFY xAxisRotationChanged REVISION 2)
public:
QQuickPathArc(QObject *parent=0)
- : QQuickCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise) {}
+ : QQuickCurve(parent), _radiusX(0), _radiusY(0), _useLargeArc(false), _direction(Clockwise), _xAxisRotation(0) {}
enum ArcDirection { Clockwise, Counterclockwise };
Q_ENUM(ArcDirection)
@@ -301,6 +311,9 @@ public:
ArcDirection direction() const;
void setDirection(ArcDirection direction);
+ qreal xAxisRotation() const;
+ void setXAxisRotation(qreal rotation);
+
void addToPath(QPainterPath &path, const QQuickPathData &) override;
Q_SIGNALS:
@@ -308,12 +321,14 @@ Q_SIGNALS:
void radiusYChanged();
void useLargeArcChanged();
void directionChanged();
+ Q_REVISION(2) void xAxisRotationChanged();
private:
qreal _radiusX;
qreal _radiusY;
bool _useLargeArc;
ArcDirection _direction;
+ qreal _xAxisRotation;
};
class Q_QUICK_PRIVATE_EXPORT QQuickPathSvg : public QQuickCurve
@@ -404,6 +419,7 @@ Q_SIGNALS:
void startYChanged();
protected:
+ QQuickPath(QQuickPathPrivate &dd, QObject *parent = nullptr);
void componentComplete() override;
void classBegin() override;
void disconnectPathElements();
@@ -458,6 +474,7 @@ QML_DECLARE_TYPE(QQuickPathElement)
QML_DECLARE_TYPE(QQuickPathAttribute)
QML_DECLARE_TYPE(QQuickCurve)
QML_DECLARE_TYPE(QQuickPathLine)
+QML_DECLARE_TYPE(QQuickPathMove)
QML_DECLARE_TYPE(QQuickPathQuad)
QML_DECLARE_TYPE(QQuickPathCubic)
QML_DECLARE_TYPE(QQuickPathCatmullRomCurve)
diff --git a/src/quick/util/qquickpath_p_p.h b/src/quick/util/qquickpath_p_p.h
index 1dc3c1c47a..8ce85dbf0f 100644
--- a/src/quick/util/qquickpath_p_p.h
+++ b/src/quick/util/qquickpath_p_p.h
@@ -64,7 +64,7 @@ QT_REQUIRE_CONFIG(quick_path);
QT_BEGIN_NAMESPACE
-class QQuickPathPrivate : public QObjectPrivate
+class Q_QUICK_PRIVATE_EXPORT QQuickPathPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQuickPath)
diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp
index 8df1a892e4..e026608150 100644
--- a/src/quick/util/qquickpixmapcache.cpp
+++ b/src/quick/util/qquickpixmapcache.cpp
@@ -497,7 +497,7 @@ void QQuickPixmapReader::networkRequestDone(QNetworkReply *reply)
QByteArray all = reply->readAll();
QBuffer buff(&all);
buff.open(QIODevice::ReadOnly);
- if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize, job->data->providerOptions))
+ if (!readImage(reply->url(), &buff, &image, &errorString, &readSize, job->requestSize, job->providerOptions))
error = QQuickPixmapReply::Decoding;
}
// send completion event to the QQuickPixmapReply
diff --git a/src/quick/util/qquicksvgparser.cpp b/src/quick/util/qquicksvgparser.cpp
index 310b600965..086c6d0b28 100644
--- a/src/quick/util/qquicksvgparser.cpp
+++ b/src/quick/util/qquicksvgparser.cpp
@@ -45,8 +45,6 @@
QT_BEGIN_NAMESPACE
-static const double Q_PI = 3.14159265358979323846; // pi
-
//copied from Qt SVG (qsvghandler.cpp).
Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
// '0' is 0x30 and '9' is 0x39
@@ -253,11 +251,11 @@ void QQuickSvgParser::pathArc(QPainterPath &path,
th_arc = th1 - th0;
if (th_arc < 0 && sweep_flag)
- th_arc += 2 * Q_PI;
+ th_arc += 2 * M_PI;
else if (th_arc > 0 && !sweep_flag)
- th_arc -= 2 * Q_PI;
+ th_arc -= 2 * M_PI;
- n_segs = qCeil(qAbs(th_arc / (Q_PI * 0.5 + 0.001)));
+ n_segs = qCeil(qAbs(th_arc / (M_PI * 0.5 + 0.001)));
for (i = 0; i < n_segs; i++) {
pathArcSegment(path, xc, yc,
diff --git a/src/quick/util/qquicksvgparser_p.h b/src/quick/util/qquicksvgparser_p.h
index 44b0d1b6dd..1777b99bf4 100644
--- a/src/quick/util/qquicksvgparser_p.h
+++ b/src/quick/util/qquicksvgparser_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <private/qtquickglobal_p.h>
#include <QtCore/qstring.h>
#include <QtGui/qpainterpath.h>
@@ -59,9 +60,9 @@ QT_BEGIN_NAMESPACE
namespace QQuickSvgParser
{
bool parsePathDataFast(const QString &dataStr, QPainterPath &path);
- void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation,
- int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx,
- qreal cury);
+ Q_QUICK_PRIVATE_EXPORT void pathArc(QPainterPath &path, qreal rx, qreal ry, qreal x_axis_rotation,
+ int large_arc_flag, int sweep_flag, qreal x, qreal y, qreal curx,
+ qreal cury);
}
QT_END_NAMESPACE
diff --git a/src/quick/util/qquickvalidator.cpp b/src/quick/util/qquickvalidator.cpp
index a05117bd06..93f414fe80 100644
--- a/src/quick/util/qquickvalidator.cpp
+++ b/src/quick/util/qquickvalidator.cpp
@@ -219,6 +219,19 @@ void QQuickDoubleValidator::resetLocaleName()
matching "a".
By default, this property contains a regular expression with the pattern .* that matches any string.
+
+ Below you can find an example of a \l TextInput object with a RegExpValidator specified:
+
+ \snippet qml/regexp.qml 0
+
+ Some more examples of regular expressions:
+
+ \list
+ \li A list of numbers with one to three positions separated by a comma:
+ /\d{1,3}(?:,\d{1,3})+$/
+ \li An amount consisting of up to 3 numbers before the decimal point, and
+ 1 to 2 after the decimal point: \li /(\d{1,3})([.,]\d{1,2})?$/
+ \endlist
*/
#endif // validator
diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp
index 4d021cb680..2e8623f508 100644
--- a/src/quickwidgets/qquickwidget.cpp
+++ b/src/quickwidgets/qquickwidget.cpp
@@ -1440,6 +1440,9 @@ bool QQuickWidget::event(QEvent *e)
d->offscreenWindow->setWindowState(resolveWindowState(windowState()));
break;
+ case QEvent::ShortcutOverride:
+ return QCoreApplication::sendEvent(d->offscreenWindow, e);
+
default:
break;
}
@@ -1596,10 +1599,10 @@ QQuickWindow *QQuickWidget::quickWindow() const
void QQuickWidget::paintEvent(QPaintEvent *event)
{
- Q_UNUSED(event)
Q_D(QQuickWidget);
if (d->useSoftwareRenderer) {
QPainter painter(this);
+ d->updateRegion = d->updateRegion.united(event->region());
if (d->updateRegion.isNull()) {
//Paint everything
painter.drawImage(rect(), d->softwareImage);