Commit Graph

278 Commits

Author SHA1 Message Date
Lars Knoll 74c8fe8675 Always set the correct FunctionObject when calling JS functions
Renamed ScopedCallData to JSCall, enforced passing a JS
FunctionObject to it, and added call() and callAsConstructor()
methods to it.

Change-Id: I30db65c9765c2896b5909fe2105c0934c6dad861
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-09-02 07:12:17 +00:00
Lars Knoll aa8f956e8d Move ScopedCallData and ScopedStackFrame into a separate file
Change-Id: I9ae42aa7a811aa93fe0950725e9d253a0c5e8dba
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-09-01 12:31:19 +00:00
Lars Knoll 4e0174a88e Move line number information into a side table
Don't emit any Line instructions anymore, and instead store
the info in a side table in the compiled data, where it can
be looked up on demand.

Change-Id: Idcaf3bf4ee4129fd62f9e717bf1277dc6a34fe19
Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
2017-08-25 12:05:59 +00:00
Lars Knoll f1aff1f2d4 Merge remote-tracking branch 'origin/dev' into wip/new-backend
Change-Id: Iff06429f948ac6cdec77a9e5bb8c5375c56fe705
2017-08-22 17:17:57 +02:00
Simon Hausmann 43a615e309 Merge remote-tracking branch 'origin/5.9' into dev
Conflicts:
	src/qml/compiler/qqmltypecompiler.cpp
	src/qml/jsruntime/qv4qmlcontext.cpp
	src/qml/jsruntime/qv4qobjectwrapper.cpp
	src/qml/qml/qqmlcustomparser.cpp
	src/qml/qml/qqmlimport.cpp
	src/qml/qml/qqmlimport_p.h
	src/qml/qml/qqmlmetatype.cpp
	src/qml/qml/qqmlmetatype_p.h
	src/qml/qml/qqmltypenamecache.cpp
	src/qml/qml/qqmltypenamecache_p.h
	src/qml/qml/qqmltypewrapper.cpp
	src/qml/qml/qqmltypewrapper_p.h
	src/qml/qml/qqmlvmemetaobject.cpp
	src/qml/util/qqmladaptormodel.cpp

Change-Id: Ic959d03e6f9c328fb02710d9abbb0f27cddde131
2017-08-18 11:46:11 +02:00
Lars Knoll 5bc4f4d958 Refactor context handling
Fix the push/pop context instructions to not modify the JS
stack anymore, as that can cause conflicts with the VME
(and was an ugly hack in any case). Instead, these instructions
not return the old context, that is then stored in a temporary.

Get rid of Engine::current and Engine::currentContext. The
StackFrame structures do now contain the only and authoritive
data. This finally gives us a nice setup where we create and
destroy frames on the stack when entering/leaving functions.

Change-Id: If161e3e941f59865c47ecfe1e094faf62b52bfa0
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-08-10 08:19:18 +00:00
Lars Knoll 50e7badd5f Remove Scope::result and convert calling convention for builtins
Allow for faster calling of builtins, and completely avoid
scope creation in many cases.

Change-Id: I0f1681e19e9908db10def85a74e134a87fc2e44c
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-08-08 18:58:14 +00:00
Lars Knoll c0f961cd6b Change function signatures for call/construct back
Change those back again to return a value. This will be required
to avoid creation of Scope objects between JS function calls.

Change-Id: I05cb5cf8fd0c13dcefa60d213ccd5983fab57ea3
Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
2017-08-04 07:08:19 +00:00
Lars Knoll be70a025c1 Don't store the current line number in the ExecutionContext
Instead modify our StackFrame struct to hold the
QV4::Function and have a linked list of those for
the frames.

Change-Id: I8676e16bc51a5ba6cf25a5b3423576d44e8a926a
Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
2017-08-04 07:08:02 +00:00
Lars Knoll 49a11e8820 Use QQmlType by value
QQmlType is now refcounted, and we need to use it by
value, to control it's lifetime properly. This is
required, so we can clean up the QQmlMetaTypeData
cache on engine destruction and with trimComponentCache()

Task-number: QTBUG-61536
Change-Id: If86391c86ea20a646ded7c9925d8f743f628fb91
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-08-02 16:32:43 +00:00
Lars Knoll a6633e41e7 Use QQmlType by value in the type wrapper
Task-number: QTBUG-61536
Change-Id: Ie40cb3a6e170331b0ec7ab5deaf7c1d7ef0cdaeb
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-08-02 16:32:37 +00:00
Liang Qi c254cec22a Merge remote-tracking branch 'origin/5.9' into dev
Conflicts:
	.qmake.conf
	src/qml/jsruntime/qv4argumentsobject.cpp
	src/qml/jsruntime/qv4arraydata.cpp
	src/qml/jsruntime/qv4context.cpp
	src/qml/jsruntime/qv4context_p.h
	src/qml/jsruntime/qv4errorobject.cpp
	src/qml/jsruntime/qv4functionobject.cpp
	src/qml/jsruntime/qv4internalclass.cpp
	src/qml/jsruntime/qv4lookup.cpp
	src/qml/jsruntime/qv4managed.cpp
	src/qml/jsruntime/qv4managed_p.h
	src/qml/jsruntime/qv4object.cpp
	src/qml/jsruntime/qv4object_p.h
	src/qml/jsruntime/qv4qmlcontext.cpp
	src/qml/jsruntime/qv4runtime.cpp
	src/qml/jsruntime/qv4vme_moth.cpp
	src/qml/memory/qv4heap_p.h
	src/qml/memory/qv4mm.cpp
	src/qml/memory/qv4mm_p.h
	src/qml/memory/qv4mmdefs_p.h
	src/quick/scenegraph/util/qsgdistancefieldutil.cpp
	src/quick/scenegraph/util/qsgdistancefieldutil_p.h
	tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp

Change-Id: I7ed925d4f5d308f872a58ddf51fdce0c8494ec9c
2017-06-06 15:59:38 +02:00
Lars Knoll 8bc243f569 Move the engine() accessor from Object to Managed
We can easily do this now that Managed has a pointer to an
internal class (which always has a back pointer to the
ExecutionEngine).

Remove the extra engine pointer from ExecutionContext, and clean
up tow methods in String.

Change-Id: I98d750b1afbdeadf42e66ae0c92c48db1a7adc31
Reviewed-by: Robin Burchell <robin.burchell@crimson.no>
2017-05-19 18:54:54 +00:00
Lars Knoll cae7975a03 Move the internalClass field from Heap::Object to Heap::Base
And do not store the vtable in Heap::Base anymore. This change
makes the internal class the main distinguishing feature
of all garbage collected objects.

It also saves one pointer on all Objects. No measurable
impact on runtime performance.

Change-Id: I040a28b7581b993f1886b5219e279173dfa567e8
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-05-19 06:23:26 +00:00
Olivier JG 4425d6f9b6 QML: emit messages when breaking bindings
Add a new logging category that can be used to debug issues with
unexpected binding breakages caused by assignment to previously bound
properties. If enabled, outputs a message when a binding is broken with
detailed location and property/value information.

[ChangeLog][QtQml] The QML engine can now emit informational messages
(in the "qt.qml.binding.removal" logging category) whenever a binding is
lost due to an imperative assignment. This can be used to debug issues
due to broken bindings.

Change-Id: Ie31e5a198450b6f998c1266acfc97e8f33a92e3d
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-05-17 09:23:39 +00:00
Robin Burchell 161d0854ba QmlTypeWrapper: -> QQmlTypeWrapper
That poor Q looked so lonely.

Change-Id: Ie4cef3fa8f2ecb8ba106654e8a9d6611a9407aa2
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-05-04 09:31:37 +00:00
Lars Knoll c3e1e6390e Merge remote-tracking branch 'origin/5.9' into dev
Change-Id: I71275a2076c3d32ee2896571be882067320a2e9e
2017-05-02 08:40:48 +02:00
Ville Voutilainen 4be29bdbd5 Don't wrap std::vector into a QVariant when passing it to a Q_INVOKABLE
Task-number: QTBUG-60386
Change-Id: Idd5a8939a575c254636042b5cb1900d2d8673072
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-04-27 14:31:41 +00:00
Thiago Macieira 80dc036882 Run includemocs in qtdeclarative
Change-Id: I84e363d735b443cb9beefffd14b8c023a37aa489
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
2017-04-26 13:19:13 +00:00
Robin Burchell 1a3dd2031a QObjectWrapper: Try harder to avoid creating wrappers if we can
When searching a QObject for properties, try to find QObject properties first,
and then bail if we don't find a match and the object is not already wrapped.

It makes no sense to wrap the object just to check the (empty) JS side
props, which is often the case with QmlContextWrapper's scopeObject and
contextObject. This can especially be seen when looking at QML's model
activity, as they set a context object in order to hijack lookup of
model data & index.

This can be seen in a test like the following:

    Repeater {
        model: 10
        delegate: Text {
            text: index
        }
    }

Before this patch, the 'index' access would previously trigger wrapping
of the QQmlDelegateModelItem. The same applies to the 'model' prop etc.

Noteworthy improvements of a full qmlbench run that aren't part of the "usual"
statistical noise are generally around the 5% mark.

Outside performance, the advantages are also clear on memory. Examining
for instance delegates_flipable.qml with QV4_MM_STATS=1 on my MBP:

Before:
     Freed JS type: QmlContext (5000 instances)
     Freed JS type: QObjectWrapper (5000 instances)
     Freed JS type: QmlContextWrapper (5000 instances)
     Freed JS type: QQuickItemWrapper (5000 instances)

After:
     Freed JS type: QmlContext (5000 instances)
     Freed JS type: QmlContextWrapper (5000 instances)
     Freed JS type: QQuickItemWrapper (5000 instances)

... which is what we expect, no more QObjectWrappers here, a lot less garbage
to clear up.

Timewise, while the measured mark/sweep times are not stable, there is a
proportional decrease of about 40% in time for both mark and sweep phases in
the GC (slightly more on TX1 than desktop, curiously).

Finally, GC's memory use is quite stable for each round of the test, and in this
case, we see a decrease of 378kb used at the time of each GC.

Before:
     Allocated 2515968 bytes in 39 chunks
     Used memory before GC: 2489824
     Used memory after GC : 889856
     Freed up bytes       : 1599968

After:
     Allocated 2128896 bytes in 33 chunks
     Used memory before GC: 2098304
     Used memory after GC : 818304
     Freed up bytes       : 1280000

As a final note, while this provides an improvement to many of qmlbench's
benchmarks, as they make heavy use of Repeater, these benefits should map quite
well to real world applications as well: anything that uses item views will
likely see a significant reduction to the amount of GC'd objects.

In the future, it would be nice to have tests for the amount of garbage
created by various activities, but that is an item for another day.

Change-Id: Idd14180e689235eea8883d68487570b0e14d47e6
Reviewed-by: Michael Brasser <michael.brasser@live.com>
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-04-25 15:34:14 +00:00
Liang Qi 0aaca005c0 Merge remote-tracking branch 'origin/5.9' into dev
Change-Id: Ibed6ee74d36b4ce37391c82db00a0abd30d09e7a
2017-04-21 11:08:24 +02:00
Erik Verbruggen 749a7212e9 QML: clear the property cache on QObjectWrapper destuction
If an external QObject is exposed to an engine through a QObjectWrapper,
make sure to deref and clear the propertyCache reference in the object's
declarative data when the QObjectWrapper is destroyed. This makes sure
that there is no dangling propertyCache pointer when the object is
subsequently exposed to another engine.

Task-number: QTBUG-57633
Change-Id: I37f6793d8be65b23b4e81bb4ed91db18271261b0
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-04-19 13:05:39 +00:00
Lars Knoll 589f8a90fa Separate the stack used for GC from the regular JS stack
This is required to be able to implement concurrent or
incremental garbage collection.

Change-Id: Ib3c5eee3779ca2ee08a57cd3961dbcb0537bbb54
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-04-07 06:06:17 +00:00
Lars Knoll 91714e004e Make all write operations to Pointer<> types go through a set() method
The new set() method also taked an ExecutionEngine pointer. This makes
it trivial to now add a write barrier for those operations.

Change-Id: I321eccfe6fb279cc240b5c84910e6854f71759f6
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-03-09 08:58:34 +00:00
Lars Knoll 6b4b2f5f1b New mark table implementation
Automatically generate a table containing the data where JS Values
and pointers are in objects in the JS heap.

This will allow making the GC mark phase a lot more efficient.

A bit of a special hack is currently required for MemberData and
ArrayData, as they have a variable length, and we need to read the
size from the object.

We keep backwards compatibility with the old markObjects() functions
for now (calling them if they are defined). Some further work on
QV4::String and in a few other places is required before we can get
remove the compatibility.

Change-Id: I78528ace67e886bdbe4a4330c9677c7fc9f08a33
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-03-09 08:58:04 +00:00
Liang Qi afec9016d0 Merge remote-tracking branch 'origin/5.9' into dev
Change-Id: I92b13a9c1727644d63e125c1e6f1fdac72720ad7
2017-02-28 13:04:17 +01:00
Ulf Hermann 74dc6ef8d5 Directly load already known metaproperties in QV4QObjectWrapper
A method and a property can have the same name in a QObject. This is
not directly expressible in a JS object, but when iterating the
properties of a wrapped QObject we should not look them up by name as
we might find the wrong one this way. However, as we already know what
we are looking for, there is no need for any further searching anyway.

Task-number: QTBUG-58887
Change-Id: I68574008c7a078baab9b343d550cc27956b0d5a9
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-02-15 13:51:26 +00:00
Robin Burchell 5f83e6dfe6 Object: Allow put and putIndexed to return success or failure
Some parts of the ES6 (and even ES5!) spec specifically require handling
of a property write failure. This will be introduced in followup changes,
as it's going to be rather more involved than this.

Change-Id: Ie482493fcf4780df0e23619650a856421d20bd55
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-02-09 14:53:01 +00:00
Lars Knoll 25552c1404 Convert more builtin functions to the new calling convention
Change-Id: I053215261e1186aff25f29e0967219ef667f7678
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2017-01-25 08:31:28 +00:00
Liang Qi 0e80d28aa5 Merge remote-tracking branch 'origin/5.8' into dev
Conflicts:
	src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp
	src/plugins/qmltooling/qmldbg_inspector/globalinspector.cpp
	src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp
	src/qml/qml/qqmlimport.cpp
	src/quick/items/context2d/qquickcontext2dtexture_p.h
	tools/qmleasing/splineeditor.h

Change-Id: I8f6630fcac243824350986c8e9f4bd6483bf20b5
2016-12-14 19:01:23 +01:00
Lars Knoll b45ebc6f45 Avoid passing a FunctionObject to QQmlBinding::create()
Like this we can remove the QQmlBinding::create() overload
that takes a FunctionObject.

Change-Id: Ib6c37395ded325e68cf0fbf3afd08fb6dd6efa3b
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2016-12-09 14:02:26 +00:00
Lars Knoll 91ac4a8d09 Don't store a source location in the QQmlBindingFunction anymore
It's not needed anymore as we now store this in the binding
directly.

Change-Id: I518c83207f219b690f31200e4d17251075bbd322
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2016-12-09 14:02:05 +00:00
Lars Knoll 9de7d0e9f1 Change signature of QQmlBinding::create to take a FunctionObject *
This is what's in the Value in all cases anyway.

Change-Id: I212c4c4076050e8d0ea4cf6f72a1683e132cd51b
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2016-12-09 14:01:26 +00:00
Lars Knoll 8a6383775a Start cleaning up the QmlContextWrapper
The class should get merged with the QV4::QmlContext class.
Simplify the cleanup by moving both classes into a common
file.

Change-Id: I0074da79701d5f41eb51681b70fcde85bfd45fc1
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2016-12-04 08:31:49 +00:00
Lars Knoll 17c7cb2b0d Avoid a duplicated wasDeleted() check
Change-Id: I30258041ca19cb7d925e4817c8f36577c335282d
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2016-11-29 20:00:35 +00:00
Liang Qi 2ff13bdf69 Merge remote-tracking branch 'origin/5.8' into dev
Change-Id: I171c7dbb6a74fe743c2eec63e86e9c0bef7c7dfd
2016-10-22 21:21:39 +02:00
Erik Verbruggen 15630c45e7 QML: allow QObjectWrapper::init to be inlined
And also hint that wrap_slowPath should not be inlined with LTO,
otherwise the fast-path wrap method will lose any advantage it has.

Change-Id: I30d52fa2f64b813aaeb5c0d62f6d48ec1ba03fa1
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2016-10-18 09:18:44 +00:00
Liang Qi f04c2c40fd Merge remote-tracking branch 'origin/5.8' into dev
Conflicts:
	src/qml/jsruntime/qv4variantobject.cpp
	src/qml/types/qquickworkerscript.cpp
	src/quick/scenegraph/util/qsgdefaultpainternode_p.h
	tools/qmljs/qmljs.cpp

Change-Id: I876242714ec8c046238d8fd673a5ace2455b2b59
2016-10-18 08:33:26 +02:00
Liang Qi 102fa9b6db Merge remote-tracking branch 'origin/5.7' into 5.8
Conflicts:
	examples/quick/quickwidgets/quickwidget/main.cpp
	src/qml/jsruntime/qv4jsonobject.cpp
	src/qml/jsruntime/qv4qobjectwrapper.cpp
	src/qml/jsruntime/qv4qobjectwrapper_p.h
	src/qml/qml/qqmlengine.cpp
	src/qml/qml/qqmlpropertycache.cpp
	src/qml/qml/qqmlpropertycache_p.h
	src/quick/items/qquickanimatedsprite.cpp
	src/quick/items/qquickitem.cpp
	src/quick/items/qquickitem.h
	src/quick/items/qquickitem_p.h
	src/quick/items/qquickview_p.h
	src/quick/scenegraph/qsgcontext.cpp
	src/quick/scenegraph/qsgdefaultrendercontext.cpp

Change-Id: I172c6fbff97208f21ed4c8b6db3d1747a889f22b
2016-10-10 16:01:48 +02:00
Erik Verbruggen 3b14e2ffdd QML: Make Heap::Object and all subclasses trivial
GCC6 might dead-store-eliminate out our secret write to Base::mmdata,
because it expects all memory content to be "undefined" before
constructor calls. Clang might take the same approach if the constructor
of Heap::Object is removed.

By making these structs trivial, it also makes them memcpy-able.

Change-Id: I055b2ad28311b997fbe059849ebda4d5894eaa9b
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2016-10-06 11:44:08 +00:00
Liang Qi fff4477661 Merge remote-tracking branch 'origin/5.6' into 5.7
Change-Id: I081d9b15796b4133d2ba6f1a862f15b873a4846d
2016-10-05 19:32:12 +02:00
Erik Verbruggen 64afa01c32 QML: Introduce destroy() on Base subclasses
This removes the destructors of subclasses of Base, making them nearly
trivial.

Change-Id: Ia6f7d467e87899b5ad37b8709a8f633a51689d59
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2016-10-05 13:15:18 +00:00
Erik Verbruggen 4d375f3f2b QML: Clear weak references on Object destruction for C++-owned QObjects
Otherwise a re-use of the C++-owned QObject will have a back reference
to a possibly GCed QV4::QObjectWrapper, which results in exciting
behavior.

Task-number: QTBUG-46263
Change-Id: Iff0e36f9e67c01abd02cfb5a89605d0f26ddb0de
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
2016-10-05 08:26:42 +00:00
Liang Qi 0d2fad48fc Merge remote-tracking branch 'origin/5.6' into 5.7
Change-Id: I48764527fa1ab6d8d59c24552394459b1cdc58ee
2016-10-04 12:27:15 +02:00
Liang Qi ead7aea128 Merge remote-tracking branch 'origin/5.8' into dev
Conflicts:
	src/qml/jsruntime/qv4qobjectwrapper.cpp

Change-Id: I1a125b2334532ec5de4af39c0d6628890f4d0587
2016-10-01 22:27:26 +02:00
Simon Hausmann c9ffed92a0 Fix crash with window-less QQuickItems
Mark QQuickItem visual children directly in QQuickItem instead of
relying on the item being a (grand) child of a window.

[ChangeLog][QtQuick] Fix crash with QQuickItems created via JavaScript being
garbage collected sometimes when they're not assigned to a window.

This may happen even in qmlscene when between the creation of the root item and
the assignment to the QQuickWindow the garbage collector runs.

The previous approach of a persistent in QQuickView marking the visual item
hierarchy relies on the existence of a view. The only thing left to do in the
view and qml window implementation is enforcing the CppOwnership policy set on
the content item in QQuickWindow by ensuring the presence of the JS wrapper,
replacing the persistent with a weak value.

This also introduces a new internal mechanism for QObject sub-classes to
provide their own V4 JS wrapper types.

Task-number: QTBUG-39888
Change-Id: Icd45a636a6d4e4528fc19165b13f4e1ca7967087
Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
2016-09-30 08:17:39 +00:00
Erik Verbruggen c08423ac01 QML: Replace QPointer with a QQmlQPointer (which is trivial)
One of the steps needed to make QV4::Heap::structs trivial.

Change-Id: Ic4d73f15035af21c8a682aaad1ee68cdd91f8e7d
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
2016-09-27 08:37:39 +00:00
Erik Verbruggen bbcf887f43 QML: Replace a QVector with a plain old C-style array
One of the steps needed to make QV4::Heap::structs trivial. It also
makes QMetaObjectWrapper memcpy-able.

Change-Id: I1a1b2e5a3fdb87ac4d2b5ace5af3aac54a63d9ed
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
2016-09-22 10:09:54 +00:00
Liang Qi cb5cbe9eb7 Merge remote-tracking branch 'origin/5.8' into dev
Conflicts:
	src/qml/compiler/qv4isel_moth_p.h

Change-Id: I8e86a649d1ef8ad27dc66cc8c290093b2faabc69
2016-09-21 09:24:03 +02:00
Anton Kudryavtsev 798b6ba239 optimize string usage: use fromLatin1() less
.. to reduce allocations.

Replace fromLatin1 with QLatin1String or
with QStringBuilder where it is possible.

Change-Id: I09c7242fa7b118447b51239e2a6743a34fb3de14
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2016-09-08 18:18:29 +00:00