diff options
289 files changed, 6727 insertions, 3721 deletions
diff --git a/.gitmodules b/.gitmodules index 881629497e..c9561f396d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,3 @@ -[submodule "tests/manual/v4/test262"] - path = tests/manual/v4/test262 +[submodule "tests/auto/qml/ecmascripttests/test262"] + path = tests/auto/qml/ecmascripttests/test262 url = ../qtdeclarative-testsuites.git - update = none diff --git a/examples/qml/doc/src/qml-extending.qdoc b/examples/qml/doc/src/qml-extending.qdoc index b4174426a8..f99aca5457 100644 --- a/examples/qml/doc/src/qml-extending.qdoc +++ b/examples/qml/doc/src/qml-extending.qdoc @@ -73,24 +73,24 @@ This example builds on: \li \l {Extending QML - Adding Types Example} \endlist -Shows how to use \l {QQmlEngine::}{qmlRegisterExtendedType()} to provide an \l {Registering -Extension Objects}{extension object} to a \l QLineEdit without modifying or -subclassing. +Shows how to use \l {QQmlEngine::}{qmlRegisterExtendedType()} to provide an +\l {Registering Extension Objects}{extension object} to a \l QLineEdit without modifying or +subclassing it. + +Firstly, the LineEditExtension class is registered with the QML system as an extension of QLineEdit: \snippet referenceexamples/extended/main.cpp 0 -The QML engine instantiates a \l QLineEdit +The QML engine then instantiates a \l QLineEdit: \snippet referenceexamples/extended/main.cpp 1 -and sets a property that oly exists on the extension type. +In QML, a property is set on the line edit that only exists in the LineEditExtension class: \snippet referenceexamples/extended/example.qml 0 -The QML engine instantiates a \l QLineEdit and sets a property that -only exists on the extension type. The extension type performs calls on the -\l QLineEdit that otherwise will not be accessible to the QML engine. - +The extension type performs calls on the \l QLineEdit that otherwise will +not be accessible to the QML engine. */ /*! diff --git a/examples/qml/referenceexamples/properties/birthdayparty.cpp b/examples/qml/referenceexamples/properties/birthdayparty.cpp index b69b7c8a11..0b426fc00b 100644 --- a/examples/qml/referenceexamples/properties/birthdayparty.cpp +++ b/examples/qml/referenceexamples/properties/birthdayparty.cpp @@ -57,9 +57,18 @@ void BirthdayParty::setHost(Person *c) QQmlListProperty<Person> BirthdayParty::guests() { - return QQmlListProperty<Person>(this, m_guests); + return QQmlListProperty<Person>(this, this, + &BirthdayParty::appendGuest, + &BirthdayParty::guestCount, + &BirthdayParty::guest, + &BirthdayParty::clearGuests); } +void BirthdayParty::appendGuest(Person* p) { + m_guests.append(p); +} + + int BirthdayParty::guestCount() const { return m_guests.count(); @@ -69,5 +78,25 @@ Person *BirthdayParty::guest(int index) const { return m_guests.at(index); } + +void BirthdayParty::clearGuests() { + return m_guests.clear(); +} + // ![0] +void BirthdayParty::appendGuest(QQmlListProperty<Person>* list, Person* p) { + reinterpret_cast< BirthdayParty* >(list->data)->appendGuest(p); +} + +void BirthdayParty::clearGuests(QQmlListProperty<Person>* list) { + reinterpret_cast< BirthdayParty* >(list->data)->clearGuests(); +} + +Person* BirthdayParty::guest(QQmlListProperty<Person>* list, int i) { + return reinterpret_cast< BirthdayParty* >(list->data)->guest(i); +} + +int BirthdayParty::guestCount(QQmlListProperty<Person>* list) { + return reinterpret_cast< BirthdayParty* >(list->data)->guestCount(); +} diff --git a/examples/qml/referenceexamples/properties/birthdayparty.h b/examples/qml/referenceexamples/properties/birthdayparty.h index d0a2cad285..df55df3e80 100644 --- a/examples/qml/referenceexamples/properties/birthdayparty.h +++ b/examples/qml/referenceexamples/properties/birthdayparty.h @@ -41,6 +41,7 @@ #define BIRTHDAYPARTY_H #include <QObject> +#include <QVector> #include <QQmlListProperty> #include "person.h" @@ -63,12 +64,19 @@ public: void setHost(Person *); QQmlListProperty<Person> guests(); + void appendGuest(Person*); int guestCount() const; Person *guest(int) const; + void clearGuests(); private: + static void appendGuest(QQmlListProperty<Person>*, Person*); + static int guestCount(QQmlListProperty<Person>*); + static Person* guest(QQmlListProperty<Person>*, int); + static void clearGuests(QQmlListProperty<Person>*); + Person *m_host; - QList<Person *> m_guests; + QVector<Person *> m_guests; }; // ![3] diff --git a/examples/quick/demos/demos.pro b/examples/quick/demos/demos.pro index 0644b81a22..5a6fd52baf 100644 --- a/examples/quick/demos/demos.pro +++ b/examples/quick/demos/demos.pro @@ -7,5 +7,7 @@ SUBDIRS = samegame \ photosurface \ stocqt -qtHaveModule(xmlpatterns): SUBDIRS += rssnews photoviewer - +qtHaveModule(xmlpatterns) { + SUBDIRS += rssnews + qtHaveModule(widgets): SUBDIRS += photoviewer +} diff --git a/examples/quick/demos/stocqt/content/StockListModel.qml b/examples/quick/demos/stocqt/content/StockListModel.qml index 9b48124bda..02ece32a49 100644 --- a/examples/quick/demos/stocqt/content/StockListModel.qml +++ b/examples/quick/demos/stocqt/content/StockListModel.qml @@ -82,27 +82,28 @@ ListModel { xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.LOADING || xhr.readyState === XMLHttpRequest.DONE) { var records = xhr.responseText.split('\n'); + var unknown = "n/a"; + set(index, {"value": unknown, "change": unknown, "changePercentage": unknown}); if (records.length > 0 && xhr.status == 200) { var r = records[1].split(','); var today = parseFloat(r[4]); - setProperty(index, "value", today.toFixed(2)); + if (!isNaN(today)) + setProperty(index, "value", today.toFixed(2)); + if (records.length > 2) { + r = records[2].split(','); + var yesterday = parseFloat(r[4]); + var change = today - yesterday; + if (change >= 0.0) + setProperty(index, "change", "+" + change.toFixed(2)); + else + setProperty(index, "change", change.toFixed(2)); - r = records[2].split(','); - var yesterday = parseFloat(r[4]); - var change = today - yesterday; - if (change >= 0.0) - setProperty(index, "change", "+" + change.toFixed(2)); - else - setProperty(index, "change", change.toFixed(2)); - - var changePercentage = (change / yesterday) * 100.0; - if (changePercentage >= 0.0) - setProperty(index, "changePercentage", "+" + changePercentage.toFixed(2) + "%"); - else - setProperty(index, "changePercentage", changePercentage.toFixed(2) + "%"); - } else { - var unknown = "n/a"; - set(index, {"value": unknown, "change": unknown, "changePercentage": unknown}); + var changePercentage = (change / yesterday) * 100.0; + if (changePercentage >= 0.0) + setProperty(index, "changePercentage", "+" + changePercentage.toFixed(2) + "%"); + else + setProperty(index, "changePercentage", changePercentage.toFixed(2) + "%"); + } } } } diff --git a/examples/quick/rendercontrol/window_singlethreaded.cpp b/examples/quick/rendercontrol/window_singlethreaded.cpp index ef8f2fed43..bd4de9a7cb 100644 --- a/examples/quick/rendercontrol/window_singlethreaded.cpp +++ b/examples/quick/rendercontrol/window_singlethreaded.cpp @@ -82,6 +82,10 @@ WindowSingleThreaded::WindowSingleThreaded() { setSurfaceType(QSurface::OpenGLSurface); + // The rendercontrol does not necessarily need an FBO. Demonstrate this + // when requested. + m_onscreen = QCoreApplication::arguments().contains(QStringLiteral("--onscreen")); + QSurfaceFormat format; // Qt Quick may need a depth and stencil buffer. Always make sure these are available. format.setDepthBufferSize(16); @@ -164,8 +168,14 @@ void WindowSingleThreaded::createFbo() // The scene graph has been initialized. It is now time to create an FBO and associate // it with the QQuickWindow. m_dpr = devicePixelRatio(); - m_fbo = new QOpenGLFramebufferObject(size() * m_dpr, QOpenGLFramebufferObject::CombinedDepthStencil); - m_quickWindow->setRenderTarget(m_fbo); + if (!m_onscreen) { + m_fbo = new QOpenGLFramebufferObject(size() * m_dpr, QOpenGLFramebufferObject::CombinedDepthStencil); + m_quickWindow->setRenderTarget(m_fbo); + } else { + // Special case: No FBO. Render directly to the window's default framebuffer. + m_onscreenSize = size() * m_dpr; + m_quickWindow->setRenderTarget(0, m_onscreenSize); + } } void WindowSingleThreaded::destroyFbo() @@ -176,7 +186,10 @@ void WindowSingleThreaded::destroyFbo() void WindowSingleThreaded::render() { - if (!m_context->makeCurrent(m_offscreenSurface)) + QSurface *surface = m_offscreenSurface; + if (m_onscreen) + surface = this; + if (!m_context->makeCurrent(surface)) return; // Polish, synchronize and render the next frame (into our fbo). In this example @@ -195,7 +208,10 @@ void WindowSingleThreaded::render() m_quickReady = true; // Get something onto the screen. - m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); + if (!m_onscreen) + m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); + else + m_context->swapBuffers(this); } void WindowSingleThreaded::requestUpdate() @@ -237,7 +253,10 @@ void WindowSingleThreaded::run() updateSizes(); // Initialize the render control and our OpenGL resources. - m_context->makeCurrent(m_offscreenSurface); + QSurface *surface = m_offscreenSurface; + if (m_onscreen) + surface = this; + m_context->makeCurrent(surface); m_renderControl->initialize(m_context); m_quickInitialized = true; } @@ -266,7 +285,8 @@ void WindowSingleThreaded::exposeEvent(QExposeEvent *) { if (isExposed()) { if (!m_quickInitialized) { - m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); + if (!m_onscreen) + m_cubeRenderer->render(this, m_context, m_quickReady ? m_fbo->texture() : 0); startQuick(QStringLiteral("qrc:/rendercontrol/demo.qml")); } } @@ -274,7 +294,10 @@ void WindowSingleThreaded::exposeEvent(QExposeEvent *) void WindowSingleThreaded::resizeFbo() { - if (m_rootItem && m_context->makeCurrent(m_offscreenSurface)) { + QSurface *surface = m_offscreenSurface; + if (m_onscreen) + surface = this; + if (m_rootItem && m_context->makeCurrent(surface)) { delete m_fbo; createFbo(); m_context->doneCurrent(); @@ -287,8 +310,13 @@ void WindowSingleThreaded::resizeEvent(QResizeEvent *) { // If this is a resize after the scene is up and running, recreate the fbo and the // Quick item and scene. - if (m_fbo && m_fbo->size() != size() * devicePixelRatio()) - resizeFbo(); + if (!m_onscreen) { + if (m_fbo && m_fbo->size() != size() * devicePixelRatio()) + resizeFbo(); + } else { + if (m_onscreenSize != size() * devicePixelRatio()) + resizeFbo(); + } } void WindowSingleThreaded::handleScreenChange() diff --git a/examples/quick/rendercontrol/window_singlethreaded.h b/examples/quick/rendercontrol/window_singlethreaded.h index 534d6b9bc3..4736f036ad 100644 --- a/examples/quick/rendercontrol/window_singlethreaded.h +++ b/examples/quick/rendercontrol/window_singlethreaded.h @@ -97,6 +97,8 @@ private: QTimer m_updateTimer; CubeRenderer *m_cubeRenderer; qreal m_dpr; + bool m_onscreen; + QSize m_onscreenSize; }; #endif diff --git a/src/3rdparty/masm/assembler/MacroAssembler.h b/src/3rdparty/masm/assembler/MacroAssembler.h index 7d9f156c8c..f37861eb66 100644 --- a/src/3rdparty/masm/assembler/MacroAssembler.h +++ b/src/3rdparty/masm/assembler/MacroAssembler.h @@ -94,6 +94,7 @@ public: using DataLabelCompact = typename MacroAssemblerBase::DataLabelCompact; using Jump = typename MacroAssemblerBase::Jump; using PatchableJump = typename MacroAssemblerBase::PatchableJump; + using MacroAssemblerBase::PointerSize; using MacroAssemblerBase::pop; using MacroAssemblerBase::jump; @@ -200,19 +201,19 @@ public: // described in terms of other macro assembly methods. void pop() { - addPtr(TrustedImm32(sizeof(void*)), MacroAssemblerBase::stackPointerRegister); + addPtr(TrustedImm32(PointerSize), MacroAssemblerBase::stackPointerRegister); } void peek(RegisterID dest, int index = 0) { - loadPtr(Address(MacroAssemblerBase::stackPointerRegister, (index * sizeof(void*))), dest); + loadPtr(Address(MacroAssemblerBase::stackPointerRegister, (index * PointerSize)), dest); } Address addressForPoke(int index) { - return Address(MacroAssemblerBase::stackPointerRegister, (index * sizeof(void*))); + return Address(MacroAssemblerBase::stackPointerRegister, (index * PointerSize)); } - + void poke(RegisterID src, int index = 0) { storePtr(src, addressForPoke(index)); @@ -223,10 +224,12 @@ public: store32(value, addressForPoke(index)); } +#if !defined(V4_BOOTSTRAP) void poke(TrustedImmPtr imm, int index = 0) { storePtr(imm, addressForPoke(index)); } +#endif #if (CPU(X86_64) || CPU(ARM64)) && !defined(V4_BOOTSTRAP) void peek64(RegisterID dest, int index = 0) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h index a11637f7ca..11f1672e15 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h @@ -127,6 +127,8 @@ private: static const ptrdiff_t REPATCH_OFFSET_CALL_TO_POINTER = -16; public: + static const int PointerSize = 8; + MacroAssemblerARM64() : m_dataMemoryTempRegister(this, dataTempRegister) , m_cachedMemoryTempRegister(this, memoryTempRegister) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h index 806f2e13b6..fe8170d098 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h @@ -46,6 +46,8 @@ protected: // the YarrJIT needs know about addressTempRegister in order to push inline ARMRegisters::FPSingleRegisterID fpTempRegisterAsSingle() { return ARMRegisters::asSingle(fpTempRegister); } public: + static const int PointerSize = 4; + MacroAssemblerARMv7() : m_makeJumpPatchable(false) { @@ -1242,7 +1244,7 @@ public: void pop(RegisterID dest) { // store postindexed with writeback - m_assembler.ldr(dest, ARMRegisters::sp, sizeof(void*), false, true); + m_assembler.ldr(dest, ARMRegisters::sp, 4 /*sizeof(void*)*/, false, true); } void push(RegisterID src) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h index 68584527fc..f2ad6a4470 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h @@ -37,6 +37,7 @@ namespace JSC { class MacroAssemblerMIPS : public AbstractMacroAssembler<MIPSAssembler> { public: typedef MIPSRegisters::FPRegisterID FPRegisterID; + static const int PointerSize = 4; MacroAssemblerMIPS() : m_fixedWidth(false) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86.h b/src/3rdparty/masm/assembler/MacroAssemblerX86.h index 742a4b48f7..280cf427fc 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerX86.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerX86.h @@ -35,6 +35,7 @@ namespace JSC { class MacroAssemblerX86 : public MacroAssemblerX86Common { public: static const Scale ScalePtr = TimesFour; + static const int PointerSize = 4; using MacroAssemblerX86Common::add32; using MacroAssemblerX86Common::and32; diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h index 3566702413..c7c6aae637 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h @@ -37,6 +37,7 @@ namespace JSC { class MacroAssemblerX86_64 : public MacroAssemblerX86Common { public: static const Scale ScalePtr = TimesEight; + static const int PointerSize = 8; using MacroAssemblerX86Common::add32; using MacroAssemblerX86Common::and32; @@ -327,14 +328,61 @@ public: m_assembler.xorq_ir(imm.m_value, srcDest); } + void lshift64(TrustedImm32 imm, RegisterID dest) + { + m_assembler.shlq_i8r(imm.m_value, dest); + } + + void lshift64(RegisterID src, RegisterID dest) + { + if (src == X86Registers::ecx) + m_assembler.shlq_CLr(dest); + else { + ASSERT(src != dest); + + // Can only shift by ecx, so we do some swapping if we see anything else. + swap(src, X86Registers::ecx); + m_assembler.shlq_CLr(dest == X86Registers::ecx ? src : dest); + swap(src, X86Registers::ecx); + } + } + + void rshift64(TrustedImm32 imm, RegisterID dest) + { + m_assembler.sarq_i8r(imm.m_value, dest); + } + + void rshift64(RegisterID src, RegisterID dest) + { + if (src == X86Registers::ecx) + m_assembler.sarq_CLr(dest); + else { + ASSERT(src != dest); + + // Can only shift by ecx, so we do some swapping if we see anything else. + swap(src, X86Registers::ecx); + m_assembler.sarq_CLr(dest == X86Registers::ecx ? src : dest); + swap(src, X86Registers::ecx); + } + } + void urshift64(TrustedImm32 imm, RegisterID dest) { m_assembler.shrq_i8r(imm.m_value, dest); } - void lshift64(TrustedImm32 imm, RegisterID dest) + void urshift64(RegisterID src, RegisterID dest) { - m_assembler.shlq_i8r(imm.m_value, dest); + if (src == X86Registers::ecx) + m_assembler.shrq_CLr(dest); + else { + ASSERT(src != dest); + + // Can only shift by ecx, so we do some swapping if we see anything else. + swap(src, X86Registers::ecx); + m_assembler.shrq_CLr(dest == X86Registers::ecx ? src : dest); + swap(src, X86Registers::ecx); + } } void load64(ImplicitAddress address, RegisterID dest) diff --git a/src/3rdparty/masm/assembler/X86Assembler.h b/src/3rdparty/masm/assembler/X86Assembler.h index 24462ef38f..b71cf290f8 100644 --- a/src/3rdparty/masm/assembler/X86Assembler.h +++ b/src/3rdparty/masm/assembler/X86Assembler.h @@ -253,6 +253,7 @@ public: { } +#if defined(V4_BOOTSTRAP) template <typename LabelType> class Jump { template<class TemplateAssemblerType> @@ -291,6 +292,7 @@ public: private: AssemblerLabel m_label; }; +#endif // Stack operations: @@ -723,6 +725,21 @@ public: } } + void sarq_CLr(RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); + } + + void sarq_i8r(int imm, RegisterID dst) + { + if (imm == 1) + m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); + else { + m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); + m_formatter.immediate8(imm); + } + } + void shrq_i8r(int imm, RegisterID dst) { // ### doesn't work when removing the "0 &&" @@ -734,6 +751,11 @@ public: } } + void shrq_CLr(RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP2_EvCL, GROUP2_OP_SHR, dst); + } + void shlq_i8r(int imm, RegisterID dst) { // ### doesn't work when removing the "0 &&" @@ -745,7 +767,10 @@ public: } } - + void shlq_CLr(RegisterID dst) + { + m_formatter.oneByteOp64(OP_GROUP2_EvCL, GROUP2_OP_SHL, dst); + } #endif void sarl_i8r(int imm, RegisterID dst) @@ -793,23 +818,6 @@ public: m_formatter.oneByteOp(OP_GROUP2_EvCL, GROUP2_OP_SHL, dst); } -#if CPU(X86_64) - void sarq_CLr(RegisterID dst) - { - m_formatter.oneByteOp64(OP_GROUP2_EvCL, GROUP2_OP_SAR, dst); - } - - void sarq_i8r(int imm, RegisterID dst) - { - if (imm == 1) - m_formatter.oneByteOp64(OP_GROUP2_Ev1, GROUP2_OP_SAR, dst); - else { - m_formatter.oneByteOp64(OP_GROUP2_EvIb, GROUP2_OP_SAR, dst); - m_formatter.immediate8(imm); - } - } -#endif - void imull_rr(RegisterID src, RegisterID dst) { m_formatter.twoByteOp(OP2_IMUL_GvEv, dst, src); diff --git a/src/3rdparty/masm/masm-defs.pri b/src/3rdparty/masm/masm-defs.pri index fa0d3d3c55..c0c5f3d114 100644 --- a/src/3rdparty/masm/masm-defs.pri +++ b/src/3rdparty/masm/masm-defs.pri @@ -40,3 +40,10 @@ INCLUDEPATH += $$PWD/disassembler/udis86 INCLUDEPATH += $$_OUT_PWD CONFIG(release, debug|release): DEFINES += NDEBUG + +!intel_icc:!clang:gcc { + greaterThan(QT_GCC_MAJOR_VERSION, 6) { # GCC 7 + QMAKE_CXXFLAGS_WARN_ON += -Wno-expansion-to-defined + QMAKE_CXXFLAGS += -Wno-expansion-to-defined + } +} diff --git a/src/3rdparty/masm/wtf/Assertions.h b/src/3rdparty/masm/wtf/Assertions.h index 6263e50ed9..af65f5325c 100644 --- a/src/3rdparty/masm/wtf/Assertions.h +++ b/src/3rdparty/masm/wtf/Assertions.h @@ -233,7 +233,7 @@ WTF_EXPORT_PRIVATE void WTFInstallReportBacktraceOnCrashHook(); #define ASSERT_NOT_REACHED() ((void)0) #define NO_RETURN_DUE_TO_ASSERT -#if COMPILER(INTEL) && !OS(WINDOWS) || COMPILER(RVCT) +#if COMPILER(RVCT) template<typename T> inline void assertUnused(T& x) { (void)x; } #define ASSERT_UNUSED(variable, assertion) (assertUnused(variable)) diff --git a/src/imports/builtins/builtins.qmltypes b/src/imports/builtins/builtins.qmltypes index 88e3ac6634..ac95a8837b 100644 --- a/src/imports/builtins/builtins.qmltypes +++ b/src/imports/builtins/builtins.qmltypes @@ -150,6 +150,25 @@ Module { } } Enum { + name: "TextFlag" + values: { + "TextSingleLine": 256, + "TextDontClip": 512, + "TextExpandTabs": 1024, + "TextShowMnemonic": 2048, + "TextWordWrap": 4096, + "TextWrapAnywhere": 8192, + "TextDontPrint": 16384, + "TextIncludeTrailingSpaces": 134217728, + "TextHideMnemonic": 32768, + "TextJustificationForced": 65536, + "TextForceLeftToRight": 131072, + "TextForceRightToLeft": 262144, + "TextLongestVariant": 524288, + "TextBypassShaping": 1048576 + } + } + Enum { name: "TextElideMode" values: { "ElideLeft": 0, @@ -440,7 +459,8 @@ Module { "AA_SynthesizeMouseForUnhandledTabletEvents": 24, "AA_CompressHighFrequencyEvents": 25, "AA_DontCheckOpenGLContextThreadAffinity": 26, - "AA_AttributeCount": 27 + "AA_DisableShaderDiskCache": 27, + "AA_AttributeCount": 28 } } Enum { @@ -1096,7 +1116,8 @@ Module { "SystemLocaleLongDate": 5, "DefaultLocaleShortDate": 6, "DefaultLocaleLongDate": 7, - "RFC2822Date": 8 + "RFC2822Date": 8, + "ISODateWithMs": 9 } } Enum { @@ -1607,6 +1628,13 @@ Module { "MouseEventFlagMask": 255 } } + Enum { + name: "ChecksumType" + values: { + "ChecksumIso3309": 0, + "ChecksumItuV41": 1 + } + } } Component { name: "QEasingCurve"; prototype: "QQmlEasingValueType" } } diff --git a/src/imports/imports.pro b/src/imports/imports.pro index 544993c073..71c05c4b89 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -6,9 +6,9 @@ SUBDIRS += \ builtins \ qtqml \ folderlistmodel \ - localstorage \ models +qtHaveModule(sql): SUBDIRS += localstorage qtConfig(settings): SUBDIRS += settings qtConfig(statemachine): SUBDIRS += statemachine @@ -18,10 +18,10 @@ qtHaveModule(quick) { layouts \ qtquick2 \ window \ - sharedimage \ testlib - qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \ + qtConfig(systemsemaphore): SUBDIRS += sharedimage + qtConfig(quick-particles): \ SUBDIRS += particles } diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index 8679750842..40682dd6a8 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -770,6 +770,8 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args) } args->setReturnValue(db.asReturnedValue()); +#else + Q_UNUSED(args) #endif // settings } diff --git a/src/imports/particles/plugins.qmltypes b/src/imports/particles/plugins.qmltypes index 5c4bd01980..6c7c98cc71 100644 --- a/src/imports/particles/plugins.qmltypes +++ b/src/imports/particles/plugins.qmltypes @@ -275,11 +275,11 @@ Module { Parameter { name: "arg"; type: "double" } } Method { - name: "setAcceleration" + name: "setMagnitude" Parameter { name: "arg"; type: "double" } } Method { - name: "setMagnitude" + name: "setAcceleration" Parameter { name: "arg"; type: "double" } } Method { @@ -552,7 +552,7 @@ Module { Parameter { name: "arg"; type: "bool" } } Method { - name: "setmirrored" + name: "setMirrored" Parameter { name: "arg"; type: "bool" } } } diff --git a/src/imports/qtquick2/plugins.qmltypes b/src/imports/qtquick2/plugins.qmltypes index 441cd743aa..a9534b5ccc 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.8' +// 'qmlplugindump -nonrelocatable -noforceqtquick QtQuick 2.9' Module { dependencies: [] @@ -440,6 +440,13 @@ Module { } } Component { + name: "QPointingDeviceUniqueId" + exports: ["QtQuick/PointingDeviceUniqueId 2.9"] + isCreatable: false + exportMetaObjectRevisions: [0] + Property { name: "numericId"; type: "qlonglong"; isReadonly: true } + } + Component { name: "QQmlApplication" prototype: "QObject" Property { name: "arguments"; type: "QStringList"; isReadonly: true } @@ -1159,6 +1166,8 @@ Module { Property { name: "supportsMultipleWindows"; type: "bool"; isReadonly: true } Property { name: "state"; type: "Qt::ApplicationState"; isReadonly: true } Property { name: "font"; type: "QFont"; isReadonly: true } + Property { name: "displayName"; type: "string" } + Property { name: "screens"; type: "QQuickScreenInfo"; isList: true; isReadonly: true } Signal { name: "stateChanged" Parameter { name: "state"; type: "Qt::ApplicationState" } @@ -1168,9 +1177,13 @@ Module { name: "QQuickBasePositioner" defaultProperty: "data" prototype: "QQuickImplicitSizeItem" - exports: ["QtQuick/Positioner 2.0", "QtQuick/Positioner 2.6"] + exports: [ + "QtQuick/Positioner 2.0", + "QtQuick/Positioner 2.6", + "QtQuick/Positioner 2.9" + ] isCreatable: false - exportMetaObjectRevisions: [0, 6] + exportMetaObjectRevisions: [0, 6, 9] attachedType: "QQuickPositionerAttached" Property { name: "spacing"; type: "double" } Property { name: "populate"; type: "QQuickTransition"; isPointer: true } @@ -1186,6 +1199,8 @@ Module { Signal { name: "leftPaddingChanged"; revision: 6 } Signal { name: "rightPaddingChanged"; revision: 6 } Signal { name: "bottomPaddingChanged"; revision: 6 } + Signal { name: "positioningComplete"; revision: 9 } + Method { name: "forceLayout"; revision: 9 } } Component { name: "QQuickBehavior" @@ -1502,8 +1517,8 @@ Module { name: "QQuickFlickable" defaultProperty: "flickableData" prototype: "QQuickItem" - exports: ["QtQuick/Flickable 2.0"] - exportMetaObjectRevisions: [0] + exports: ["QtQuick/Flickable 2.0", "QtQuick/Flickable 2.9"] + exportMetaObjectRevisions: [0, 9] Enum { name: "BoundsBehavior" values: { @@ -1563,6 +1578,8 @@ Module { isPointer: true } Property { name: "pixelAligned"; type: "bool" } + Property { name: "horizontalOvershoot"; revision: 9; type: "double"; isReadonly: true } + Property { name: "verticalOvershoot"; revision: 9; type: "double"; isReadonly: true } Property { name: "flickableData"; type: "QObject"; isList: true; isReadonly: true } Property { name: "flickableChildren"; type: "QQuickItem"; isList: true; isReadonly: true } Signal { name: "isAtBoundaryChanged" } @@ -1572,6 +1589,8 @@ Module { Signal { name: "flickEnded" } Signal { name: "dragStarted" } Signal { name: "dragEnded" } + Signal { name: "horizontalOvershootChanged"; revision: 9 } + Signal { name: "verticalOvershootChanged"; revision: 9 } Method { name: "resizeContent" Parameter { name: "w"; type: "double" } @@ -2084,8 +2103,13 @@ Module { name: "QQuickItem" defaultProperty: "data" prototype: "QObject" - exports: ["QtQuick/Item 2.0", "QtQuick/Item 2.1", "QtQuick/Item 2.4"] - exportMetaObjectRevisions: [0, 1, 2] + exports: [ + "QtQuick/Item 2.0", + "QtQuick/Item 2.1", + "QtQuick/Item 2.4", + "QtQuick/Item 2.7" + ] + exportMetaObjectRevisions: [0, 1, 2, 7] Enum { name: "TransformOrigin" values: { @@ -2210,23 +2234,21 @@ Module { Parameter { name: "point"; type: "QPointF" } } Method { - name: "mapToGlobal" - revision: 7 - type: "QPointF" - Parameter { name: "point"; type: "QPointF" } + name: "mapFromItem" + Parameter { type: "QQmlV4Function"; isPointer: true } } Method { - name: "mapFromGlobal" - revision: 7 - type: "QPointF" - Parameter { name: "point"; type: "QPointF" } + name: "mapToItem" + Parameter { type: "QQmlV4Function"; isPointer: true } } Method { - name: "mapFromItem" + name: "mapFromGlobal" + revision: 7 Parameter { type: "QQmlV4Function"; isPointer: true } } Method { - name: "mapToItem" + name: "mapToGlobal" + revision: 7 Parameter { type: "QQmlV4Function"; isPointer: true } } Method { name: "forceActiveFocus" } @@ -2259,6 +2281,11 @@ Module { type: "bool" Parameter { name: "fileName"; type: "string" } } + Method { + name: "saveToFile" + type: "bool" + Parameter { name: "fileName"; type: "string" } + } } Component { name: "QQuickItemLayer" @@ -2318,9 +2345,13 @@ Module { name: "QQuickItemView" defaultProperty: "flickableData" prototype: "QQuickFlickable" - exports: ["QtQuick/ItemView 2.1", "QtQuick/ItemView 2.3"] + exports: [ + "QtQuick/ItemView 2.1", + "QtQuick/ItemView 2.3", + "QtQuick/ItemView 2.7" + ] isCreatable: false - exportMetaObjectRevisions: [1, 2] + exportMetaObjectRevisions: [1, 2, 7] Enum { name: "LayoutDirection" values: { @@ -2495,6 +2526,10 @@ Module { Parameter { name: "event"; type: "QQuickKeyEvent"; isPointer: true } } Signal { + name: "shortcutOverride" + Parameter { name: "event"; type: "QQuickKeyEvent"; isPointer: true } + } + Signal { name: "digit0Pressed" Parameter { name: "event"; type: "QQuickKeyEvent"; isPointer: true } } @@ -2757,9 +2792,10 @@ Module { exports: [ "QtQuick/MouseArea 2.0", "QtQuick/MouseArea 2.4", - "QtQuick/MouseArea 2.5" + "QtQuick/MouseArea 2.5", + "QtQuick/MouseArea 2.9" ] - exportMetaObjectRevisions: [0, 1, 2] + exportMetaObjectRevisions: [0, 1, 2, 9] Property { name: "mouseX"; type: "double"; isReadonly: true } Property { name: "mouseY"; type: "double"; isReadonly: true } Property { name: "containsMouse"; type: "bool"; isReadonly: true } @@ -2774,6 +2810,7 @@ Module { Property { name: "propagateComposedEvents"; type: "bool" } Property { name: "cursorShape"; type: "Qt::CursorShape" } Property { name: "containsPress"; revision: 1; type: "bool"; isReadonly: true } + Property { name: "pressAndHoldInterval"; revision: 9; type: "int" } Signal { name: "hoveredChanged" } Signal { name: "scrollGestureEnabledChanged"; revision: 2 } Signal { @@ -2816,6 +2853,7 @@ Module { Signal { name: "exited" } Signal { name: "canceled" } Signal { name: "containsPressChanged"; revision: 1 } + Signal { name: "pressAndHoldIntervalChanged"; revision: 9 } } Component { name: "QQuickMultiPointTouchArea" @@ -3600,14 +3638,20 @@ Module { Component { name: "QQuickShortcut" prototype: "QObject" - exports: ["QtQuick/Shortcut 2.5", "QtQuick/Shortcut 2.6"] - exportMetaObjectRevisions: [0, 1] + exports: [ + "QtQuick/Shortcut 2.5", + "QtQuick/Shortcut 2.6", + "QtQuick/Shortcut 2.9" + ] + exportMetaObjectRevisions: [0, 1, 9] Property { name: "sequence"; type: "QVariant" } + Property { name: "sequences"; revision: 9; type: "QVariantList" } Property { name: "nativeText"; revision: 1; type: "string"; isReadonly: true } Property { name: "portableText"; revision: 1; type: "string"; isReadonly: true } Property { name: "enabled"; type: "bool" } Property { name: "autoRepeat"; type: "bool" } Property { name: "context"; type: "Qt::ShortcutContext" } + Signal { name: "sequencesChanged"; revision: 9 } Signal { name: "activated" } Signal { name: "activatedAmbiguously" } } @@ -3926,9 +3970,10 @@ Module { "QtQuick/Text 2.0", "QtQuick/Text 2.2", "QtQuick/Text 2.3", - "QtQuick/Text 2.6" + "QtQuick/Text 2.6", + "QtQuick/Text 2.9" ] - exportMetaObjectRevisions: [0, 2, 3, 6] + exportMetaObjectRevisions: [0, 2, 3, 6, 9] Enum { name: "HAlignment" values: { @@ -4038,6 +4083,7 @@ Module { Property { name: "leftPadding"; revision: 6; type: "double" } Property { name: "rightPadding"; revision: 6; type: "double" } Property { name: "bottomPadding"; revision: 6; type: "double" } + Property { name: "fontInfo"; revision: 9; type: "QJSValue"; isReadonly: true } Signal { name: "textChanged" Parameter { name: "text"; type: "string" } @@ -4093,7 +4139,9 @@ Module { Signal { name: "leftPaddingChanged"; revision: 6 } Signal { name: "rightPaddingChanged"; revision: 6 } Signal { name: "bottomPaddingChanged"; revision: 6 } + Signal { name: "fontInfoChanged"; revision: 9 } Method { name: "doLayout" } + Method { name: "forceLayout"; revision: 9 } Method { name: "linkAt" revision: 3 @@ -4390,9 +4438,10 @@ Module { "QtQuick/TextInput 2.2", "QtQuick/TextInput 2.4", "QtQuick/TextInput 2.6", - "QtQuick/TextInput 2.7" + "QtQuick/TextInput 2.7", + "QtQuick/TextInput 2.9" ] - exportMetaObjectRevisions: [0, 2, 3, 6, 7] + exportMetaObjectRevisions: [0, 2, 3, 6, 7, 9] Enum { name: "EchoMode" values: { @@ -4497,6 +4546,7 @@ Module { Property { name: "bottomPadding"; revision: 6; type: "double" } Signal { name: "accepted" } Signal { name: "editingFinished"; revision: 2 } + Signal { name: "textEdited"; revision: 9 } Signal { name: "fontChanged" Parameter { name: "font"; type: "QFont" } @@ -4657,13 +4707,16 @@ Module { Component { name: "QQuickTouchPoint" prototype: "QObject" - exports: ["QtQuick/TouchPoint 2.0"] - exportMetaObjectRevisions: [0] + exports: ["QtQuick/TouchPoint 2.0", "QtQuick/TouchPoint 2.9"] + exportMetaObjectRevisions: [0, 0] Property { name: "pointId"; type: "int"; isReadonly: true } + Property { name: "uniqueId"; revision: 9; type: "QPointingDeviceUniqueId"; isReadonly: true } Property { name: "pressed"; type: "bool"; isReadonly: true } Property { name: "x"; type: "double"; isReadonly: true } Property { name: "y"; type: "double"; isReadonly: true } + Property { name: "ellipseDiameters"; revision: 9; type: "QSizeF"; isReadonly: true } Property { name: "pressure"; type: "double"; isReadonly: true } + Property { name: "rotation"; revision: 9; type: "double"; isReadonly: true } Property { name: "velocity"; type: "QVector2D"; isReadonly: true } Property { name: "area"; type: "QRectF"; isReadonly: true } Property { name: "startX"; type: "double"; isReadonly: true } @@ -4672,6 +4725,9 @@ Module { Property { name: "previousY"; type: "double"; isReadonly: true } Property { name: "sceneX"; type: "double"; isReadonly: true } Property { name: "sceneY"; type: "double"; isReadonly: true } + Signal { name: "uniqueIdChanged"; revision: 9 } + Signal { name: "ellipseDiametersChanged"; revision: 9 } + Signal { name: "rotationChanged"; revision: 9 } } Component { name: "QQuickTransform"; prototype: "QObject" } Component { diff --git a/src/imports/testlib/TestCase.qml b/src/imports/testlib/TestCase.qml index 18c70e1169..8c1744a2b2 100644 --- a/src/imports/testlib/TestCase.qml +++ b/src/imports/testlib/TestCase.qml @@ -38,6 +38,7 @@ ****************************************************************************/ import QtQuick 2.0 +import QtQuick.Window 2.0 // used for qtest_verifyItem import QtTest 1.1 import "testlogger.js" as TestLogger import Qt.test.qtestroot 1.0 @@ -443,6 +444,9 @@ Item { or \c{QVERIFY2(condition, message)} in C++. */ function verify(cond, msg) { + if (arguments.length > 2) + qtest_fail("More than two arguments given to verify(). Did you mean tryVerify() or tryCompare()?", 1) + if (msg === undefined) msg = ""; if (!qtest_results.verify(cond, msg, util.callerFile(), util.callerLine())) @@ -1085,8 +1089,8 @@ Item { function waitForRendering(item, timeout) { if (timeout === undefined) timeout = 5000 - if (!item) - qtest_fail("No item given to waitForRendering", 1) + if (!qtest_verifyItem(item, "waitForRendering")) + return return qtest_results.waitForRendering(item, timeout) } @@ -1198,8 +1202,8 @@ Item { \sa mouseRelease(), mouseClick(), mouseDoubleClick(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel() */ function mousePress(item, x, y, button, modifiers, delay) { - if (!item) - qtest_fail("No item given to mousePress", 1) + if (!qtest_verifyItem(item, "mousePress")) + return if (button === undefined) button = Qt.LeftButton @@ -1232,8 +1236,8 @@ Item { \sa mousePress(), mouseClick(), mouseDoubleClick(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel() */ function mouseRelease(item, x, y, button, modifiers, delay) { - if (!item) - qtest_fail("No item given to mouseRelease", 1) + if (!qtest_verifyItem(item, "mouseRelease")) + return if (button === undefined) button = Qt.LeftButton @@ -1268,8 +1272,8 @@ Item { \sa mousePress(), mouseClick(), mouseDoubleClick(), mouseDoubleClickSequence(), mouseMove(), mouseRelease(), mouseWheel() */ function mouseDrag(item, x, y, dx, dy, button, modifiers, delay) { - if (!item) - qtest_fail("No item given to mouseDrag", 1) + if (!qtest_verifyItem(item, "mouseDrag")) + return if (item.x === undefined || item.y === undefined) return @@ -1319,8 +1323,8 @@ Item { \sa mousePress(), mouseRelease(), mouseDoubleClick(), mouseDoubleClickSequence(), mouseMove(), mouseDrag(), mouseWheel() */ function mouseClick(item, x, y, button, modifiers, delay) { - if (!item) - qtest_fail("No item given to mouseClick", 1) + if (!qtest_verifyItem(item, "mouseClick")) + return if (button === undefined) button = Qt.LeftButton @@ -1353,8 +1357,8 @@ Item { \sa mouseDoubleClickSequence(), mousePress(), mouseRelease(), mouseClick(), mouseMove(), mouseDrag(), mouseWheel() */ function mouseDoubleClick(item, x, y, button, modifiers, delay) { - if (!item) - qtest_fail("No item given to mouseDoubleClick", 1) + if (!qtest_verifyItem(item, "mouseDoubleClick")) + return if (button === undefined) button = Qt.LeftButton @@ -1394,8 +1398,8 @@ Item { \sa mouseDoubleClick(), mousePress(), mouseRelease(), mouseClick(), mouseMove(), mouseDrag(), mouseWheel() */ function mouseDoubleClickSequence(item, x, y, button, modifiers, delay) { - if (!item) - qtest_fail("No item given to mouseDoubleClickSequence", 1) + if (!qtest_verifyItem(item, "mouseDoubleClickSequence")) + return if (button === undefined) button = Qt.LeftButton @@ -1426,8 +1430,8 @@ Item { \sa mousePress(), mouseRelease(), mouseClick(), mouseDoubleClick(), mouseDoubleClickSequence(), mouseDrag(), mouseWheel() */ function mouseMove(item, x, y, delay, buttons) { - if (!item) - qtest_fail("No item given to mouseMove", 1) + if (!qtest_verifyItem(item, "mouseMove")) + return if (delay == undefined) delay = -1 @@ -1454,8 +1458,8 @@ Item { \sa mousePress(), mouseClick(), mouseDoubleClick(), mouseDoubleClickSequence(), mouseMove(), mouseRelease(), mouseDrag(), QWheelEvent::angleDelta() */ function mouseWheel(item, x, y, xDelta, yDelta, buttons, modifiers, delay) { - if (!item) - qtest_fail("No item given to mouseWheel", 1) + if (!qtest_verifyItem(item, "mouseWheel")) + return if (delay == undefined) delay = -1 @@ -1515,8 +1519,8 @@ Item { */ function touchEvent(item) { - if (!item) - qtest_fail("No item given to touchEvent", 1) + if (!qtest_verifyItem(item, "touchEvent")) + return return { _defaultItem: item, @@ -1625,6 +1629,23 @@ Item { function cleanup() {} /*! \internal */ + function qtest_verifyItem(item, method) { + try { + if (!(item instanceof Item) && + !(item instanceof Window)) { + // it's a QObject, but not a type + qtest_fail("TypeError: %1 requires an Item or Window type".arg(method), 2); + return false; + } + } catch (e) { // it's not a QObject + qtest_fail("TypeError: %1 requires an Item or Window type".arg(method), 3); + return false; + } + + return true; + } + + /*! \internal */ function qtest_runInternal(prop, arg) { try { qtest_testCaseResult = testCase[prop](arg) diff --git a/src/imports/testlib/plugins.qmltypes b/src/imports/testlib/plugins.qmltypes index 563778e55e..5d7ca51adc 100644 --- a/src/imports/testlib/plugins.qmltypes +++ b/src/imports/testlib/plugins.qmltypes @@ -4,11 +4,45 @@ import QtQuick.tooling 1.2 // It is used for QML tooling purposes only. // // This file was auto-generated by: -// 'qmlplugindump -nonrelocatable -noforceqtquick QtTest 1.1' +// 'qmlplugindump -nonrelocatable -noforceqtquick QtTest 1.2' Module { dependencies: ["QtQuick 2.0"] Component { + name: "QQuickTouchEventSequence" + prototype: "QObject" + Method { + name: "press" + type: "QObject*" + Parameter { name: "touchId"; type: "int" } + Parameter { name: "item"; type: "QObject"; isPointer: true } + Parameter { name: "x"; type: "double" } + Parameter { name: "y"; type: "double" } + } + Method { + name: "move" + type: "QObject*" + Parameter { name: "touchId"; type: "int" } + Parameter { name: "item"; type: "QObject"; isPointer: true } + Parameter { name: "x"; type: "double" } + Parameter { name: "y"; type: "double" } + } + Method { + name: "release" + type: "QObject*" + Parameter { name: "touchId"; type: "int" } + Parameter { name: "item"; type: "QObject"; isPointer: true } + Parameter { name: "x"; type: "double" } + Parameter { name: "y"; type: "double" } + } + Method { + name: "stationary" + type: "QObject*" + Parameter { name: "touchId"; type: "int" } + } + Method { name: "commit"; type: "QObject*" } + } + Component { name: "QuickTestEvent" prototype: "QObject" exports: ["QtTest/TestEvent 1.0"] @@ -127,6 +161,12 @@ Module { Parameter { name: "yDelta"; type: "int" } Parameter { name: "delay"; type: "int" } } + Method { + name: "touchEvent" + type: "QQuickTouchEventSequence*" + Parameter { name: "item"; type: "QObject"; isPointer: true } + } + Method { name: "touchEvent"; type: "QQuickTouchEventSequence*" } } Component { name: "QuickTestResult" diff --git a/src/imports/window/plugins.qmltypes b/src/imports/window/plugins.qmltypes index a79bd8c332..cea2a910a7 100644 --- a/src/imports/window/plugins.qmltypes +++ b/src/imports/window/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 QtQuick.Window 2.2' +// 'qmlplugindump -nonrelocatable QtQuick.Window 2.3' Module { dependencies: ["QtQuick 2.8"] @@ -24,14 +24,28 @@ Module { Component { name: "QQuickScreen" prototype: "QObject" - exports: ["QtQuick.Window/Screen 2.0"] + exports: ["QtQuick.Window/Screen 2.0", "QtQuick.Window/Screen 2.3"] isCreatable: false - exportMetaObjectRevisions: [0] + exportMetaObjectRevisions: [0, 1] attachedType: "QQuickScreenAttached" } Component { name: "QQuickScreenAttached" + prototype: "QQuickScreenInfo" + Property { name: "orientationUpdateMask"; type: "Qt::ScreenOrientations" } + Method { + name: "angleBetween" + type: "int" + Parameter { name: "a"; type: "int" } + Parameter { name: "b"; type: "int" } + } + } + Component { + name: "QQuickScreenInfo" prototype: "QObject" + exports: ["QtQuick.Window/ScreenInfo 2.3"] + isCreatable: false + exportMetaObjectRevisions: [2] Property { name: "name"; type: "string"; isReadonly: true } Property { name: "width"; type: "int"; isReadonly: true } Property { name: "height"; type: "int"; isReadonly: true } @@ -42,25 +56,18 @@ Module { Property { name: "devicePixelRatio"; type: "double"; isReadonly: true } Property { name: "primaryOrientation"; type: "Qt::ScreenOrientation"; isReadonly: true } Property { name: "orientation"; type: "Qt::ScreenOrientation"; isReadonly: true } - Property { name: "orientationUpdateMask"; type: "Qt::ScreenOrientations" } + Property { name: "virtualX"; revision: 1; type: "int"; isReadonly: true } + Property { name: "virtualY"; revision: 1; type: "int"; isReadonly: true } Signal { name: "desktopGeometryChanged" } - Method { - name: "angleBetween" - type: "int" - Parameter { name: "a"; type: "int" } - Parameter { name: "b"; type: "int" } - } + Signal { name: "virtualXChanged"; revision: 1 } + Signal { name: "virtualYChanged"; revision: 1 } } Component { name: "QQuickWindow" defaultProperty: "data" prototype: "QWindow" - exports: [ - "QtQuick.Window/Window 2.0", - "QtQuick.Window/Window 2.1", - "QtQuick.Window/Window 2.2" - ] - exportMetaObjectRevisions: [0, 1, 2] + exports: ["QtQuick.Window/Window 2.0"] + exportMetaObjectRevisions: [0] Enum { name: "SceneGraphError" values: { @@ -125,11 +132,16 @@ Module { name: "QQuickWindowQmlImpl" defaultProperty: "data" prototype: "QQuickWindow" - exports: ["QtQuick.Window/Window 2.1", "QtQuick.Window/Window 2.2"] - exportMetaObjectRevisions: [0, 1] + exports: [ + "QtQuick.Window/Window 2.1", + "QtQuick.Window/Window 2.2", + "QtQuick.Window/Window 2.3" + ] + exportMetaObjectRevisions: [0, 1, 2] attachedType: "QQuickWindowAttached" Property { name: "visible"; type: "bool" } Property { name: "visibility"; type: "Visibility" } + Property { name: "screen"; revision: 2; type: "QObject"; isPointer: true } Signal { name: "visibleChanged" Parameter { name: "arg"; type: "bool" } @@ -138,6 +150,7 @@ Module { name: "visibilityChanged" Parameter { name: "visibility"; type: "QWindow::Visibility" } } + Signal { name: "screenChanged"; revision: 2 } } Component { name: "QWindow" @@ -153,6 +166,13 @@ Module { "FullScreen": 5 } } + Enum { + name: "AncestorMode" + values: { + "ExcludeTransients": 0, + "IncludeTransients": 1 + } + } Property { name: "title"; type: "string" } Property { name: "modality"; type: "Qt::WindowModality" } Property { name: "flags"; type: "Qt::WindowFlags" } diff --git a/src/particles/particles.pri b/src/particles/particles.pri index af71634ec6..576d826903 100644 --- a/src/particles/particles.pri +++ b/src/particles/particles.pri @@ -1,6 +1,5 @@ HEADERS += \ $$PWD/qquickangledirection_p.h \ - $$PWD/qquickcustomparticle_p.h \ $$PWD/qquickcustomaffector_p.h \ $$PWD/qquickellipseextruder_p.h \ $$PWD/qquicktrailemitter_p.h \ @@ -33,7 +32,6 @@ HEADERS += \ SOURCES += \ $$PWD/qquickangledirection.cpp \ - $$PWD/qquickcustomparticle.cpp \ $$PWD/qquickcustomaffector.cpp \ $$PWD/qquickellipseextruder.cpp \ $$PWD/qquicktrailemitter.cpp \ @@ -63,6 +61,14 @@ SOURCES += \ $$PWD/qquickparticlegroup.cpp \ $$PWD/qquickgroupgoal.cpp +qtConfig(quick-shadereffect) { +HEADERS += \ + $$PWD/qquickcustomparticle_p.h + +SOURCES += \ + $$PWD/qquickcustomparticle.cpp +} + OTHER_FILES += \ $$PWD/shaders/customparticletemplate.vert \ $$PWD/shaders/customparticle.vert \ diff --git a/src/particles/qquickparticlesmodule.cpp b/src/particles/qquickparticlesmodule.cpp index accdb668de..03f47a3961 100644 --- a/src/particles/qquickparticlesmodule.cpp +++ b/src/particles/qquickparticlesmodule.cpp @@ -37,8 +37,12 @@ ** ****************************************************************************/ +#include <private/qtquickglobal_p.h> + #include "qquickangledirection_p.h" +#if QT_CONFIG(quick_shadereffect) #include "qquickcustomparticle_p.h" +#endif #include "qquickellipseextruder_p.h" #include "qquicktrailemitter_p.h" #include "qquickfriction_p.h" @@ -84,7 +88,9 @@ void QQuickParticlesModule::defineModule() qmlRegisterType<QQuickParticleGroup>(uri, 2, 0, "ParticleGroup"); qmlRegisterType<QQuickImageParticle>(uri, 2, 0, "ImageParticle"); +#if QT_CONFIG(quick_shadereffect) qmlRegisterType<QQuickCustomParticle>(uri, 2, 0, "CustomParticle"); +#endif qmlRegisterType<QQuickItemParticle>(uri, 2, 0, "ItemParticle"); qmlRegisterType<QQuickParticleEmitter>(uri, 2, 0, "Emitter"); diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp index 99e278238b..6f134f08df 100644 --- a/src/particles/qquickparticlesystem.cpp +++ b/src/particles/qquickparticlesystem.cpp @@ -668,8 +668,11 @@ void QQuickParticleSystem::setPaused(bool arg) { if (m_animation && m_animation->state() != QAbstractAnimation::Stopped) m_paused ? m_animation->pause() : m_animation->resume(); if (!m_paused) { - foreach (QQuickParticlePainter *p, m_painters) - p->update(); + foreach (QQuickParticlePainter *p, m_painters) { + if (p) { + p->update(); + } + } } emit pausedChanged(arg); } @@ -873,8 +876,11 @@ void QQuickParticleSystem::emittersChanged() if (particleCount > bySysIdx.size())//New datum requests haven't updated it bySysIdx.resize(particleCount); - foreach (QQuickParticleAffector *a, m_affectors)//Groups may have changed - a->m_updateIntSet = true; + foreach (QQuickParticleAffector *a, m_affectors) {//Groups may have changed + if (a) { + a->m_updateIntSet = true; + } + } foreach (QQuickParticlePainter *p, m_painters) loadPainter(p); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp index 151e44c4d4..3fb0522cd1 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qqmlenginedebugservice.cpp @@ -409,18 +409,7 @@ QQmlEngineDebugServiceImpl::objectData(QObject *object) rv.objectId = QQmlDebugService::idForObject(object); rv.contextId = QQmlDebugService::idForObject(qmlContext(object)); rv.parentId = QQmlDebugService::idForObject(object->parent()); - QQmlType *type = QQmlMetaType::qmlType(object->metaObject()); - if (type) { - QString typeName = type->qmlTypeName(); - int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); - rv.objectType = lastSlash < 0 ? typeName : typeName.mid(lastSlash+1); - } else { - rv.objectType = QString::fromUtf8(object->metaObject()->className()); - int marker = rv.objectType.indexOf(QLatin1String("_QMLTYPE_")); - if (marker != -1) - rv.objectType = rv.objectType.left(marker); - } - + rv.objectType = QQmlMetaType::prettyTypeName(object); return rv; } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index a4bad5618b..430ba4f255 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -55,11 +55,11 @@ QT_BEGIN_NAMESPACE -QV4::CallContext *QV4DataCollector::findContext(int frame) +QV4::SimpleCallContext *QV4DataCollector::findContext(int frame) { QV4::ExecutionContext *ctx = engine()->currentContext; while (ctx) { - QV4::CallContext *cCtxt = ctx->asCallContext(); + QV4::SimpleCallContext *cCtxt = ctx->asSimpleCallContext(); if (cCtxt && cCtxt->d()->v4Function) { if (frame < 1) return cCtxt; @@ -71,7 +71,7 @@ QV4::CallContext *QV4DataCollector::findContext(int frame) return 0; } -QV4::Heap::CallContext *QV4DataCollector::findScope(QV4::ExecutionContext *ctxt, int scope) +QV4::Heap::SimpleCallContext *QV4DataCollector::findScope(QV4::ExecutionContext *ctxt, int scope) { if (!ctxt) return 0; @@ -81,7 +81,7 @@ QV4::Heap::CallContext *QV4DataCollector::findScope(QV4::ExecutionContext *ctxt, for (; scope > 0 && ctx; --scope) ctx = ctx->d()->outer; - return (ctx && ctx->d()) ? ctx->asCallContext()->d() : 0; + return (ctx && ctx->d()) ? ctx->asSimpleCallContext()->d() : 0; } QVector<QV4::Heap::ExecutionContext::ContextType> QV4DataCollector::getScopeTypes(int frame) @@ -89,13 +89,13 @@ QVector<QV4::Heap::ExecutionContext::ContextType> QV4DataCollector::getScopeType QVector<QV4::Heap::ExecutionContext::ContextType> types; QV4::Scope scope(engine()); - QV4::CallContext *sctxt = findContext(frame); + QV4::SimpleCallContext *sctxt = findContext(frame); if (!sctxt || sctxt->d()->type < QV4::Heap::ExecutionContext::Type_QmlContext) return types; QV4::ScopedContext it(scope, sctxt); for (; it; it = it->d()->outer) - types.append(it->d()->type); + types.append(QV4::Heap::ExecutionContext::ContextType(it->d()->type)); return types; } @@ -118,15 +118,18 @@ int QV4DataCollector::encodeScopeType(QV4::Heap::ExecutionContext::ContextType s } } -QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine) : m_engine(engine) +QV4DataCollector::QV4DataCollector(QV4::ExecutionEngine *engine) + : m_engine(engine), m_namesAsObjects(true), m_redundantRefs(true) { m_values.set(engine, engine->newArrayObject()); } +// TODO: Directly call addRef() once we don't need to support redundantRefs anymore QV4DataCollector::Ref QV4DataCollector::collect(const QV4::ScopedValue &value) { Ref ref = addRef(value); - m_collectedRefs.append(ref); + if (m_redundantRefs) + m_collectedRefs.append(ref); return ref; } @@ -184,30 +187,48 @@ const QV4::Object *collectProperty(const QV4::ScopedValue &value, QV4::Execution case QV4::Value::Integer_Type: dict.insert(valueKey, value->integerValue()); return 0; - default: // double - dict.insert(valueKey, value->doubleValue()); + default: {// double + const double val = value->doubleValue(); + if (qIsFinite(val)) + dict.insert(valueKey, val); + else if (qIsNaN(val)) + dict.insert(valueKey, QStringLiteral("NaN")); + else if (val < 0) + dict.insert(valueKey, QStringLiteral("-Infinity")); + else + dict.insert(valueKey, QStringLiteral("Infinity")); return 0; } + } } -QJsonObject QV4DataCollector::lookupRef(Ref ref) +QJsonObject QV4DataCollector::lookupRef(Ref ref, bool deep) { QJsonObject dict; - if (lookupSpecialRef(ref, &dict)) - return dict; + + if (m_namesAsObjects) { + if (lookupSpecialRef(ref, &dict)) + return dict; + } + + if (m_redundantRefs) + deep = true; dict.insert(QStringLiteral("handle"), qint64(ref)); QV4::Scope scope(engine()); QV4::ScopedValue value(scope, getValue(ref)); - if (const QV4::Object *o = collectProperty(value, engine(), dict)) - dict.insert(QStringLiteral("properties"), collectProperties(o)); + const QV4::Object *object = collectProperty(value, engine(), dict); + if (deep && object) + dict.insert(QStringLiteral("properties"), collectProperties(object)); return dict; } +// TODO: Drop this method once we don't need to support namesAsObjects anymore QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionName) { + Q_ASSERT(m_namesAsObjects); Ref ref = addRef(QV4::Primitive::emptyValue(), false); QJsonObject dict; @@ -220,8 +241,10 @@ QV4DataCollector::Ref QV4DataCollector::addFunctionRef(const QString &functionNa return ref; } +// TODO: Drop this method once we don't need to support namesAsObjects anymore QV4DataCollector::Ref QV4DataCollector::addScriptRef(const QString &scriptName) { + Q_ASSERT(m_namesAsObjects); Ref ref = addRef(QV4::Primitive::emptyValue(), false); QJsonObject dict; @@ -250,6 +273,7 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) if (!ctxt) return false; + Refs collectedRefs; QV4::ScopedValue v(scope); int nFormals = ctxt->formalCount(); for (unsigned i = 0, ei = nFormals; i != ei; ++i) { @@ -258,7 +282,7 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) qName = name->string; names.append(qName); v = ctxt->argument(i); - collect(v); + collectedRefs.append(collect(v)); } for (unsigned i = 0, ei = ctxt->variableCount(); i != ei; ++i) { @@ -267,21 +291,26 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) qName = name->string; names.append(qName); v = ctxt->d()->locals[i]; - collect(v); + collectedRefs.append(collect(v)); } QV4::ScopedObject scopeObject(scope, engine()->newObject()); - Q_ASSERT(names.size() == m_collectedRefs.size()); + Q_ASSERT(names.size() == collectedRefs.size()); QV4::ScopedString propName(scope); - for (int i = 0, ei = m_collectedRefs.size(); i != ei; ++i) { + for (int i = 0, ei = collectedRefs.size(); i != ei; ++i) { propName = engine()->newString(names.at(i)); - scopeObject->put(propName, QV4::Value::fromReturnedValue(getValue(m_collectedRefs.at(i)))); + scopeObject->put(propName, QV4::Value::fromReturnedValue(getValue(collectedRefs.at(i)))); } Ref scopeObjectRef = addRef(scopeObject); - dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef)); - m_collectedRefs.append(scopeObjectRef); + if (m_redundantRefs) { + dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef)); + m_collectedRefs.append(scopeObjectRef); + } else { + *dict = lookupRef(scopeObjectRef, true); + } + return true; } @@ -293,15 +322,16 @@ QJsonObject toRef(QV4DataCollector::Ref ref) { QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int frameNr) { - QV4DataCollector::Ref ref; - QJsonObject frame; frame[QLatin1String("index")] = frameNr; frame[QLatin1String("debuggerFrame")] = false; - ref = addFunctionRef(stackFrame.function); - frame[QLatin1String("func")] = toRef(ref); - ref = addScriptRef(stackFrame.source); - frame[QLatin1String("script")] = toRef(ref); + if (m_namesAsObjects) { + frame[QLatin1String("func")] = toRef(addFunctionRef(stackFrame.function)); + frame[QLatin1String("script")] = toRef(addScriptRef(stackFrame.source)); + } else { + frame[QLatin1String("func")] = stackFrame.function; + frame[QLatin1String("script")] = stackFrame.source; + } frame[QLatin1String("line")] = stackFrame.line - 1; if (stackFrame.column >= 0) frame[QLatin1String("column")] = stackFrame.column; @@ -310,7 +340,7 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int QV4::Scope scope(engine()); QV4::ScopedContext ctxt(scope, findContext(frameNr)); while (ctxt) { - if (QV4::CallContext *cCtxt = ctxt->asCallContext()) { + if (QV4::SimpleCallContext *cCtxt = ctxt->asSimpleCallContext()) { if (cCtxt->d()->activation) break; } @@ -318,7 +348,7 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int } if (ctxt) { - QV4::ScopedValue o(scope, ctxt->asCallContext()->d()->activation); + QV4::ScopedValue o(scope, ctxt->asSimpleCallContext()->d()->activation); frame[QLatin1String("receiver")] = toRef(collect(o)); } @@ -340,15 +370,17 @@ QJsonObject QV4DataCollector::buildFrame(const QV4::StackFrame &stackFrame, int return frame; } +// TODO: Drop this method once we don't need to support redundantRefs anymore QJsonArray QV4DataCollector::flushCollectedRefs() { + Q_ASSERT(m_redundantRefs); QJsonArray refs; std::sort(m_collectedRefs.begin(), m_collectedRefs.end()); for (int i = 0, ei = m_collectedRefs.size(); i != ei; ++i) { QV4DataCollector::Ref ref = m_collectedRefs.at(i); if (i > 0 && ref == m_collectedRefs.at(i - 1)) continue; - refs.append(lookupRef(ref)); + refs.append(lookupRef(ref, true)); } m_collectedRefs.clear(); @@ -360,14 +392,16 @@ void QV4DataCollector::clear() m_values.set(engine(), engine()->newArrayObject()); m_collectedRefs.clear(); m_specialRefs.clear(); + m_namesAsObjects = true; + m_redundantRefs = true; } QV4DataCollector::Ref QV4DataCollector::addRef(QV4::Value value, bool deduplicate) { class ExceptionStateSaver { - quint32 *hasExceptionLoc; - quint32 hadException; + quint8 *hasExceptionLoc; + quint8 hadException; public: ExceptionStateSaver(QV4::ExecutionEngine *engine) @@ -403,8 +437,10 @@ QV4::ReturnedValue QV4DataCollector::getValue(Ref ref) return array->getIndexed(ref, Q_NULLPTR); } +// TODO: Drop this method once we don't need to support namesAsObjects anymore bool QV4DataCollector::lookupSpecialRef(Ref ref, QJsonObject *dict) { + Q_ASSERT(m_namesAsObjects); SpecialRefs::const_iterator it = m_specialRefs.constFind(ref); if (it == m_specialRefs.cend()) return false; @@ -442,7 +478,8 @@ QJsonObject QV4DataCollector::collectAsJson(const QString &name, const QV4::Scop if (value->isManaged() && !value->isString()) { Ref ref = addRef(value); dict.insert(QStringLiteral("ref"), qint64(ref)); - m_collectedRefs.append(ref); + if (m_redundantRefs) + m_collectedRefs.append(ref); } collectProperty(value, engine(), dict); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h index fd6356f22e..de12e8d527 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.h @@ -58,42 +58,50 @@ public: typedef uint Ref; typedef QVector<uint> Refs; - static QV4::Heap::CallContext *findScope(QV4::ExecutionContext *ctxt, int scope); + static QV4::Heap::SimpleCallContext *findScope(QV4::ExecutionContext *ctxt, int scope); static int encodeScopeType(QV4::Heap::ExecutionContext::ContextType scopeType); QVector<QV4::Heap::ExecutionContext::ContextType> getScopeTypes(int frame); - QV4::CallContext *findContext(int frame); + QV4::SimpleCallContext *findContext(int frame); QV4DataCollector(QV4::ExecutionEngine *engine); - Ref collect(const QV4::ScopedValue &value); - Ref addFunctionRef(const QString &functionName); - Ref addScriptRef(const QString &scriptName); + Ref collect(const QV4::ScopedValue &value); // only for redundantRefs + Ref addFunctionRef(const QString &functionName); // only for namesAsObjects + Ref addScriptRef(const QString &scriptName); // only for namesAsObjects + + void setNamesAsObjects(bool namesAsObjects) { m_namesAsObjects = namesAsObjects; } + bool namesAsObjects() const { return m_namesAsObjects; } + + void setRedundantRefs(bool redundantRefs) { m_redundantRefs = redundantRefs; } + bool redundantRefs() const { return m_redundantRefs; } bool isValidRef(Ref ref) const; - QJsonObject lookupRef(Ref ref); + QJsonObject lookupRef(Ref ref, bool deep); bool collectScope(QJsonObject *dict, int frameNr, int scopeNr); QJsonObject buildFrame(const QV4::StackFrame &stackFrame, int frameNr); QV4::ExecutionEngine *engine() const { return m_engine; } - QJsonArray flushCollectedRefs(); + QJsonArray flushCollectedRefs(); // only for redundantRefs void clear(); private: Ref addRef(QV4::Value value, bool deduplicate = true); QV4::ReturnedValue getValue(Ref ref); - bool lookupSpecialRef(Ref ref, QJsonObject *dict); + bool lookupSpecialRef(Ref ref, QJsonObject *dict); // only for namesAsObjects QJsonArray collectProperties(const QV4::Object *object); QJsonObject collectAsJson(const QString &name, const QV4::ScopedValue &value); void collectArgumentsInContext(); QV4::ExecutionEngine *m_engine; - Refs m_collectedRefs; + Refs m_collectedRefs; // only for redundantRefs QV4::PersistentValue m_values; - typedef QHash<Ref, QJsonObject> SpecialRefs; - SpecialRefs m_specialRefs; + typedef QHash<Ref, QJsonObject> SpecialRefs; // only for namesAsObjects + SpecialRefs m_specialRefs; // only for namesAsObjects + bool m_namesAsObjects; + bool m_redundantRefs; }; QT_END_NAMESPACE diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp index 5cc2043cb1..75cbc9eba1 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugger.cpp @@ -230,7 +230,12 @@ void QV4Debugger::leavingFunction(const QV4::ReturnedValue &retVal) QMutexLocker locker(&m_lock); if (m_stepping != NotStepping && m_currentContext.asManaged()->d() == m_engine->current) { - m_currentContext.set(m_engine, *m_engine->parentContext(m_engine->currentContext)); + if (QV4::ExecutionContext *parentContext + = m_engine->parentContext(m_engine->currentContext)) { + m_currentContext.set(m_engine, *parentContext); + } else { + m_currentContext.clear(); + } m_stepping = StepOver; m_returnedValue.set(m_engine, retVal); } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp index d5fadad50a..107ec60943 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp @@ -151,7 +151,7 @@ void BacktraceJob::run() result.insert(QStringLiteral("toFrame"), fromFrame + frameArray.size()); result.insert(QStringLiteral("frames"), frameArray); } - collectedRefs = collector->flushCollectedRefs(); + flushRedundantRefs(); } FrameJob::FrameJob(QV4DataCollector *collector, int frameNr) : @@ -166,7 +166,7 @@ void FrameJob::run() success = false; } else { result = collector->buildFrame(frames[frameNr], frameNr); - collectedRefs = collector->flushCollectedRefs(); + flushRedundantRefs(); success = true; } } @@ -196,7 +196,7 @@ void ScopeJob::run() result[QLatin1String("index")] = scopeNr; result[QLatin1String("frameIndex")] = frameNr; result[QLatin1String("object")] = object; - collectedRefs = collector->flushCollectedRefs(); + flushRedundantRefs(); } bool ScopeJob::wasSuccessful() const @@ -226,9 +226,9 @@ void ValueLookupJob::run() exception = QString::fromLatin1("Invalid Ref: %1").arg(ref); break; } - result[QString::number(ref)] = collector->lookupRef(ref); + result[QString::number(ref)] = collector->lookupRef(ref, true); } - collectedRefs = collector->flushCollectedRefs(); + flushRedundantRefs(); if (scopeObject) engine->popContext(); } @@ -249,8 +249,9 @@ void ExpressionEvalJob::handleResult(QV4::ScopedValue &value) { if (hasExeption()) exception = value->toQStringNoThrow(); - result = collector->lookupRef(collector->collect(value)); - collectedRefs = collector->flushCollectedRefs(); + result = collector->lookupRef(collector->collect(value), true); + if (collector->redundantRefs()) + collectedRefs = collector->flushCollectedRefs(); } const QString &ExpressionEvalJob::exceptionMessage() const @@ -263,8 +264,10 @@ const QJsonObject &ExpressionEvalJob::returnValue() const return result; } +// TODO: Drop this method once we don't need to support redundantRefs anymore const QJsonArray &ExpressionEvalJob::refs() const { + Q_ASSERT(collector->redundantRefs()); return collectedRefs; } diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h index 00d3e6206a..eca8710e15 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.h @@ -78,11 +78,24 @@ class CollectJob : public QV4DebugJob protected: QV4DataCollector *collector; QJsonObject result; - QJsonArray collectedRefs; + QJsonArray collectedRefs; // only for redundantRefs + + void flushRedundantRefs() + { + if (collector->redundantRefs()) + collectedRefs = collector->flushCollectedRefs(); + } + public: CollectJob(QV4DataCollector *collector) : collector(collector) {} const QJsonObject &returnValue() const { return result; } - const QJsonArray &refs() const { return collectedRefs; } + + // TODO: Drop this method once we don't need to support redundantRefs anymore + const QJsonArray &refs() const + { + Q_ASSERT(collector->redundantRefs()); + return collectedRefs; + } }; class BacktraceJob: public CollectJob @@ -133,7 +146,7 @@ class ExpressionEvalJob: public JavaScriptJob QV4DataCollector *collector; QString exception; QJsonObject result; - QJsonArray collectedRefs; + QJsonArray collectedRefs; // only for redundantRefs public: ExpressionEvalJob(QV4::ExecutionEngine *engine, int frameNr, int context, @@ -141,7 +154,7 @@ public: void handleResult(QV4::ScopedValue &value) override; const QString &exceptionMessage() const; const QJsonObject &returnValue() const; - const QJsonArray &refs() const; + const QJsonArray &refs() const; // only for redundantRefs }; class GatherSourcesJob: public QV4DebugJob diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp index 1d2cc092dc..68cdb57a44 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.cpp @@ -121,8 +121,18 @@ protected: response.insert(QStringLiteral("running"), debugService->debuggerAgent.isRunning()); } + QV4DataCollector *saneCollector(QV4Debugger *debugger) + { + QV4DataCollector *collector = debugger->collector(); + collector->setNamesAsObjects(debugService->clientRequiresNamesAsObjects()); + collector->setRedundantRefs(debugService->clientRequiresRedundantRefs()); + return collector; + } + + // TODO: drop this method once we don't need to support redundantRefs anymore. void addRefs(const QJsonArray &refs) { + Q_ASSERT(debugService->clientRequiresRedundantRefs()); response.insert(QStringLiteral("refs"), refs); } @@ -286,7 +296,7 @@ public: return; } - BacktraceJob job(debugger->collector(), fromFrame, toFrame); + BacktraceJob job(saneCollector(debugger), fromFrame, toFrame); debugger->runInEngine(&job); // response: @@ -295,7 +305,8 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - addRefs(job.refs()); + if (debugService->clientRequiresRedundantRefs()) + addRefs(job.refs()); } }; @@ -322,7 +333,7 @@ public: return; } - FrameJob job(debugger->collector(), frameNr); + FrameJob job(saneCollector(debugger), frameNr); debugger->runInEngine(&job); if (!job.wasSuccessful()) { createErrorResponse(QStringLiteral("frame retrieval failed")); @@ -337,7 +348,8 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - addRefs(job.refs()); + if (debugService->clientRequiresRedundantRefs()) + addRefs(job.refs()); } }; @@ -369,7 +381,7 @@ public: return; } - ScopeJob job(debugger->collector(), frameNr, scopeNr); + ScopeJob job(saneCollector(debugger), frameNr, scopeNr); debugger->runInEngine(&job); if (!job.wasSuccessful()) { createErrorResponse(QStringLiteral("scope retrieval failed")); @@ -382,7 +394,8 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - addRefs(job.refs()); + if (debugService->clientRequiresRedundantRefs()) + addRefs(job.refs()); } }; @@ -410,7 +423,7 @@ public: debugger = debuggers.first(); } - ValueLookupJob job(handles, debugger->collector()); + ValueLookupJob job(handles, saneCollector(debugger)); debugger->runInEngine(&job); if (!job.exceptionMessage().isEmpty()) { createErrorResponse(job.exceptionMessage()); @@ -421,7 +434,8 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - addRefs(job.refs()); + if (debugService->clientRequiresRedundantRefs()) + addRefs(job.refs()); } } }; @@ -630,7 +644,7 @@ public: } ExpressionEvalJob job(debugger->engine(), frame, context, expression, - debugger->collector()); + saneCollector(debugger)); debugger->runInEngine(&job); if (job.hasExeption()) { createErrorResponse(job.exceptionMessage()); @@ -640,7 +654,8 @@ public: addSuccess(true); addRunning(); addBody(job.returnValue()); - addRefs(job.refs()); + if (debugService->clientRequiresRedundantRefs()) + addRefs(job.refs()); } } }; @@ -662,7 +677,7 @@ V8CommandHandler *QV4DebugServiceImpl::v8CommandHandler(const QString &command) QV4DebugServiceImpl::QV4DebugServiceImpl(QObject *parent) : QQmlConfigurableDebugService<QV4DebugService>(1, parent), - debuggerAgent(this), theSelectedFrame(0), + debuggerAgent(this), theSelectedFrame(0), redundantRefs(true), namesAsObjects(true), unknownV8CommandHandler(new UnknownV8CommandHandler) { addHandler(new V8VersionRequest); @@ -766,6 +781,14 @@ void QV4DebugServiceImpl::messageReceived(const QByteArray &message) TRACE_PROTOCOL(qDebug() << "... type:" << type); if (type == V4_CONNECT) { + QJsonObject parameters = QJsonDocument::fromJson(payload).object(); + namesAsObjects = true; + redundantRefs = true; + if (parameters.contains("namesAsObjects")) + namesAsObjects = parameters.value("namesAsObjects").toBool(); + if (parameters.contains("redundantRefs")) + redundantRefs = parameters.value("redundantRefs").toBool(); + emit messageToClient(name(), packMessage(type)); stopWaiting(); } else if (type == V4_PAUSE) { diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h index 69e32189b8..bb13890ae4 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugservice.h @@ -86,6 +86,9 @@ public: int selectedFrame() const; void selectFrame(int frameNr); + bool clientRequiresRedundantRefs() const { return redundantRefs; } + bool clientRequiresNamesAsObjects() const { return namesAsObjects; } + QV4DebuggerAgent debuggerAgent; protected: @@ -105,6 +108,9 @@ private: static int sequence; int theSelectedFrame; + bool redundantRefs; + bool namesAsObjects; + void addHandler(V8CommandHandler* handler); QHash<QString, V8CommandHandler*> handlers; QScopedPointer<UnknownV8CommandHandler> unknownV8CommandHandler; diff --git a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp index 7f842419e7..6cb1ab4051 100644 --- a/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp +++ b/src/plugins/qmltooling/qmldbg_nativedebugger/qqmlnativedebugservice.cpp @@ -481,17 +481,20 @@ void NativeDebugger::handleVariables(QJsonObject *response, const QJsonObject &a QJsonArray output; QV4::Scope scope(engine); - if (QV4::CallContext *callContext = executionContext->asCallContext()) { + if (QV4::SimpleCallContext *callContext = executionContext->asSimpleCallContext()) { QV4::Value thisObject = callContext->thisObject(); collector.collect(&output, QString(), QStringLiteral("this"), thisObject); QV4::Identifier *const *variables = callContext->variables(); QV4::Identifier *const *formals = callContext->formals(); - for (unsigned i = 0, ei = callContext->variableCount(); i != ei; ++i) { - QString qName; - if (QV4::Identifier *name = variables[i]) - qName = name->string; - QV4::Value val = callContext->d()->locals[i]; - collector.collect(&output, QString(), qName, val); + if (callContext->d()->type == QV4::Heap::ExecutionContext::Type_CallContext) { + QV4::CallContext *ctx = static_cast<QV4::CallContext *>(callContext); + for (unsigned i = 0, ei = ctx->variableCount(); i != ei; ++i) { + QString qName; + if (QV4::Identifier *name = variables[i]) + qName = name->string; + QV4::Value val = ctx->d()->locals[i]; + collector.collect(&output, QString(), qName, val); + } } for (unsigned i = 0, ei = callContext->formalCount(); i != ei; ++i) { QString qName; diff --git a/src/plugins/scenegraph/openvg/openvg.pro b/src/plugins/scenegraph/openvg/openvg.pro index 6d5b190b37..43c2636343 100644 --- a/src/plugins/scenegraph/openvg/openvg.pro +++ b/src/plugins/scenegraph/openvg/openvg.pro @@ -31,7 +31,6 @@ HEADERS += \ qsgopenvghelpers.h \ qsgopenvgfontglyphcache.h \ qsgopenvgpainternode.h \ - qsgopenvgspritenode.h \ qsgopenvgrenderable.h \ qopenvgoffscreensurface.h @@ -52,6 +51,10 @@ SOURCES += \ qsgopenvghelpers.cpp \ qsgopenvgfontglyphcache.cpp \ qsgopenvgpainternode.cpp \ - qsgopenvgspritenode.cpp \ qsgopenvgrenderable.cpp \ qopenvgoffscreensurface.cpp + +qtConfig(quick-sprite) { + HEADERS += qsgopenvgspritenode.h + SOURCES += qsgopenvgspritenode.cpp +} diff --git a/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp b/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp index 41fce7c7fc..76ebb7c4ee 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgcontext.cpp @@ -45,7 +45,9 @@ #include "qsgopenvgglyphnode_p.h" #include "qsgopenvgfontglyphcache.h" #include "qsgopenvgpainternode.h" +#if QT_CONFIG(quick_sprite) #include "qsgopenvgspritenode.h" +#endif #include "qopenvgcontext_p.h" @@ -171,11 +173,12 @@ int QSGOpenVGRenderContext::maxTextureSize() const return qMin(width, height); } - +#if QT_CONFIG(quick_sprite) QSGSpriteNode *QSGOpenVGContext::createSpriteNode() { return new QSGOpenVGSpriteNode(); } +#endif QSGRendererInterface *QSGOpenVGContext::rendererInterface(QSGRenderContext *renderContext) { diff --git a/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h b/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h index fa9939a253..31a1e8643f 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h +++ b/src/plugins/scenegraph/openvg/qsgopenvgcontext_p.h @@ -95,7 +95,9 @@ public: QSurfaceFormat defaultSurfaceFormat() const override; QSGInternalRectangleNode *createInternalRectangleNode() override; QSGInternalImageNode *createInternalImageNode() override; +#if QT_CONFIG(quick_sprite) QSGSpriteNode *createSpriteNode() override; +#endif QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext) override; }; diff --git a/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.cpp b/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.cpp index dd630c776f..df47e920af 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.cpp @@ -94,7 +94,7 @@ void QSGOpenVGFontGlyphCache::populate(const QVector<quint32> &glyphs) referencedGlyphs.insert(glyphIndex); - if (!m_cachedGlyphs.contains(glyphIndex)) { + if (!m_glyphReferences.contains(glyphIndex)) { newGlyphs.insert(glyphIndex); } } @@ -119,17 +119,9 @@ void QSGOpenVGFontGlyphCache::requestGlyphs(const QSet<quint32> &glyphs) { VGfloat origin[2]; VGfloat escapement[2]; - QRectF metrics; QRawFont rawFont = m_referenceFont; - // Before adding any new glyphs, remove any unused glyphs - for (auto glyph : qAsConst(m_unusedGlyphs)) { - vgClearGlyph(m_font, glyph); - } - for (auto glyph : glyphs) { - m_cachedGlyphs.insert(glyph); - // Calculate the path for the glyph and cache it. QPainterPath path = rawFont.pathForGlyph(glyph); VGPath vgPath; @@ -151,12 +143,23 @@ void QSGOpenVGFontGlyphCache::requestGlyphs(const QSet<quint32> &glyphs) void QSGOpenVGFontGlyphCache::referenceGlyphs(const QSet<quint32> &glyphs) { - m_unusedGlyphs -= glyphs; + for (auto glyph : glyphs) { + if (m_glyphReferences.contains(glyph)) + m_glyphReferences[glyph] += 1; + else + m_glyphReferences.insert(glyph, 1); + } } void QSGOpenVGFontGlyphCache::releaseGlyphs(const QSet<quint32> &glyphs) { - m_unusedGlyphs += glyphs; + for (auto glyph : glyphs) { + int references = m_glyphReferences[glyph] -= 1; + if (references == 0) { + vgClearGlyph(m_font, glyph); + m_glyphReferences.remove(glyph); + } + } } QT_END_NAMESPACE diff --git a/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h b/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h index a88d28b0fe..107ec0c892 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h +++ b/src/plugins/scenegraph/openvg/qsgopenvgfontglyphcache.h @@ -87,8 +87,7 @@ private: int m_glyphCount; VGFont m_font; - QSet<quint32> m_cachedGlyphs; - QSet<quint32> m_unusedGlyphs; + QHash<quint32, int> m_glyphReferences; }; diff --git a/src/plugins/scenegraph/openvg/qsgopenvglayer.h b/src/plugins/scenegraph/openvg/qsgopenvglayer.h index 2af0bfb40f..8deedc3347 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvglayer.h +++ b/src/plugins/scenegraph/openvg/qsgopenvglayer.h @@ -83,6 +83,7 @@ public: void setDevicePixelRatio(qreal ratio) override; void setMirrorHorizontal(bool mirror) override; void setMirrorVertical(bool mirror) override; + void setSamples(int) override { } public slots: void markDirtyTexture() override; diff --git a/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.cpp b/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.cpp index 8aa179f705..41606c653a 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.cpp @@ -43,7 +43,9 @@ #include "qsgopenvgpublicnodes.h" #include "qsgopenvgglyphnode_p.h" #include "qsgopenvgpainternode.h" +#if QT_CONFIG(quick_sprite) #include "qsgopenvgspritenode.h" +#endif #include "qsgopenvgrenderable.h" #include "qopenvgcontext_p.h" @@ -209,6 +211,7 @@ void QSGOpenVGNodeVisitor::endVisit(QSGRootNode *) { } +#if QT_CONFIG(quick_sprite) bool QSGOpenVGNodeVisitor::visit(QSGSpriteNode *node) { renderRenderableNode(static_cast<QSGOpenVGSpriteNode*>(node)); @@ -218,6 +221,7 @@ bool QSGOpenVGNodeVisitor::visit(QSGSpriteNode *node) void QSGOpenVGNodeVisitor::endVisit(QSGSpriteNode *) { } +#endif bool QSGOpenVGNodeVisitor::visit(QSGRenderNode *) { diff --git a/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.h b/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.h index 4805d63024..c6461ca67d 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.h +++ b/src/plugins/scenegraph/openvg/qsgopenvgnodevisitor.h @@ -73,8 +73,10 @@ public: void endVisit(QSGGlyphNode *) override; bool visit(QSGRootNode *) override; void endVisit(QSGRootNode *) override; +#if QT_CONFIG(quick_sprite) bool visit(QSGSpriteNode *) override; void endVisit(QSGSpriteNode *) override; +#endif bool visit(QSGRenderNode *) override; void endVisit(QSGRenderNode *) override; diff --git a/src/plugins/scenegraph/openvg/qsgopenvgspritenode.h b/src/plugins/scenegraph/openvg/qsgopenvgspritenode.h index 3ade2ef8ad..d47b389a0b 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgspritenode.h +++ b/src/plugins/scenegraph/openvg/qsgopenvgspritenode.h @@ -43,6 +43,8 @@ #include <private/qsgadaptationlayer_p.h> #include "qsgopenvgrenderable.h" +QT_REQUIRE_CONFIG(quick_sprite); + QT_BEGIN_NAMESPACE class QSGOpenVGTexture; class QSGOpenVGSpriteNode : public QSGSpriteNode, public QSGOpenVGRenderable diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index fa66d3a6e3..871f28f2d0 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -10,7 +10,8 @@ HEADERS += \ $$PWD/qv4isel_util_p.h \ $$PWD/qv4ssa_p.h \ $$PWD/qqmlirbuilder_p.h \ - $$PWD/qqmltypecompiler_p.h + $$PWD/qqmltypecompiler_p.h \ + $$PWD/qv4jssimplifier_p.h SOURCES += \ $$PWD/qv4compileddata.cpp \ @@ -19,7 +20,8 @@ SOURCES += \ $$PWD/qv4isel_p.cpp \ $$PWD/qv4jsir.cpp \ $$PWD/qv4ssa.cpp \ - $$PWD/qqmlirbuilder.cpp + $$PWD/qqmlirbuilder.cpp \ + $$PWD/qv4jssimplifier.cpp !qmldevtools_build { diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 030f485504..218f5675dc 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1361,7 +1361,7 @@ bool IRBuilder::isRedundantNullInitializerForPropertyDeclaration(Property *prope return QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr); } -QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes) +QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) { QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = output.javaScriptCompilationUnit; QV4::CompiledData::Unit *jsUnit = compilationUnit->createUnitData(&output); @@ -1404,17 +1404,16 @@ QV4::CompiledData::Unit *QmlUnitGenerator::generate(Document &output, QQmlEngine qmlUnit->stringTableSize = output.jsGenerator.stringTable.stringCount(); #ifndef V4_BOOTSTRAP - if (!dependentTypes.isEmpty()) { + if (dependencyHasher) { QCryptographicHash hash(QCryptographicHash::Md5); - if (dependentTypes.addToHash(&hash, engine)) { + if (dependencyHasher(&hash)) { QByteArray checksum = hash.result(); Q_ASSERT(checksum.size() == sizeof(qmlUnit->dependencyMD5Checksum)); memcpy(qmlUnit->dependencyMD5Checksum, checksum.constData(), sizeof(qmlUnit->dependencyMD5Checksum)); } } #else - Q_UNUSED(dependentTypes); - Q_UNUSED(engine); + Q_UNUSED(dependencyHasher); #endif // write imports @@ -2116,7 +2115,8 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; - + object->flags = serializedObject->flags; + object->id = serializedObject->id; object->location = serializedObject->location; object->locationOfIdProperty = serializedObject->locationOfIdProperty; @@ -2175,6 +2175,15 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO object->properties->append(p); } + { + const QV4::CompiledData::Alias *serializedAlias = serializedObject->aliasTable(); + for (uint i = 0; i < serializedObject->nAliases; ++i, ++serializedAlias) { + QmlIR::Alias *a = pool->New<QmlIR::Alias>(); + *static_cast<QV4::CompiledData::Alias*>(a) = *serializedAlias; + object->aliases->append(a); + } + } + QQmlJS::Engine *jsParserEngine = &output->jsParserEngine; const QV4::CompiledData::LEUInt32 *functionIdx = serializedObject->functionOffsetTable(); @@ -2205,6 +2214,11 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO const QString name = unit->stringAt(compiledFunction->nameIndex); f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/0); + f->formals.allocate(pool, int(compiledFunction->nFormals)); + formalNameIdx = compiledFunction->formalsTable(); + for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) + f->formals[i] = *formalNameIdx; + object->functions->append(f); } diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index b579a963a1..64bf111d9a 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -548,7 +548,7 @@ public: struct Q_QML_PRIVATE_EXPORT QmlUnitGenerator { - QV4::CompiledData::Unit *generate(Document &output, QQmlEngine *engine, const QV4::CompiledData::ResolvedTypeReferenceMap &dependentTypes); + QV4::CompiledData::Unit *generate(Document &output, const QV4::CompiledData::DependentTypesHasher &dependencyHasher = QV4::CompiledData::DependentTypesHasher()); private: typedef bool (Binding::*BindingFilter)() const; diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 109eb86c2e..d1d22be0ac 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -47,6 +47,7 @@ #include <private/qv4ssa_p.h> #include "qqmlpropertycachecreator_p.h" +#include "qv4jssimplifier_p.h" #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -58,10 +59,11 @@ QT_BEGIN_NAMESPACE QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher) : resolvedTypes(resolvedTypeCache) , engine(engine) , typeData(typeData) + , dependencyHasher(dependencyHasher) , typeNameCache(typeNameCache) , document(parsedQML) { @@ -143,7 +145,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() if (!jsCodeGen.generateCodeForComponents()) return nullptr; - QQmlJavaScriptBindingExpressionSimplificationPass pass(this); + QQmlJavaScriptBindingExpressionSimplificationPass pass(document->objects, &document->jsModule, &document->jsGenerator); pass.reduceTranslationBindings(); QV4::ExecutionEngine *v4 = engine->v4engine(); @@ -156,7 +158,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() // Generate QML compiled type data structures QmlIR::QmlUnitGenerator qmlGenerator; - QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document, QQmlEnginePrivate::get(engine), resolvedTypes); + QV4::CompiledData::Unit *qmlUnit = qmlGenerator.generate(*document, dependencyHasher); Q_ASSERT(document->javaScriptCompilationUnit); // The js unit owns the data and will free the qml unit. @@ -1428,344 +1430,4 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex) } } -QQmlJavaScriptBindingExpressionSimplificationPass::QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , jsModule(typeCompiler->jsIRModule()) -{ - -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings() -{ - for (int i = 0; i < qmlObjects.count(); ++i) - reduceTranslationBindings(i); - if (!irFunctionsToRemove.isEmpty()) { - QQmlIRFunctionCleanser cleanser(compiler, irFunctionsToRemove); - cleanser.clean(); - } -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings(int objectIndex) -{ - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - - const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex); - QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex); - if (simplifyBinding(irFunction, binding)) { - irFunctionsToRemove.append(irFunctionIndex); - jsModule->functions[irFunctionIndex] = 0; - delete irFunction; - } - } -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move *move) -{ - QV4::IR::Temp *target = move->target->asTemp(); - if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { - discard(); - return; - } - - if (QV4::IR::Call *call = move->source->asCall()) { - if (QV4::IR::Name *n = call->base->asName()) { - if (n->builtin == QV4::IR::Name::builtin_invalid) { - visitFunctionCall(n->id, call->args, target); - return; - } - } - discard(); - return; - } - - if (QV4::IR::Name *n = move->source->asName()) { - if (n->builtin == QV4::IR::Name::builtin_qml_context - || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) { - // these are free of side-effects - return; - } - discard(); - return; - } - - if (!move->source->asTemp() && !move->source->asString() && !move->source->asConst()) { - discard(); - return; - } - - _temps[target->index] = move->source; -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target) -{ - // more than one function call? - if (_nameOfFunctionCalled) { - discard(); - return; - } - - _nameOfFunctionCalled = name; - - _functionParameters.clear(); - while (args) { - int slot; - if (QV4::IR::Temp *param = args->expr->asTemp()) { - if (param->kind != QV4::IR::Temp::VirtualRegister) { - discard(); - return; - } - slot = param->index; - _functionParameters.append(slot); - } else if (QV4::IR::Const *param = args->expr->asConst()) { - slot = --_synthesizedConsts; - Q_ASSERT(!_temps.contains(slot)); - _temps[slot] = param; - _functionParameters.append(slot); - } - args = args->next; - } - - _functionCallReturnValue = target->index; -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::visitRet(QV4::IR::Ret *ret) -{ - // nothing initialized earlier? - if (_returnValueOfBindingExpression != -1) { - discard(); - return; - } - QV4::IR::Temp *target = ret->expr->asTemp(); - if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { - discard(); - return; - } - _returnValueOfBindingExpression = target->index; -} - -bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding) -{ - _canSimplify = true; - _nameOfFunctionCalled = 0; - _functionParameters.clear(); - _functionCallReturnValue = -1; - _temps.clear(); - _returnValueOfBindingExpression = -1; - _synthesizedConsts = 0; - - // It would seem unlikely that function with some many basic blocks (after optimization) - // consists merely of a qsTr call or a constant value return ;-) - if (function->basicBlockCount() > 10) - return false; - - for (QV4::IR::BasicBlock *bb : function->basicBlocks()) { - for (QV4::IR::Stmt *s : bb->statements()) { - visit(s); - if (!_canSimplify) - return false; - } - } - - if (_returnValueOfBindingExpression == -1) - return false; - - if (_nameOfFunctionCalled) { - if (_functionCallReturnValue != _returnValueOfBindingExpression) - return false; - return detectTranslationCallAndConvertBinding(binding); - } - - return false; -} - -bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding) -{ - if (*_nameOfFunctionCalled == QLatin1String("qsTr")) { - QString translation; - QV4::CompiledData::TranslationData translationData; - translationData.number = -1; - translationData.commentIndex = 0; // empty string - - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - translation = *stringParam->value; - - ++param; - if (param != end) { - stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - translationData.commentIndex = compiler->registerString(*stringParam->value); - ++param; - - if (param != end) { - QV4::IR::Const *constParam = _temps[*param]->asConst(); - if (!constParam || constParam->type != QV4::IR::SInt32Type) - return false; - - translationData.number = int(constParam->value); - ++param; - } - } - - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_Translation; - binding->stringIndex = compiler->registerString(translation); - binding->value.translationData = translationData; - return true; - } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) { - QString id; - QV4::CompiledData::TranslationData translationData; - translationData.number = -1; - translationData.commentIndex = 0; // empty string, but unused - - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - id = *stringParam->value; - - ++param; - if (param != end) { - QV4::IR::Const *constParam = _temps[*param]->asConst(); - if (!constParam || constParam->type != QV4::IR::SInt32Type) - return false; - - translationData.number = int(constParam->value); - ++param; - } - - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_TranslationById; - binding->stringIndex = compiler->registerString(id); - binding->value.translationData = translationData; - return true; - } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) { - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - ++param; - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_String; - binding->stringIndex = compiler->registerString(*stringParam->value); - return true; - } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) { - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - ++param; - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - ++param; - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_String; - binding->stringIndex = compiler->registerString(*stringParam->value); - return true; - } - return false; -} - -QQmlIRFunctionCleanser::QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove) - : QQmlCompilePass(typeCompiler) - , module(typeCompiler->jsIRModule()) - , functionsToRemove(functionsToRemove) -{ -} - -void QQmlIRFunctionCleanser::clean() -{ - QVector<QV4::IR::Function*> newFunctions; - newFunctions.reserve(module->functions.count() - functionsToRemove.count()); - - newFunctionIndices.resize(module->functions.count()); - - for (int i = 0; i < module->functions.count(); ++i) { - QV4::IR::Function *f = module->functions.at(i); - Q_ASSERT(f || functionsToRemove.contains(i)); - if (f) { - newFunctionIndices[i] = newFunctions.count(); - newFunctions << f; - } - } - - module->functions = newFunctions; - - for (QV4::IR::Function *function : qAsConst(module->functions)) { - for (QV4::IR::BasicBlock *block : function->basicBlocks()) { - for (QV4::IR::Stmt *s : block->statements()) { - visit(s); - } - } - } - - for (QmlIR::Object *obj : qAsConst(*compiler->qmlObjects())) { - for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i) - obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)]; - } -} - -void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s) -{ - - switch (s->stmtKind) { - case QV4::IR::Stmt::PhiStmt: - // nothing to do - break; - default: - STMT_VISIT_ALL_KINDS(s); - break; - } -} - -void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e) -{ - switch (e->exprKind) { - case QV4::IR::Expr::ClosureExpr: { - auto closure = e->asClosure(); - closure->value = newFunctionIndices.at(closure->value); - } break; - default: - EXPR_VISIT_ALL_KINDS(e); - break; - } -} - QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 2b59e7e42f..79fc073d8b 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -89,7 +89,8 @@ struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); + QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache, + const QV4::CompiledData::DependentTypesHasher &dependencyHasher); // --- interface used by QQmlPropertyCacheCreator typedef QmlIR::Object CompiledObject; @@ -139,6 +140,7 @@ private: QList<QQmlError> errors; QQmlEnginePrivate *engine; QQmlTypeData *typeData; + const QV4::CompiledData::DependentTypesHasher &dependencyHasher; QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QmlIR::Document *document; // index is string index of type name (use obj->inheritedTypeNameIndex) @@ -343,86 +345,6 @@ private: const QQmlPropertyCacheVector * const propertyCaches; }; -class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass -{ -public: - QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler); - - void reduceTranslationBindings(); - -private: - void reduceTranslationBindings(int objectIndex); - - void visit(QV4::IR::Stmt *s) - { - switch (s->stmtKind) { - case QV4::IR::Stmt::MoveStmt: - visitMove(s->asMove()); - break; - case QV4::IR::Stmt::RetStmt: - visitRet(s->asRet()); - break; - case QV4::IR::Stmt::CJumpStmt: - discard(); - break; - case QV4::IR::Stmt::ExpStmt: - discard(); - break; - case QV4::IR::Stmt::JumpStmt: - break; - case QV4::IR::Stmt::PhiStmt: - break; - } - } - - void visitMove(QV4::IR::Move *move); - void visitRet(QV4::IR::Ret *ret); - - void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target); - - void discard() { _canSimplify = false; } - - bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding); - bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding); - - const QVector<QmlIR::Object*> &qmlObjects; - QV4::IR::Module *jsModule; - - bool _canSimplify; - const QString *_nameOfFunctionCalled; - QVector<int> _functionParameters; - int _functionCallReturnValue; - - QHash<int, QV4::IR::Expr*> _temps; - int _returnValueOfBindingExpression; - int _synthesizedConsts; - - QVector<int> irFunctionsToRemove; -}; - -class QQmlIRFunctionCleanser : public QQmlCompilePass -{ -public: - QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove); - - void clean(); - -private: - virtual void visitMove(QV4::IR::Move *s) { - visit(s->source); - visit(s->target); - } - - void visit(QV4::IR::Stmt *s); - void visit(QV4::IR::Expr *e); - -private: - QV4::IR::Module *module; - const QVector<int> &functionsToRemove; - - QVector<int> newFunctionIndices; -}; - QT_END_NAMESPACE #endif // QQMLTYPECOMPILER_P_H diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 0afc97e4bf..693a4230ba 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -92,6 +92,27 @@ static bool cjumpCanHandle(IR::AluOp op) } } +static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body, + const SourceLocation &fallback) +{ + switch (body->kind) { + // Statements where we might never execute the last line. + // Use the fallback. + case Statement::Kind_ConditionalExpression: + case Statement::Kind_ForEachStatement: + case Statement::Kind_ForStatement: + case Statement::Kind_IfStatement: + case Statement::Kind_LocalForEachStatement: + case Statement::Kind_LocalForStatement: + case Statement::Kind_WhileStatement: + setLocation(s, fallback); + break; + default: + setLocation(s, body->lastSourceLocation()); + break; + } +} + Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) : _cg(cg) , _sourceCode(sourceCode) @@ -1497,7 +1518,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) IR::Function *f = _function; while (f && e->parent) { - if (f->insideWithOrCatch || (f->isNamedExpression && f->name == name)) + if (f->insideWithOrCatch || (f->isNamedExpression && QStringRef(f->name) == name)) return _block->NAME(name, line, col); int index = e->findMember(name); @@ -1508,7 +1529,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) al->isArgumentsOrEval = true; return al; } - const int argIdx = f->indexOfArgument(&name); + const int argIdx = f->indexOfArgument(QStringRef(&name)); if (argIdx != -1) return _block->ARG(argIdx, scope); @@ -2269,7 +2290,7 @@ bool Codegen::visit(DoWhileStatement *ast) _block = loopbody; statement(ast->statement); - _block->JUMP(loopcond); + setJumpOutLocation(_block->JUMP(loopcond), ast->statement, ast->semicolonToken); _block = loopcond; condition(ast->expression, loopbody, loopend); @@ -2334,7 +2355,7 @@ bool Codegen::visit(ForEachStatement *ast) return false; move(*init, _block->TEMP(temp)); statement(ast->statement); - _block->JUMP(foreachin); + setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken); _block = foreachin; @@ -2373,7 +2394,7 @@ bool Codegen::visit(ForStatement *ast) _block = forbody; statement(ast->statement); - _block->JUMP(forstep); + setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken); _block = forstep; statement(ast->expression); @@ -2399,12 +2420,12 @@ bool Codegen::visit(IfStatement *ast) _block = iftrue; statement(ast->ok); - _block->JUMP(endif); + setJumpOutLocation(_block->JUMP(endif), ast->ok, ast->ifToken); if (ast->ko) { _block = iffalse; statement(ast->ko); - _block->JUMP(endif); + setJumpOutLocation(_block->JUMP(endif), ast->ko, ast->elseToken); } _block = endif; @@ -2473,7 +2494,7 @@ bool Codegen::visit(LocalForEachStatement *ast) int temp = _block->newTemp(); move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); statement(ast->statement); - _block->JUMP(foreachin); + setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken); _block = foreachin; @@ -2512,7 +2533,7 @@ bool Codegen::visit(LocalForStatement *ast) _block = forbody; statement(ast->statement); - _block->JUMP(forstep); + setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken); _block = forstep; statement(ast->expression); @@ -2813,7 +2834,7 @@ bool Codegen::visit(WhileStatement *ast) _block = whilebody; statement(ast->statement); - _block->JUMP(whilecond); + setJumpOutLocation(_block->JUMP(whilecond), ast->statement, ast->whileToken); _block = whileend; leaveLoop(); diff --git a/src/qml/compiler/qv4compilationunitmapper.cpp b/src/qml/compiler/qv4compilationunitmapper.cpp index 2e1213464c..d94f7ac238 100644 --- a/src/qml/compiler/qv4compilationunitmapper.cpp +++ b/src/qml/compiler/qv4compilationunitmapper.cpp @@ -59,7 +59,7 @@ CompilationUnitMapper::~CompilationUnitMapper() close(); } -bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const QString &sourcePath, QString *errorString) +bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, QDateTime sourceTimeStamp, QString *errorString) { if (strncmp(header->magic, CompiledData::magic_str, sizeof(header->magic))) { *errorString = QStringLiteral("Magic bytes in the header do not match"); @@ -76,12 +76,7 @@ bool CompilationUnitMapper::verifyHeader(const CompiledData::Unit *header, const return false; } - { - QFileInfo sourceCode(sourcePath); - QDateTime sourceTimeStamp; - if (sourceCode.exists()) - sourceTimeStamp = sourceCode.lastModified(); - + if (header->sourceTimeStamp) { // Files from the resource system do not have any time stamps, so fall back to the application // executable. if (!sourceTimeStamp.isValid()) diff --git a/src/qml/compiler/qv4compilationunitmapper_p.h b/src/qml/compiler/qv4compilationunitmapper_p.h index 5b6939f1cf..b24f98df7c 100644 --- a/src/qml/compiler/qv4compilationunitmapper_p.h +++ b/src/qml/compiler/qv4compilationunitmapper_p.h @@ -68,11 +68,11 @@ public: CompilationUnitMapper(); ~CompilationUnitMapper(); - CompiledData::Unit *open(const QString &cacheFilePath, const QString &sourcePath, QString *errorString); + CompiledData::Unit *open(const QString &cacheFilePath, const QDateTime &sourceTimeStamp, QString *errorString); void close(); private: - static bool verifyHeader(const QV4::CompiledData::Unit *header, const QString &sourcePath, QString *errorString); + static bool verifyHeader(const QV4::CompiledData::Unit *header, QDateTime sourceTimeStamp, QString *errorString); #if defined(Q_OS_UNIX) size_t length; diff --git a/src/qml/compiler/qv4compilationunitmapper_unix.cpp b/src/qml/compiler/qv4compilationunitmapper_unix.cpp index 1aa3e05f5f..38dabc41cf 100644 --- a/src/qml/compiler/qv4compilationunitmapper_unix.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_unix.cpp @@ -43,6 +43,7 @@ #include <functional> #include <private/qcore_unix_p.h> #include <private/qdeferredcleanup_p.h> +#include <QDateTime> #include "qv4compileddata_p.h" @@ -50,7 +51,7 @@ QT_BEGIN_NAMESPACE using namespace QV4; -CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString) +CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QDateTime &sourceTimeStamp, QString *errorString) { close(); @@ -72,7 +73,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co return nullptr; } - if (!verifyHeader(&header, sourcePath, errorString)) + if (!verifyHeader(&header, sourceTimeStamp, errorString)) return nullptr; // Data structure and qt version matched, so now we can access the rest of the file safely. diff --git a/src/qml/compiler/qv4compilationunitmapper_win.cpp b/src/qml/compiler/qv4compilationunitmapper_win.cpp index 37cac846a0..d7a93ae233 100644 --- a/src/qml/compiler/qv4compilationunitmapper_win.cpp +++ b/src/qml/compiler/qv4compilationunitmapper_win.cpp @@ -49,7 +49,7 @@ QT_BEGIN_NAMESPACE using namespace QV4; -CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QString &sourcePath, QString *errorString) +CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, const QDateTime &sourceTimeStamp, QString *errorString) { close(); @@ -87,7 +87,7 @@ CompiledData::Unit *CompilationUnitMapper::open(const QString &cacheFileName, co return nullptr; } - if (!verifyHeader(&header, sourcePath, errorString)) + if (!verifyHeader(&header, sourceTimeStamp, errorString)) return nullptr; const uint mappingFlags = header.flags & QV4::CompiledData::Unit::ContainsMachineCode diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index a8f065210b..9832e1c49b 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -77,13 +77,7 @@ namespace QV4 { namespace CompiledData { -#ifdef V4_BOOTSTRAP -static QString cacheFilePath(const QString &localSourcePath) -{ - const QString localCachePath = localSourcePath + QLatin1Char('c'); - return localCachePath; -} -#else +#if !defined(V4_BOOTSTRAP) static QString cacheFilePath(const QUrl &url) { const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); @@ -102,7 +96,6 @@ static QString cacheFilePath(const QUrl &url) CompilationUnit::CompilationUnit() : data(0) , engine(0) - , runtimeStrings(0) , runtimeLookups(0) , runtimeRegularExpressions(0) , runtimeClasses(0) @@ -147,7 +140,8 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) flags |= IR::RegExp::RegExp_IgnoreCase; if (re->flags & CompiledData::RegExp::RegExp_Multiline) flags |= IR::RegExp::RegExp_Multiline; - runtimeRegularExpressions[i] = engine->newRegExpObject(data->stringAt(re->stringIndex), flags); + QV4::Heap::RegExpObject *ro = engine->newRegExpObject(data->stringAt(re->stringIndex), flags); + runtimeRegularExpressions[i] = ro; } if (data->lookupTableSize) { @@ -174,8 +168,6 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) l->level = -1; l->index = UINT_MAX; l->nameIndex = compiledLookups[i].nameIndex; - if (type == CompiledData::Lookup::Type_IndexedGetter || type == CompiledData::Lookup::Type_IndexedSetter) - l->engine = engine; } } @@ -249,14 +241,14 @@ void CompilationUnit::unlink() #endif } -void CompilationUnit::markObjects(QV4::ExecutionEngine *e) +void CompilationUnit::markObjects(QV4::MarkStack *markStack) { for (uint i = 0; i < data->stringTableSize; ++i) if (runtimeStrings[i]) - runtimeStrings[i]->mark(e); + runtimeStrings[i]->mark(markStack); if (runtimeRegularExpressions) { for (uint i = 0; i < data->regexpTableSize; ++i) - runtimeRegularExpressions[i].mark(e); + runtimeRegularExpressions[i].mark(markStack); } } @@ -331,10 +323,9 @@ void CompilationUnit::finalize(QQmlEnginePrivate *engine) totalObjectCount = objectCount; } -bool CompilationUnit::verifyChecksum(QQmlEngine *engine, - const ResolvedTypeReferenceMap &dependentTypes) const +bool CompilationUnit::verifyChecksum(const DependentTypesHasher &dependencyHasher) const { - if (dependentTypes.isEmpty()) { + if (!dependencyHasher) { for (size_t i = 0; i < sizeof(data->dependencyMD5Checksum); ++i) { if (data->dependencyMD5Checksum[i] != 0) return false; @@ -342,7 +333,7 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine, return true; } QCryptographicHash hash(QCryptographicHash::Md5); - if (!dependentTypes.addToHash(&hash, engine)) + if (!dependencyHasher(&hash)) return false; QByteArray checksum = hash.result(); Q_ASSERT(checksum.size() == sizeof(data->dependencyMD5Checksum)); @@ -350,7 +341,7 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine, sizeof(data->dependencyMD5Checksum)) == 0; } -bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) +bool CompilationUnit::loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString) { if (!QQmlFile::isLocalFile(url)) { *errorString = QStringLiteral("File has to be a local file."); @@ -360,14 +351,14 @@ bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); - CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString); + CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourceTimeStamp, errorString); if (!mappedUnit) return false; const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); - if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { + if (data->sourceFileIndex != 0 && sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { *errorString = QStringLiteral("QML source file has moved to a different location."); return false; } @@ -408,28 +399,29 @@ bool CompilationUnit::memoryMapCode(QString *errorString) #endif // V4_BOOTSTRAP #if defined(V4_BOOTSTRAP) -bool CompilationUnit::saveToDisk(const QString &unitUrl, QString *errorString) +bool CompilationUnit::saveToDisk(const QString &outputFileName, QString *errorString) #else bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) #endif { errorString->clear(); +#if !defined(V4_BOOTSTRAP) if (data->sourceTimeStamp == 0) { *errorString = QStringLiteral("Missing time stamp for source file"); return false; } -#if !defined(V4_BOOTSTRAP) if (!QQmlFile::isLocalFile(unitUrl)) { *errorString = QStringLiteral("File has to be a local file."); return false; } + const QString outputFileName = cacheFilePath(unitUrl); #endif #if QT_CONFIG(temporaryfile) // Foo.qml -> Foo.qmlc - QSaveFile cacheFile(cacheFilePath(unitUrl)); + QSaveFile cacheFile(outputFileName); if (!cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) { *errorString = cacheFile.errorString(); return false; @@ -461,6 +453,7 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return true; #else + Q_UNUSED(outputFileName) *errorString = QStringLiteral("features.temporaryfile is disabled."); return false; #endif // QT_CONFIG(temporaryfile) @@ -485,10 +478,22 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = irDocument->javaScriptCompilationUnit; - QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(irDocument->javaScriptCompilationUnit->data); + QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(compilationUnit->data); + auto ensureWritableUnit = [&jsUnit, &compilationUnit]() { + if (jsUnit == compilationUnit->data) { + char *unitCopy = (char*)malloc(jsUnit->unitSize); + memcpy(unitCopy, jsUnit, jsUnit->unitSize); + jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy); + } + }; QV4::Compiler::StringTableGenerator &stringTable = irDocument->jsGenerator.stringTable; + if (jsUnit->sourceFileIndex == quint32(0) || jsUnit->stringAt(jsUnit->sourceFileIndex) != irDocument->jsModule.fileName) { + ensureWritableUnit(); + jsUnit->sourceFileIndex = stringTable.registerString(irDocument->jsModule.fileName); + } + // Collect signals that have had a change in signature (from onClicked to onClicked(mouse) for example) // and now need fixing in the QV4::CompiledData. Also register strings at the same time, to finalize // the string table. @@ -551,6 +556,7 @@ Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) } if (!signalParameterNameTable.isEmpty()) { + ensureWritableUnit(); Q_ASSERT(jsUnit != compilationUnit->data); const uint signalParameterTableSize = signalParameterNameTable.count() * sizeof(quint32); uint newSize = jsUnit->unitSize + signalParameterTableSize; @@ -732,7 +738,7 @@ static QByteArray ownLibraryChecksum() if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) { QFile library(QFile::decodeName(libInfo.dli_fname)); if (library.open(QIODevice::ReadOnly)) { - QCryptographicHash hash(QCryptographicHash::Sha1); + QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(&library); libraryChecksum = hash.result(); } @@ -769,7 +775,7 @@ void Unit::generateChecksum() #ifndef V4_BOOTSTRAP QCryptographicHash hash(QCryptographicHash::Md5); - const int checksummableDataOffset = qOffsetOf(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum); + const int checksummableDataOffset = offsetof(QV4::CompiledData::Unit, md5Checksum) + sizeof(md5Checksum); const char *dataPtr = reinterpret_cast<const char *>(this) + checksummableDataOffset; hash.addData(dataPtr, unitSize - checksummableDataOffset); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 13a0c4b075..f4ba257cf5 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x09 +#define QV4_DATA_STRUCTURE_VERSION 0x11 class QIODevice; class QQmlPropertyCache; @@ -211,7 +211,8 @@ struct Function HasDirectEval = 0x2, UsesArgumentsObject = 0x4, IsNamedExpression = 0x8, - HasCatchOrWith = 0x10 + HasCatchOrWith = 0x10, + CanUseSimpleCall = 0x20 }; // Absolute offset into file where the code for this function is located. Only used when the function @@ -786,8 +787,10 @@ struct ResolvedTypeReferenceMap: public QMap<int, ResolvedTypeReference*> { bool addToHash(QCryptographicHash *hash, QQmlEngine *engine) const; }; + +using DependentTypesHasher = std::function<bool(QCryptographicHash *)>; #else -struct ResolvedTypeReferenceMap {}; +struct DependentTypesHasher {}; #endif // index is per-object binding index @@ -795,11 +798,15 @@ typedef QVector<QQmlPropertyData*> BindingPropertyData; // This is how this hooks into the existing structures: -//VM::Function -// CompilationUnit * (for functions that need to clean up) -// CompiledData::Function *compiledFunction +struct Q_QML_PRIVATE_EXPORT CompilationUnitBase +{ + QV4::Heap::String **runtimeStrings = 0; // Array +}; + +Q_STATIC_ASSERT(std::is_standard_layout<CompilationUnitBase>::value); +Q_STATIC_ASSERT(offsetof(CompilationUnitBase, runtimeStrings) == 0); -struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount +struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public QQmlRefCount { #ifdef V4_BOOTSTRAP CompilationUnit() @@ -818,11 +825,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount #ifndef V4_BOOTSTRAP ExecutionEngine *engine; -#endif - - QV4::Heap::String **runtimeStrings; // Array -#ifndef V4_BOOTSTRAP QString fileName() const { return data->stringAt(data->sourceFileIndex); } QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } @@ -860,8 +863,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QVector<QQmlScriptData *> dependentScripts; ResolvedTypeReferenceMap resolvedTypes; - bool verifyChecksum(QQmlEngine *engine, - const ResolvedTypeReferenceMap &dependentTypes) const; + bool verifyChecksum(const DependentTypesHasher &dependencyHasher) const; int metaTypeId; int listMetaTypeId; @@ -895,11 +897,11 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); void unlink(); - void markObjects(QV4::ExecutionEngine *e); + void markObjects(MarkStack *markStack); void destroy() Q_DECL_OVERRIDE; - bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString); + bool loadFromDisk(const QUrl &url, const QDateTime &sourceTimeStamp, EvalISelFactory *iselFactory, QString *errorString); protected: virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; @@ -908,7 +910,7 @@ protected: public: #if defined(V4_BOOTSTRAP) - bool saveToDisk(const QString &unitUrl, QString *errorString); + bool saveToDisk(const QString &outputFileName, QString *errorString); #else bool saveToDisk(const QUrl &unitUrl, QString *errorString); #endif diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 9cfac4a676..f7e63437e1 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -296,6 +296,8 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::IR::Function *i function->flags |= CompiledData::Function::IsNamedExpression; if (irFunction->hasTry || irFunction->hasWith) function->flags |= CompiledData::Function::HasCatchOrWith; + if (irFunction->canUseSimpleCall()) + function->flags |= CompiledData::Function::CanUseSimpleCall; function->nFormals = irFunction->formals.size(); function->formalsOffset = currentOffset; currentOffset += function->nFormals * sizeof(quint32); @@ -374,7 +376,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); - unit.architectureIndex = registerString(QSysInfo::buildAbi()); + unit.architectureIndex = registerString(irModule->targetABI.isEmpty() ? QSysInfo::buildAbi() : irModule->targetABI); unit.codeGeneratorIndex = registerString(codeGeneratorName); memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); @@ -425,7 +427,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp } unit.indexOfRootFunction = -1; unit.sourceFileIndex = getStringId(irModule->fileName); - unit.sourceTimeStamp = irModule->sourceTimeStamp; + unit.sourceTimeStamp = irModule->sourceTimeStamp.isValid() ? irModule->sourceTimeStamp.toMSecsSinceEpoch() : 0; unit.nImports = 0; unit.offsetToImports = 0; unit.nObjects = 0; diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 53d9956315..fbd6ac8f99 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -690,7 +690,7 @@ union Instr }; struct instr_binop { MOTH_INSTR_HEADER - uint alu; // offset inside the runtime methods + int alu; // QV4::Runtime::RuntimeMethods enum value Param lhs; Param rhs; Param result; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 04844302d9..aefb084971 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -55,70 +55,70 @@ using namespace QV4::Moth; namespace { -inline uint aluOpFunction(IR::AluOp op) +inline QV4::Runtime::RuntimeMethods aluOpFunction(IR::AluOp op) { switch (op) { case IR::OpInvalid: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; case IR::OpIfTrue: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; case IR::OpNot: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; case IR::OpUMinus: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; case IR::OpUPlus: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; case IR::OpCompl: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; case IR::OpBitAnd: - return offsetof(QV4::Runtime, bitAnd); + return QV4::Runtime::bitAnd; case IR::OpBitOr: - return offsetof(QV4::Runtime, bitOr); + return QV4::Runtime::bitOr; case IR::OpBitXor: - return offsetof(QV4::Runtime, bitXor); + return QV4::Runtime::bitXor; case IR::OpAdd: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; case IR::OpSub: - return offsetof(QV4::Runtime, sub); + return QV4::Runtime::sub; case IR::OpMul: - return offsetof(QV4::Runtime, mul); + return QV4::Runtime::mul; case IR::OpDiv: - return offsetof(QV4::Runtime, div); + return QV4::Runtime::div; case IR::OpMod: - return offsetof(QV4::Runtime, mod); + return QV4::Runtime::mod; case IR::OpLShift: - return offsetof(QV4::Runtime, shl); + return QV4::Runtime::shl; case IR::OpRShift: - return offsetof(QV4::Runtime, shr); + return QV4::Runtime::shr; case IR::OpURShift: - return offsetof(QV4::Runtime, ushr); + return QV4::Runtime::ushr; case IR::OpGt: - return offsetof(QV4::Runtime, greaterThan); + return QV4::Runtime::greaterThan; case IR::OpLt: - return offsetof(QV4::Runtime, lessThan); + return QV4::Runtime::lessThan; case IR::OpGe: - return offsetof(QV4::Runtime, greaterEqual); + return QV4::Runtime::greaterEqual; case IR::OpLe: - return offsetof(QV4::Runtime, lessEqual); + return QV4::Runtime::lessEqual; case IR::OpEqual: - return offsetof(QV4::Runtime, equal); + return QV4::Runtime::equal; case IR::OpNotEqual: - return offsetof(QV4::Runtime, notEqual); + return QV4::Runtime::notEqual; case IR::OpStrictEqual: - return offsetof(QV4::Runtime, strictEqual); + return QV4::Runtime::strictEqual; case IR::OpStrictNotEqual: - return offsetof(QV4::Runtime, strictNotEqual); + return QV4::Runtime::strictNotEqual; case IR::OpInstanceof: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; case IR::OpIn: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; case IR::OpAnd: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; case IR::OpOr: - return 0; + return QV4::Runtime::InvalidRuntimeMethod; default: Q_ASSERT(!"Unknown AluOp"); - return 0; + return QV4::Runtime::InvalidRuntimeMethod; } }; @@ -889,24 +889,25 @@ Param InstructionSelection::binopHelper(IR::AluOp oper, IR::Expr *leftSource, IR if (oper == IR::OpInstanceof || oper == IR::OpIn || oper == IR::OpAdd) { Instruction::BinopContext binop; if (oper == IR::OpInstanceof) - binop.alu = offsetof(QV4::Runtime, instanceof); + binop.alu = QV4::Runtime::instanceof; else if (oper == IR::OpIn) - binop.alu = offsetof(QV4::Runtime, in); + binop.alu = QV4::Runtime::in; else - binop.alu = offsetof(QV4::Runtime, add); + binop.alu = QV4::Runtime::add; binop.lhs = getParam(leftSource); binop.rhs = getParam(rightSource); binop.result = getResultParam(target); - Q_ASSERT(binop.alu); + Q_ASSERT(binop.alu != QV4::Runtime::InvalidRuntimeMethod); addInstruction(binop); return binop.result; } else { + auto binopFunc = aluOpFunction(oper); + Q_ASSERT(binopFunc != QV4::Runtime::InvalidRuntimeMethod); Instruction::Binop binop; - binop.alu = aluOpFunction(oper); + binop.alu = binopFunc; binop.lhs = getParam(leftSource); binop.rhs = getParam(rightSource); binop.result = getResultParam(target); - Q_ASSERT(binop.alu); addInstruction(binop); return binop.result; } diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index 41469f1985..4b84bd2831 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -179,7 +179,7 @@ private: int scratchTempIndex() const { return _function->tempCount; } int callDataStart() const { return scratchTempIndex() + 1; } - int outgoingArgumentTempStart() const { return callDataStart() + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value); } + int outgoingArgumentTempStart() const { return callDataStart() + offsetof(QV4::CallData, args)/sizeof(QV4::Value); } int frameSize() const { return outgoingArgumentTempStart() + _function->maxNumberOfArguments; } template <int Instr> diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h index 1755193d32..e949e6f0ad 100644 --- a/src/qml/compiler/qv4isel_util_p.h +++ b/src/qml/compiler/qv4isel_util_p.h @@ -58,6 +58,59 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct TargetPrimitive32 { + static TargetPrimitive32 emptyValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Empty) << 32; return p; } + static TargetPrimitive32 nullValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Null) << 32; return p; } + static TargetPrimitive32 undefinedValue() { TargetPrimitive32 p; p._val = quint64(Value::Managed_Type_Internal_32) << 32; return p; } + static TargetPrimitive32 fromBoolean(bool b) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Boolean) << 32 | quint64(b); return p; } + static TargetPrimitive32 fromInt32(int v) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Integer) << 32 | quint32(v); return p; } + static TargetPrimitive32 fromDouble(double v) { + TargetPrimitive32 p; + memcpy(&p._val, &v, 8); + return p; + } + static TargetPrimitive32 fromUInt32(uint v) { + if (v < INT_MAX) + return fromInt32(qint32(v)); + return fromDouble(double(v)); + } + + quint32 value() const { return _val & quint64(~quint32(0)); } + quint32 tag() const { return _val >> 32; } + + quint64 rawValue() const { return _val; } + +private: + quint64 _val; +}; + +struct TargetPrimitive64 { + static TargetPrimitive64 emptyValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Empty) << 32; return p; } + static TargetPrimitive64 nullValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Null) << 32; return p; } + static TargetPrimitive64 undefinedValue() { TargetPrimitive64 p; p._val = 0; return p; } + static TargetPrimitive64 fromBoolean(bool b) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Boolean) << 32 | quint64(b); return p; } + static TargetPrimitive64 fromInt32(int v) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Integer) << 32 | quint32(v); return p; } + static TargetPrimitive64 fromDouble(double v) { + TargetPrimitive64 p; + memcpy(&p._val, &v, 8); + p._val ^= Value::NaNEncodeMask; + return p; + } + static TargetPrimitive64 fromUInt32(uint v) { + if (v < INT_MAX) + return fromInt32(qint32(v)); + return fromDouble(double(v)); + } + + quint32 value() const { return _val & quint64(~quint32(0)); } + quint32 tag() const { return _val >> 32; } + + quint64 rawValue() const { return _val; } + +private: + quint64 _val; +}; + inline bool canConvertToSignedInteger(double value) { int ival = (int) value; @@ -72,36 +125,37 @@ inline bool canConvertToUnsignedInteger(double value) return uval == value && !(value == 0 && isNegative(value)); } -inline Primitive convertToValue(IR::Const *c) +template <typename PrimitiveType = Primitive> +inline PrimitiveType convertToValue(IR::Const *c) { switch (c->type) { case IR::MissingType: - return Primitive::emptyValue(); + return PrimitiveType::emptyValue(); case IR::NullType: - return Primitive::nullValue(); + return PrimitiveType::nullValue(); case IR::UndefinedType: - return Primitive::undefinedValue(); + return PrimitiveType::undefinedValue(); case IR::BoolType: - return Primitive::fromBoolean(c->value != 0); + return PrimitiveType::fromBoolean(c->value != 0); case IR::SInt32Type: - return Primitive::fromInt32(int(c->value)); + return PrimitiveType::fromInt32(int(c->value)); case IR::UInt32Type: - return Primitive::fromUInt32(unsigned(c->value)); + return PrimitiveType::fromUInt32(unsigned(c->value)); case IR::DoubleType: - return Primitive::fromDouble(c->value); + return PrimitiveType::fromDouble(c->value); case IR::NumberType: { int ival = (int)c->value; if (canConvertToSignedInteger(c->value)) { - return Primitive::fromInt32(ival); + return PrimitiveType::fromInt32(ival); } else { - return Primitive::fromDouble(c->value); + return PrimitiveType::fromDouble(c->value); } } default: Q_UNREACHABLE(); } // unreachable, but the function must return something - return Primitive::undefinedValue(); + return PrimitiveType::undefinedValue(); } class ConvertTemps diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 5687834b00..cc2f9b7cf2 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -348,11 +348,7 @@ Module::~Module() void Module::setFileName(const QString &name) { - if (fileName.isEmpty()) - fileName = name; - else { - Q_ASSERT(fileName == name); - } + fileName = name; } Function::Function(Module *module, Function *outer, const QString &name) diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 23ebe0c962..35cf0fc174 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -61,6 +61,7 @@ #include <QtCore/QBitArray> #include <QtCore/qurl.h> #include <QtCore/QVarLengthArray> +#include <QtCore/QDateTime> #include <qglobal.h> #if defined(CONST) && defined(Q_OS_WIN) @@ -942,9 +943,10 @@ struct Q_QML_PRIVATE_EXPORT Module { QVector<Function *> functions; Function *rootFunction; QString fileName; - qint64 sourceTimeStamp; + QDateTime sourceTimeStamp; bool isQmlModule; // implies rootFunction is always 0 uint unitFlags; // flags merged into CompiledData::Unit::flags + QString targetABI; // fallback to QSysInfo::buildAbi() if empty #ifdef QT_NO_QML_DEBUGGER static const bool debugMode = false; #else @@ -955,7 +957,6 @@ struct Q_QML_PRIVATE_EXPORT Module { Module(bool debugMode) : rootFunction(0) - , sourceTimeStamp(0) , isQmlModule(false) , unitFlags(0) #ifndef QT_NO_QML_DEBUGGER @@ -1352,6 +1353,31 @@ struct Function { int getNewStatementId() { return _statementCount++; } int statementCount() const { return _statementCount; } + bool canUseSimpleCall() const { + return nestedFunctions.isEmpty() && + locals.isEmpty() && formals.size() <= QV4::Global::ReservedArgumentCount && + !hasTry && !hasWith && !isNamedExpression && !usesArgumentsObject && !hasDirectEval; + } + + bool argLocalRequiresWriteBarrier(ArgLocal *al) const { + uint scope = al->scope; + const IR::Function *f = this; + while (scope) { + f = f->outer; + --scope; + } + return !f->canUseSimpleCall(); + } + int localsCountForScope(ArgLocal *al) const { + uint scope = al->scope; + const IR::Function *f = this; + while (scope) { + f = f->outer; + --scope; + } + return f->locals.size(); + } + private: BasicBlock *getOrCreateBasicBlock(int index); void setStatementCount(int cnt); @@ -1420,6 +1446,7 @@ public: ArgLocal *newArgLocal = f->New<ArgLocal>(); newArgLocal->init(argLocal->kind, argLocal->index, argLocal->scope); newArgLocal->type = argLocal->type; + newArgLocal->isArgumentsOrEval = argLocal->isArgumentsOrEval; return newArgLocal; } diff --git a/src/qml/compiler/qv4jssimplifier.cpp b/src/qml/compiler/qv4jssimplifier.cpp new file mode 100644 index 0000000000..7d09218fe6 --- /dev/null +++ b/src/qml/compiler/qv4jssimplifier.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 "qv4jssimplifier_p.h" + +QT_BEGIN_NAMESPACE + +QQmlJavaScriptBindingExpressionSimplificationPass::QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator) + : qmlObjects(qmlObjects) + , jsModule(jsModule) + , unitGenerator(unitGenerator) +{ + +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings() +{ + for (int i = 0; i < qmlObjects.count(); ++i) + reduceTranslationBindings(i); + if (!irFunctionsToRemove.isEmpty()) { + QQmlIRFunctionCleanser cleanser(jsModule, qmlObjects, irFunctionsToRemove); + cleanser.clean(); + } +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings(int objectIndex) +{ + const QmlIR::Object *obj = qmlObjects.at(objectIndex); + + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + + const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex); + QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex); + if (simplifyBinding(irFunction, binding)) { + irFunctionsToRemove.append(irFunctionIndex); + jsModule->functions[irFunctionIndex] = 0; + delete irFunction; + } + } +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move *move) +{ + QV4::IR::Temp *target = move->target->asTemp(); + if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { + discard(); + return; + } + + if (QV4::IR::Call *call = move->source->asCall()) { + if (QV4::IR::Name *n = call->base->asName()) { + if (n->builtin == QV4::IR::Name::builtin_invalid) { + visitFunctionCall(n->id, call->args, target); + return; + } + } + discard(); + return; + } + + if (QV4::IR::Name *n = move->source->asName()) { + if (n->builtin == QV4::IR::Name::builtin_qml_context + || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) { + // these are free of side-effects + return; + } + discard(); + return; + } + + if (!move->source->asTemp() && !move->source->asString() && !move->source->asConst()) { + discard(); + return; + } + + _temps[target->index] = move->source; +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target) +{ + // more than one function call? + if (_nameOfFunctionCalled) { + discard(); + return; + } + + _nameOfFunctionCalled = name; + + _functionParameters.clear(); + while (args) { + int slot; + if (QV4::IR::Temp *param = args->expr->asTemp()) { + if (param->kind != QV4::IR::Temp::VirtualRegister) { + discard(); + return; + } + slot = param->index; + _functionParameters.append(slot); + } else if (QV4::IR::Const *param = args->expr->asConst()) { + slot = --_synthesizedConsts; + Q_ASSERT(!_temps.contains(slot)); + _temps[slot] = param; + _functionParameters.append(slot); + } + args = args->next; + } + + _functionCallReturnValue = target->index; +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::visitRet(QV4::IR::Ret *ret) +{ + // nothing initialized earlier? + if (_returnValueOfBindingExpression != -1) { + discard(); + return; + } + QV4::IR::Temp *target = ret->expr->asTemp(); + if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { + discard(); + return; + } + _returnValueOfBindingExpression = target->index; +} + +bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding) +{ + _canSimplify = true; + _nameOfFunctionCalled = 0; + _functionParameters.clear(); + _functionCallReturnValue = -1; + _temps.clear(); + _returnValueOfBindingExpression = -1; + _synthesizedConsts = 0; + + // It would seem unlikely that function with some many basic blocks (after optimization) + // consists merely of a qsTr call or a constant value return ;-) + if (function->basicBlockCount() > 10) + return false; + + for (QV4::IR::BasicBlock *bb : function->basicBlocks()) { + for (QV4::IR::Stmt *s : bb->statements()) { + visit(s); + if (!_canSimplify) + return false; + } + } + + if (_returnValueOfBindingExpression == -1) + return false; + + if (_nameOfFunctionCalled) { + if (_functionCallReturnValue != _returnValueOfBindingExpression) + return false; + return detectTranslationCallAndConvertBinding(binding); + } + + return false; +} + +bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding) +{ + if (*_nameOfFunctionCalled == QLatin1String("qsTr")) { + QString translation; + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string + + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + translation = *stringParam->value; + + ++param; + if (param != end) { + stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + translationData.commentIndex = unitGenerator->registerString(*stringParam->value); + ++param; + + if (param != end) { + QV4::IR::Const *constParam = _temps[*param]->asConst(); + if (!constParam || constParam->type != QV4::IR::SInt32Type) + return false; + + translationData.number = int(constParam->value); + ++param; + } + } + + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_Translation; + binding->stringIndex = unitGenerator->registerString(translation); + binding->value.translationData = translationData; + return true; + } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) { + QString id; + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string, but unused + + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + id = *stringParam->value; + + ++param; + if (param != end) { + QV4::IR::Const *constParam = _temps[*param]->asConst(); + if (!constParam || constParam->type != QV4::IR::SInt32Type) + return false; + + translationData.number = int(constParam->value); + ++param; + } + + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_TranslationById; + binding->stringIndex = unitGenerator->registerString(id); + binding->value.translationData = translationData; + return true; + } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) { + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + ++param; + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = unitGenerator->registerString(*stringParam->value); + return true; + } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) { + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + ++param; + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + ++param; + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = unitGenerator->registerString(*stringParam->value); + return true; + } + return false; +} + +QQmlIRFunctionCleanser::QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object *> &qmlObjects, const QVector<int> &functionsToRemove) + : module(module) + , qmlObjects(qmlObjects) + , functionsToRemove(functionsToRemove) +{ +} + +void QQmlIRFunctionCleanser::clean() +{ + QVector<QV4::IR::Function*> newFunctions; + newFunctions.reserve(module->functions.count() - functionsToRemove.count()); + + newFunctionIndices.resize(module->functions.count()); + + for (int i = 0; i < module->functions.count(); ++i) { + QV4::IR::Function *f = module->functions.at(i); + Q_ASSERT(f || functionsToRemove.contains(i)); + if (f) { + newFunctionIndices[i] = newFunctions.count(); + newFunctions << f; + } + } + + module->functions = newFunctions; + + for (QV4::IR::Function *function : qAsConst(module->functions)) { + for (QV4::IR::BasicBlock *block : function->basicBlocks()) { + for (QV4::IR::Stmt *s : block->statements()) { + visit(s); + } + } + } + + for (QmlIR::Object *obj : qmlObjects) { + for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i) + obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)]; + } +} + +void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s) +{ + + switch (s->stmtKind) { + case QV4::IR::Stmt::PhiStmt: + // nothing to do + break; + default: + STMT_VISIT_ALL_KINDS(s); + break; + } +} + +void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e) +{ + switch (e->exprKind) { + case QV4::IR::Expr::ClosureExpr: { + auto closure = e->asClosure(); + closure->value = newFunctionIndices.at(closure->value); + } break; + default: + EXPR_VISIT_ALL_KINDS(e); + break; + } +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4jssimplifier_p.h b/src/qml/compiler/qv4jssimplifier_p.h new file mode 100644 index 0000000000..ae8d74135c --- /dev/null +++ b/src/qml/compiler/qv4jssimplifier_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 QV4JSSIMPLIFIER +#define QV4JSSIMPLIFIER + +// +// 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 "qqmlirbuilder_p.h" + +QT_BEGIN_NAMESPACE + +namespace QmlIR { +struct Document; +} + +namespace QV4 { +namespace CompiledData { +struct QmlUnit; +struct Location; +} +} + +class QQmlJavaScriptBindingExpressionSimplificationPass +{ +public: + QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator); + + void reduceTranslationBindings(); + +private: + void reduceTranslationBindings(int objectIndex); + + void visit(QV4::IR::Stmt *s) + { + switch (s->stmtKind) { + case QV4::IR::Stmt::MoveStmt: + visitMove(s->asMove()); + break; + case QV4::IR::Stmt::RetStmt: + visitRet(s->asRet()); + break; + case QV4::IR::Stmt::CJumpStmt: + discard(); + break; + case QV4::IR::Stmt::ExpStmt: + discard(); + break; + case QV4::IR::Stmt::JumpStmt: + break; + case QV4::IR::Stmt::PhiStmt: + break; + } + } + + void visitMove(QV4::IR::Move *move); + void visitRet(QV4::IR::Ret *ret); + + void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target); + + void discard() { _canSimplify = false; } + + bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding); + bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding); + + const QVector<QmlIR::Object*> &qmlObjects; + QV4::IR::Module *jsModule; + QV4::Compiler::JSUnitGenerator *unitGenerator; + + bool _canSimplify; + const QString *_nameOfFunctionCalled; + QVector<int> _functionParameters; + int _functionCallReturnValue; + + QHash<int, QV4::IR::Expr*> _temps; + int _returnValueOfBindingExpression; + int _synthesizedConsts; + + QVector<int> irFunctionsToRemove; +}; + +class QQmlIRFunctionCleanser +{ +public: + QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object*> &qmlObjects, const QVector<int> &functionsToRemove); + + void clean(); + +private: + virtual void visitMove(QV4::IR::Move *s) { + visit(s->source); + visit(s->target); + } + + void visit(QV4::IR::Stmt *s); + void visit(QV4::IR::Expr *e); + +private: + QV4::IR::Module *module; + const QVector<QmlIR::Object*> &qmlObjects; + const QVector<int> &functionsToRemove; + + QVector<int> newFunctionIndices; +}; + +QT_END_NAMESPACE + +#endif // QV4JSSIMPLIFIER diff --git a/src/qml/configure.json b/src/qml/configure.json index d22ba3b8f0..2c4887365f 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -22,6 +22,19 @@ "label": "QML network support", "purpose": "Provides network transparency for QML", "output": [ "publicFeature" ] + }, + "qml-profiler": { + "label": "Command line QML Profiler", + "purpose": "The QML Profiler retrieves QML tracing data from an application.", + "condition": [ + "features.commandlineparser", + "features.localserver", + "features.process", + "features.qml-debug", + "features.qml-network", + "features.xmlstreamwriter" + ], + "output": [ "privateFeature" ] } }, diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp index 15230d75a5..681dc06215 100644 --- a/src/qml/debugger/qqmldebug.cpp +++ b/src/qml/debugger/qqmldebug.cpp @@ -42,6 +42,7 @@ #include "qqmldebugserviceinterfaces_p.h" #include <private/qqmlengine_p.h> +#include <private/qv4compileddata_p.h> QT_BEGIN_NAMESPACE @@ -181,12 +182,12 @@ bool QQmlDebuggingEnabler::startDebugConnector(const QString &pluginName, return connector ? connector->open(configuration) : false; } -enum { HookCount = 3 }; +enum { HookCount = 4 }; // Only add to the end, and bump version if you do. quintptr Q_QML_EXPORT qtDeclarativeHookData[] = { // Version of this Array. Bump if you add to end. - 1, + 2, // Number of entries in this array. HookCount, @@ -194,7 +195,10 @@ quintptr Q_QML_EXPORT qtDeclarativeHookData[] = { // TypeInformationVersion, an integral value, bumped whenever private // object sizes or member offsets that are used in Qt Creator's // data structure "pretty printing" change. - 2 + 3, + + // Version of the cache data. + QV4_DATA_STRUCTURE_VERSION }; Q_STATIC_ASSERT(HookCount == sizeof(qtDeclarativeHookData) / sizeof(qtDeclarativeHookData[0])); diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index 7d4a543089..32084bd308 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -687,7 +687,7 @@ class MessageBoard : public QObject Q_PROPERTY(QQmlListProperty<Message> messages READ messages) Q_CLASSINFO("DefaultProperty", "messages") public: - QQmlListProperty<Message> messages() const; + QQmlListProperty<Message> messages(); private: QList<Message *> messages; diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc index 3bffd2eb6f..c4c58c2821 100644 --- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc +++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc @@ -267,7 +267,7 @@ class MessageBoard : public QObject Q_OBJECT Q_PROPERTY(QQmlListProperty<Message> messages READ messages) public: - QQmlListProperty<Message> messages() const; + QQmlListProperty<Message> messages(); private: static void append_message(QQmlListProperty<Message> *list, Message *msg); diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index 1e33f2f641..7e9a22f5d3 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -74,6 +74,19 @@ Note that QML makes the following modifications to native objects: \li Locale-aware conversion functions are added to the \l Date and \l Number prototypes. \endlist +In addition, QML also extends the behavior of the instanceof function to +allow for type checking against QML types. This means that you may use it to +verify that a variable is indeed the type you expect, for example: + +\qml + var v = something(); + if (!v instanceof Item) { + throw new TypeError("I need an Item type!"); + } + + ... +\endqml + \section1 JavaScript Environment Restrictions diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index e658977da1..d062f3bbb2 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -95,6 +95,12 @@ bool CompilationUnit::memoryMapCode(QString *errorString) JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr)); JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize); codeRefs[i] = codeRef; + + static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM"); + if (showCode) { + WTF::dataLogF("Mapped JIT code for %s\n", qPrintable(stringAt(compiledFunction->nameIndex))); + disassemble(codeRef.code(), compiledFunction->codeSize, " ", WTF::dataFile()); + } } return true; @@ -150,9 +156,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit } template <typename TargetConfiguration> -const typename Assembler<TargetConfiguration>::VoidType Assembler<TargetConfiguration>::Void; - -template <typename TargetConfiguration> Assembler<TargetConfiguration>::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) : _function(function) , _nextBlock(0) @@ -246,13 +249,16 @@ void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition } template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadAddress(RegisterID tmp, IR::Expr *e) +typename Assembler<TargetConfiguration>::Pointer +Assembler<TargetConfiguration>::loadAddressForWriting(RegisterID tmp, IR::Expr *e, WriteBarrier::Type *barrier) { + if (barrier) + *barrier = WriteBarrier::NoBarrier; IR::Temp *t = e->asTemp(); if (t) return loadTempAddress(t); else - return loadArgLocalAddress(tmp, e->asArgLocal()); + return loadArgLocalAddressForWriting(tmp, e->asArgLocal(), barrier); } template <typename TargetConfiguration> @@ -265,29 +271,42 @@ typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>: } template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al) +typename Assembler<TargetConfiguration>::Pointer +Assembler<TargetConfiguration>::loadArgLocalAddressForWriting(RegisterID baseReg, IR::ArgLocal *al, WriteBarrier::Type *barrier) { + if (barrier) + *barrier = _function->argLocalRequiresWriteBarrier(al) ? WriteBarrier::Barrier : WriteBarrier::NoBarrier; + int32_t offset = 0; int scope = al->scope; - loadPtr(Address(EngineRegister, qOffsetOf(ExecutionEngine, current)), baseReg); - if (scope) { - loadPtr(Address(baseReg, qOffsetOf(ExecutionContext::Data, outer)), baseReg); + loadPtr(Address(EngineRegister, targetStructureOffset(offsetof(EngineBase, current))), baseReg); + + const qint32 outerOffset = targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, outer)); + const qint32 localsOffset = targetStructureOffset(Heap::CallContextData::baseOffset + offsetof(Heap::CallContextData, function)) + + 8 // locals is always 8 bytes away from function, regardless of pointer size. + + offsetof(ValueArray<0>, values); + + while (scope) { + loadPtr(Address(baseReg, outerOffset), baseReg); --scope; - while (scope) { - loadPtr(Address(baseReg, qOffsetOf(ExecutionContext::Data, outer)), baseReg); - --scope; - } } switch (al->kind) { case IR::ArgLocal::Formal: case IR::ArgLocal::ScopedFormal: { - loadPtr(Address(baseReg, qOffsetOf(ExecutionContext::Data, callData)), baseReg); - offset = sizeof(CallData) + (al->index - 1) * sizeof(Value); + if (barrier && *barrier == WriteBarrier::Barrier) { + // if we need a barrier, the baseReg has to point to the ExecutionContext + // callData comes directly after locals, calculate the offset using that + offset = localsOffset + _function->localsCountForScope(al) * sizeof(Value); + offset += sizeof(CallData) + (al->index - 1) * sizeof(Value); + } else { + const qint32 callDataOffset = targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, callData)); + loadPtr(Address(baseReg, callDataOffset), baseReg); + offset = sizeof(CallData) + (al->index - 1) * sizeof(Value); + } } break; case IR::ArgLocal::Local: case IR::ArgLocal::ScopedLocal: { - loadPtr(Address(baseReg, qOffsetOf(CallContext::Data, locals)), baseReg); - offset = al->index * sizeof(Value); + offset = localsOffset + al->index * sizeof(Value); } break; default: Q_UNREACHABLE(); @@ -298,25 +317,25 @@ typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>: template <typename TargetConfiguration> typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadStringAddress(RegisterID reg, const QString &string) { - loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, compilationUnit)), Assembler::ScratchRegister); - loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(QV4::CompiledData::CompilationUnit, runtimeStrings)), reg); + loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), Assembler::ScratchRegister); + loadPtr(Address(Assembler::ScratchRegister, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, compilationUnit))), Assembler::ScratchRegister); + loadPtr(Address(Assembler::ScratchRegister, offsetof(CompiledData::CompilationUnitBase, runtimeStrings)), reg); const int id = _jsGenerator->registerString(string); - return Pointer(reg, id * sizeof(QV4::String*)); + return Pointer(reg, id * RegisterSize); } template <typename TargetConfiguration> typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(IR::Const *c, RegisterID baseReg) { - return loadConstant(convertToValue(c), baseReg); + return loadConstant(convertToValue<TargetPrimitive>(c), baseReg); } template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const Primitive &v, RegisterID baseReg) +typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const TargetPrimitive &v, RegisterID baseReg) { - loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), baseReg); - loadPtr(Address(baseReg, qOffsetOf(QV4::Heap::ExecutionContext, constantTable)), baseReg); - const int index = _jsGenerator->registerConstant(v.asReturnedValue()); + loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), baseReg); + loadPtr(Address(baseReg, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, constantTable))), baseReg); + const int index = _jsGenerator->registerConstant(v.rawValue()); return Address(baseReg, index * sizeof(QV4::Value)); } @@ -328,10 +347,11 @@ void Assembler<TargetConfiguration>::loadStringRef(RegisterID reg, const QString } template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::storeValue(QV4::Primitive value, IR::Expr *destination) +void Assembler<TargetConfiguration>::storeValue(TargetPrimitive value, IR::Expr *destination) { - Address addr = loadAddress(ScratchRegister, destination); - storeValue(value, addr); + WriteBarrier::Type barrier; + Address addr = loadAddressForWriting(ScratchRegister, destination, &barrier); + storeValue(value, addr, barrier); } template <typename TargetConfiguration> @@ -420,25 +440,19 @@ typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::ge // It's not a number type, so it cannot be in a register. Q_ASSERT(src->asArgLocal() || src->asTemp()->kind != IR::Temp::PhysicalRegister || src->type == IR::BoolType); - Assembler::Pointer tagAddr = loadAddress(Assembler::ScratchRegister, src); + Assembler::Pointer tagAddr = loadAddressForReading(Assembler::ScratchRegister, src); tagAddr.offset += 4; load32(tagAddr, Assembler::ScratchRegister); // check if it's an int32: Assembler::Jump isNoInt = branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); + Assembler::TrustedImm32(quint32(ValueTypeInternal::Integer))); convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), dest); Assembler::Jump intDone = jump(); // not an int, check if it's a double: isNoInt.link(this); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - rshift32(TrustedImm32(Value::IsDoubleTag_Shift), ScratchRegister); - Assembler::Jump isNoDbl = branch32(RelationalCondition::Equal, JITTargetPlatform::ScratchRegister, TrustedImm32(0)); -#else - and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister); - Assembler::Jump isNoDbl = branch32(RelationalCondition::Equal, JITTargetPlatform::ScratchRegister, TrustedImm32(Value::NotDouble_Mask)); -#endif + Assembler::Jump isNoDbl = RegisterSizeDependentOps::checkIfTagRegisterIsDouble(this, ScratchRegister); toDoubleRegister(src, dest); intDone.link(this); @@ -507,7 +521,7 @@ void Assembler<TargetConfiguration>::returnFromFunction(IR::Ret *s, RegisterInfo } else if (IR::Temp *t = s->expr->asTemp()) { RegisterSizeDependentOps::setFunctionReturnValueFromTemp(this, t); } else if (IR::Const *c = s->expr->asConst()) { - QV4::Primitive retVal = convertToValue(c); + auto retVal = convertToValue<TargetPrimitive>(c); RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); } else { Q_UNREACHABLE(); @@ -518,15 +532,13 @@ void Assembler<TargetConfiguration>::returnFromFunction(IR::Ret *s, RegisterInfo const int locals = stackLayout().calculateJSStackFrameSize(); subPtr(TrustedImm32(sizeof(QV4::Value)*locals), JITTargetPlatform::LocalsRegister); - loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister); - loadPtr(Address(JITTargetPlatform::ScratchRegister, qOffsetOf(ExecutionContext::Data, engine)), JITTargetPlatform::ScratchRegister); - storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop))); + storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop)))); leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave); ret(); exceptionReturnLabel = label(); - QV4::Primitive retVal = Primitive::undefinedValue(); + auto retVal = TargetPrimitive::undefinedValue(); RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); jump(leaveStackFrame); } diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index 720c522e1d..9e38696d7a 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -57,6 +57,7 @@ #include "private/qv4value_p.h" #include "private/qv4context_p.h" #include "private/qv4engine_p.h" +#include "private/qv4writebarrier_p.h" #include "qv4targetplatform_p.h" #include <config.h> @@ -131,7 +132,7 @@ typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingS #define isel_stringIfy(s) isel_stringIfyx(s) #define generateRuntimeCall(as, t, function, ...) \ - as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) + as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), typename JITAssembler::RuntimeCall(QV4::Runtime::function), __VA_ARGS__) template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform, int RegisterSize> @@ -152,35 +153,86 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo using TrustedImm64 = typename JITAssembler::TrustedImm64; using Jump = typename JITAssembler::Jump; using Label = typename JITAssembler::Label; + using ValueTypeInternal = Value::ValueTypeInternal_32; + using TargetPrimitive = TargetPrimitive32; + + static void emitSetGrayBit(JITAssembler *as, RegisterID base) + { + bool returnValueUsed = (base == TargetPlatform::ReturnValueRegister); + + as->push(TargetPlatform::EngineRegister); // free up one register for work + + RegisterID grayBitmap = returnValueUsed ? TargetPlatform::ScratchRegister : TargetPlatform::ReturnValueRegister; + as->move(base, grayBitmap); + Q_ASSERT(base != grayBitmap); + as->urshift32(TrustedImm32(Chunk::ChunkShift), grayBitmap); + as->lshift32(TrustedImm32(Chunk::ChunkShift), grayBitmap); + Q_STATIC_ASSERT(offsetof(Chunk, grayBitmap) == 0); + + RegisterID index = base; + as->move(base, index); + as->sub32(grayBitmap, index); + as->urshift32(TrustedImm32(Chunk::SlotSizeShift), index); + RegisterID grayIndex = TargetPlatform::EngineRegister; + as->move(index, grayIndex); + as->urshift32(TrustedImm32(Chunk::BitShift), grayIndex); + as->lshift32(TrustedImm32(2), grayIndex); // 4 bytes per quintptr + as->add32(grayIndex, grayBitmap); + as->and32(TrustedImm32(Chunk::Bits - 1), index); + + RegisterID bit = TargetPlatform::EngineRegister; + as->move(TrustedImm32(1), bit); + as->lshift32(index, bit); + + as->load32(Pointer(grayBitmap, 0), index); + as->or32(bit, index); + as->store32(index, Pointer(grayBitmap, 0)); + + as->pop(TargetPlatform::EngineRegister); + } + +#if WRITEBARRIER(none) + static Q_ALWAYS_INLINE void emitWriteBarrier(JITAssembler *, Address) {} +#endif static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) { as->MacroAssembler::loadDouble(addr, dest); } - static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr) + static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr, WriteBarrier::Type barrier) { as->MacroAssembler::storeDouble(source, addr); + if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, addr); } static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) { - Pointer ptr = as->loadAddress(TargetPlatform::ScratchRegister, target); - as->storeDouble(source, ptr); + WriteBarrier::Type barrier; + Pointer ptr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); + as->storeDouble(source, ptr, barrier); } - static void storeValue(JITAssembler *as, QV4::Primitive value, Address destination) + static void storeValue(JITAssembler *as, TargetPrimitive value, Address destination, WriteBarrier::Type barrier) { - as->store32(TrustedImm32(value.int_32()), destination); + as->store32(TrustedImm32(value.value()), destination); destination.offset += 4; as->store32(TrustedImm32(value.tag()), destination); + if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, destination); } template <typename Source, typename Destination> - static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination) + static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination, WriteBarrier::Type barrier) { as->loadDouble(source, TargetPlatform::FPGpr0); - as->storeDouble(TargetPlatform::FPGpr0, destination); + // We need to pass NoBarrier to storeDouble and call emitWriteBarrier ourselves, as the + // code in storeDouble assumes the type we're storing is actually a double, something + // that isn't always the case here. + as->storeDouble(TargetPlatform::FPGpr0, destination, WriteBarrier::NoBarrier); + if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, destination); } static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target) @@ -193,12 +245,14 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo as->moveIntsToDouble(TargetPlatform::LowReturnValueRegister, TargetPlatform::HighReturnValueRegister, dest, TargetPlatform::FPGpr0); } - static void storeReturnValue(JITAssembler *as, const Pointer &dest) + static void storeReturnValue(JITAssembler *as, const Pointer &dest, WriteBarrier::Type barrier) { Address destination = dest; as->store32(TargetPlatform::LowReturnValueRegister, destination); destination.offset += 4; as->store32(TargetPlatform::HighReturnValueRegister, destination); + if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, dest); } static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t) @@ -219,31 +273,31 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Jump done = as->jump(); intRange.link(as); as->move(srcReg, lowReg); - as->move(TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); + as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Integer)), highReg); done.link(as); } break; case IR::SInt32Type: as->move((RegisterID) t->index, lowReg); - as->move(TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); + as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Integer)), highReg); break; case IR::BoolType: as->move((RegisterID) t->index, lowReg); - as->move(TrustedImm32(QV4::Value::Boolean_Type_Internal), highReg); + as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Boolean)), highReg); break; default: Q_UNREACHABLE(); } } else { - Pointer addr = as->loadAddress(TargetPlatform::ScratchRegister, t); + Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, t); as->load32(addr, lowReg); addr.offset += 4; as->load32(addr, highReg); } } - static void setFunctionReturnValueFromConst(JITAssembler *as, QV4::Primitive retVal) + static void setFunctionReturnValueFromConst(JITAssembler *as, TargetPrimitive retVal) { - as->move(TrustedImm32(retVal.int_32()), TargetPlatform::LowReturnValueRegister); + as->move(TrustedImm32(retVal.value()), TargetPlatform::LowReturnValueRegister); as->move(TrustedImm32(retVal.tag()), TargetPlatform::HighReturnValueRegister); } @@ -295,7 +349,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { - Pointer tagAddr = as->loadAddress(scratchRegister, right); + Pointer tagAddr = as->loadAddressForReading(scratchRegister, right); as->load32(tagAddr, tagRegister); Jump j = as->branch32(JITAssembler::invert(cond), tagRegister, TrustedImm32(0)); as->addPatch(falseBlock, j); @@ -312,21 +366,24 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo { Q_ASSERT(source->type == IR::VarType); // load the tag: - Pointer addr = as->loadAddress(TargetPlatform::ScratchRegister, source); + Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, source); Pointer tagAddr = addr; tagAddr.offset += 4; as->load32(tagAddr, TargetPlatform::ReturnValueRegister); // check if it's an int32: Jump fallback = as->branch32(RelationalCondition::NotEqual, TargetPlatform::ReturnValueRegister, - TrustedImm32(Value::Integer_Type_Internal)); + TrustedImm32(quint32(Value::ValueTypeInternal_32::Integer))); IR::Temp *targetTemp = target->asTemp(); if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { as->load32(addr, TargetPlatform::ReturnValueRegister); - Pointer targetAddr = as->loadAddress(TargetPlatform::ScratchRegister, target); + WriteBarrier::Type barrier; + Pointer targetAddr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); as->store32(TargetPlatform::ReturnValueRegister, targetAddr); targetAddr.offset += 4; - as->store32(TrustedImm32(Value::Integer_Type_Internal), targetAddr); + as->store32(TrustedImm32(quint32(Value::ValueTypeInternal_32::Integer)), targetAddr); + if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, targetAddr); } else { as->load32(addr, (RegisterID) targetTemp->index); } @@ -335,17 +392,19 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo // not an int: fallback.link(as); generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, - as->loadAddress(TargetPlatform::ScratchRegister, source)); + as->loadAddressForReading(TargetPlatform::ScratchRegister, source)); as->storeInt32(TargetPlatform::ReturnValueRegister, target); intDone.link(as); } - static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr) + static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr, WriteBarrier::Type barrier) { as->store32(registerWithPtr, destAddr); destAddr.offset += 4; as->store32(TrustedImm32(QV4::Value::Managed_Type_Internal_32), destAddr); + if (WriteBarrier::isRequired<WriteBarrier::Object>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, destAddr); } static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) @@ -367,6 +426,13 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); jump.linkTo(loop, as); } + + static Jump checkIfTagRegisterIsDouble(JITAssembler *as, RegisterID tagRegister) + { + as->and32(TrustedImm32(Value::NotDouble_Mask), tagRegister); + Jump isNoDbl = as->branch32(RelationalCondition::Equal, tagRegister, TrustedImm32(Value::NotDouble_Mask)); + return isNoDbl; + } }; template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform> @@ -383,6 +449,47 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo using BranchTruncateType = typename JITAssembler::BranchTruncateType; using Jump = typename JITAssembler::Jump; using Label = typename JITAssembler::Label; + using ValueTypeInternal = Value::ValueTypeInternal_64; + using TargetPrimitive = TargetPrimitive64; + + static void emitSetGrayBit(JITAssembler *as, RegisterID base) + { + bool returnValueUsed = (base == TargetPlatform::ReturnValueRegister); + + as->push(TargetPlatform::EngineRegister); // free up one register for work + + RegisterID grayBitmap = returnValueUsed ? TargetPlatform::ScratchRegister : TargetPlatform::ReturnValueRegister; + as->move(base, grayBitmap); + Q_ASSERT(base != grayBitmap); + as->urshift64(TrustedImm32(Chunk::ChunkShift), grayBitmap); + as->lshift64(TrustedImm32(Chunk::ChunkShift), grayBitmap); + Q_STATIC_ASSERT(offsetof(Chunk, grayBitmap) == 0); + + RegisterID index = base; + as->move(base, index); + as->sub64(grayBitmap, index); + as->urshift64(TrustedImm32(Chunk::SlotSizeShift), index); + RegisterID grayIndex = TargetPlatform::EngineRegister; + as->move(index, grayIndex); + as->urshift64(TrustedImm32(Chunk::BitShift), grayIndex); + as->lshift64(TrustedImm32(3), grayIndex); // 8 bytes per quintptr + as->add64(grayIndex, grayBitmap); + as->and64(TrustedImm32(Chunk::Bits - 1), index); + + RegisterID bit = TargetPlatform::EngineRegister; + as->move(TrustedImm32(1), bit); + as->lshift64(index, bit); + + as->load64(Pointer(grayBitmap, 0), index); + as->or64(bit, index); + as->store64(index, Pointer(grayBitmap, 0)); + + as->pop(TargetPlatform::EngineRegister); + } + +#if WRITEBARRIER(none) + static Q_ALWAYS_INLINE void emitWriteBarrier(JITAssembler *, Address) {} +#endif static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) { @@ -391,19 +498,24 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); } - static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr) + static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr, WriteBarrier::Type barrier) { as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); as->store64(TargetPlatform::ReturnValueRegister, addr); + if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, addr); } static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) { as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); as->xor64(TargetPlatform::DoubleMaskRegister, TargetPlatform::ReturnValueRegister); - Pointer ptr = as->loadAddress(TargetPlatform::ScratchRegister, target); + WriteBarrier::Type barrier; + Pointer ptr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); as->store64(TargetPlatform::ReturnValueRegister, ptr); + if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, ptr); } static void storeReturnValue(JITAssembler *as, FPRegisterID dest) @@ -412,9 +524,11 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); } - static void storeReturnValue(JITAssembler *as, const Pointer &dest) + static void storeReturnValue(JITAssembler *as, const Pointer &dest, WriteBarrier::Type barrier) { as->store64(TargetPlatform::ReturnValueRegister, dest); + if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, dest); } static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t) @@ -433,7 +547,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Jump done = as->jump(); intRange.link(as); as->zeroExtend32ToPtr(srcReg, TargetPlatform::ReturnValueRegister); - quint64 tag = QV4::Value::Integer_Type_Internal; + quint64 tag = quint64(QV4::Value::ValueTypeInternal_64::Integer); as->or64(TrustedImm64(tag << 32), TargetPlatform::ReturnValueRegister); done.link(as); @@ -442,10 +556,10 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo quint64 tag; switch (t->type) { case IR::SInt32Type: - tag = QV4::Value::Integer_Type_Internal; + tag = quint64(QV4::Value::ValueTypeInternal_64::Integer); break; case IR::BoolType: - tag = QV4::Value::Boolean_Type_Internal; + tag = quint64(QV4::Value::ValueTypeInternal_64::Boolean); break; default: tag = 31337; // bogus value @@ -455,27 +569,29 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo TargetPlatform::ReturnValueRegister); } } else { - as->copyValue(TargetPlatform::ReturnValueRegister, t); + as->copyValue(TargetPlatform::ReturnValueRegister, t, WriteBarrier::NoBarrier); } } - static void setFunctionReturnValueFromConst(JITAssembler *as, QV4::Primitive retVal) + static void setFunctionReturnValueFromConst(JITAssembler *as, TargetPrimitive retVal) { as->move(TrustedImm64(retVal.rawValue()), TargetPlatform::ReturnValueRegister); } - static void storeValue(JITAssembler *as, QV4::Primitive value, Address destination) + static void storeValue(JITAssembler *as, TargetPrimitive value, Address destination, WriteBarrier::Type barrier) { as->store64(TrustedImm64(value.rawValue()), destination); + if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, destination); } template <typename Source, typename Destination> - static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination) + static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination, WriteBarrier::Type barrier) { // Use ReturnValueRegister as "scratch" register because loadArgument // and storeArgument are functions that may need a scratch register themselves. loadArgumentInRegister(as, source, TargetPlatform::ReturnValueRegister, 0); - as->storeReturnValue(destination); + as->storeReturnValue(destination, barrier); } static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target) @@ -501,7 +617,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Pointer addr = as->loadTempAddress(temp); as->load64(addr, dest); } else { - QV4::Value undefined = QV4::Primitive::undefinedValue(); + auto undefined = TargetPrimitive::undefinedValue(); as->move(TrustedImm64(undefined.rawValue()), dest); } } @@ -511,10 +627,10 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Q_UNUSED(argumentNumber); if (al) { - Pointer addr = as->loadArgLocalAddress(dest, al); + Pointer addr = as->loadArgLocalAddressForReading(dest, al); as->load64(addr, dest); } else { - QV4::Value undefined = QV4::Primitive::undefinedValue(); + auto undefined = TargetPrimitive::undefinedValue(); as->move(TrustedImm64(undefined.rawValue()), dest); } } @@ -523,7 +639,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo { Q_UNUSED(argumentNumber); - QV4::Value v = convertToValue(c); + auto v = convertToValue<TargetPrimitive64>(c); as->move(TrustedImm64(v.rawValue()), dest); } @@ -532,7 +648,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Q_UNUSED(argumentNumber); if (!expr) { - QV4::Value undefined = QV4::Primitive::undefinedValue(); + auto undefined = TargetPrimitive::undefinedValue(); as->move(TrustedImm64(undefined.rawValue()), dest); } else if (IR::Temp *t = expr->asTemp()){ loadArgumentInRegister(as, t, dest, argumentNumber); @@ -580,7 +696,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { - Pointer addr = as->loadAddress(scratchRegister, right); + Pointer addr = as->loadAddressForReading(scratchRegister, right); as->load64(addr, tagRegister); const TrustedImm64 tag(0); generateCJumpOnCompare(as, cond, tagRegister, tag, nextBlock, currentBlock, trueBlock, falseBlock); @@ -589,7 +705,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target) { Q_ASSERT(source->type == IR::VarType); - Pointer addr = as->loadAddress(TargetPlatform::ScratchRegister, source); + Pointer addr = as->loadAddressForReading(TargetPlatform::ScratchRegister, source); as->load64(addr, TargetPlatform::ScratchRegister); as->move(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); @@ -613,25 +729,30 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo // not an int: fallback.link(as); generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, - as->loadAddress(TargetPlatform::ScratchRegister, source)); + as->loadAddressForReading(TargetPlatform::ScratchRegister, source)); isIntConvertible.link(as); success.link(as); IR::Temp *targetTemp = target->asTemp(); if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - Pointer targetAddr = as->loadAddress(TargetPlatform::ScratchRegister, target); + WriteBarrier::Type barrier; + Pointer targetAddr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); as->store32(TargetPlatform::ReturnValueRegister, targetAddr); targetAddr.offset += 4; - as->store32(TrustedImm32(Value::Integer_Type_Internal), targetAddr); + as->store32(TrustedImm32(quint32(Value::ValueTypeInternal_64::Integer)), targetAddr); + if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, targetAddr); } else { as->storeInt32(TargetPlatform::ReturnValueRegister, target); } } - static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr) + static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr, WriteBarrier::Type barrier) { as->store64(registerWithPtr, destAddr); + if (WriteBarrier::isRequired<WriteBarrier::Object>() && barrier == WriteBarrier::Barrier) + emitWriteBarrier(as, destAddr); } static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) @@ -651,6 +772,13 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); jump.linkTo(loop, as); } + + static Jump checkIfTagRegisterIsDouble(JITAssembler *as, RegisterID tagRegister) + { + as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagRegister); + Jump isNoDbl = as->branch32(RelationalCondition::Equal, tagRegister, TrustedImm32(0)); + return isNoDbl; + } }; template <typename TargetConfiguration> @@ -713,7 +841,11 @@ public: using JITTargetPlatform::platformFinishEnteringStandardStackFrame; using JITTargetPlatform::platformLeaveStandardStackFrame; - using RegisterSizeDependentOps = RegisterSizeDependentAssembler<Assembler<TargetConfiguration>, MacroAssembler, JITTargetPlatform, RegisterSize>; + static qint32 targetStructureOffset(qint32 hostOffset) + { + Q_ASSERT(hostOffset % QT_POINTER_SIZE == 0); + return (hostOffset * RegisterSize) / QT_POINTER_SIZE; + } struct LookupCall { Address addr; @@ -728,7 +860,7 @@ public: struct RuntimeCall { Address addr; - inline RuntimeCall(uint offset = uint(INT_MIN)); + inline RuntimeCall(Runtime::RuntimeMethods method = Runtime::InvalidRuntimeMethod); bool isValid() const { return addr.offset >= 0; } }; @@ -745,6 +877,10 @@ public: {} }; + using RegisterSizeDependentOps = RegisterSizeDependentAssembler<Assembler<TargetConfiguration>, MacroAssembler, JITTargetPlatform, RegisterSize>; + using ValueTypeInternal = typename RegisterSizeDependentOps::ValueTypeInternal; + using TargetPrimitive = typename RegisterSizeDependentOps::TargetPrimitive; + // V4 uses two stacks: one stack with QV4::Value items, which is checked by the garbage // collector, and one stack used by the native C/C++/ABI code. This C++ stack is not scanned // by the garbage collector, so if any JS object needs to be retained, it should be put on the @@ -962,12 +1098,19 @@ public: Jump branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); Jump branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); - Pointer loadAddress(RegisterID tmp, IR::Expr *t); + Pointer loadAddressForWriting(RegisterID tmp, IR::Expr *t, WriteBarrier::Type *barrier); + Pointer loadAddressForReading(RegisterID tmp, IR::Expr *t) { + return loadAddressForWriting(tmp, t, 0); + } + Pointer loadTempAddress(IR::Temp *t); - Pointer loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al); + Pointer loadArgLocalAddressForWriting(RegisterID baseReg, IR::ArgLocal *al, WriteBarrier::Type *barrier); + Pointer loadArgLocalAddressForReading(RegisterID baseReg, IR::ArgLocal *al) { + return loadArgLocalAddressForWriting(baseReg, al, 0); + } Pointer loadStringAddress(RegisterID reg, const QString &string); Address loadConstant(IR::Const *c, RegisterID baseReg); - Address loadConstant(const Primitive &v, RegisterID baseReg); + Address loadConstant(const TargetPrimitive &v, RegisterID baseReg); void loadStringRef(RegisterID reg, const QString &string); Pointer stackSlotPointer(IR::Temp *t) const { @@ -986,16 +1129,16 @@ public: Pointer addr(_stackLayout->savedRegPointer(argumentNumber)); switch (t->type) { case IR::BoolType: - storeBool((RegisterID) t->index, addr); + storeBool((RegisterID) t->index, addr, WriteBarrier::NoBarrier); break; case IR::SInt32Type: - storeInt32((RegisterID) t->index, addr); + storeInt32((RegisterID) t->index, addr, WriteBarrier::NoBarrier); break; case IR::UInt32Type: - storeUInt32((RegisterID) t->index, addr); + storeUInt32((RegisterID) t->index, addr, WriteBarrier::NoBarrier); break; case IR::DoubleType: - storeDouble((FPRegisterID) t->index, addr); + storeDouble((FPRegisterID) t->index, addr, WriteBarrier::NoBarrier); break; default: Q_UNIMPLEMENTED(); @@ -1026,7 +1169,7 @@ public: if (!temp.value) { RegisterSizeDependentOps::zeroRegister(this, dest); } else { - Pointer addr = toAddress(dest, temp.value, argumentNumber); + Pointer addr = toAddress(dest, temp.value, argumentNumber, 0); loadArgumentInRegister(addr, dest, argumentNumber); } } @@ -1039,7 +1182,7 @@ public: void loadArgumentInRegister(Reference temp, RegisterID dest, int argumentNumber) { Q_ASSERT(temp.value); - Pointer addr = loadAddress(dest, temp.value); + Pointer addr = loadAddressForReading(dest, temp.value); loadArgumentInRegister(addr, dest, argumentNumber); } @@ -1072,8 +1215,10 @@ public: move(imm32, dest); } - void storeReturnValue(RegisterID dest) + void storeReturnValue(RegisterID dest, WriteBarrier::Type barrier = WriteBarrier::NoBarrier) { + Q_UNUSED(barrier); + Q_ASSERT(barrier == WriteBarrier::NoBarrier); move(ReturnValueRegister, dest); } @@ -1081,7 +1226,7 @@ public: { subPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); Pointer tmp(StackPointerRegister, 0); - storeReturnValue(tmp); + storeReturnValue(tmp, WriteBarrier::NoBarrier); toUInt32Register(tmp, dest); addPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); } @@ -1091,9 +1236,9 @@ public: RegisterSizeDependentOps::storeReturnValue(this, dest); } - void storeReturnValue(const Pointer &dest) + void storeReturnValue(const Pointer &dest, WriteBarrier::Type barrier) { - RegisterSizeDependentOps::storeReturnValue(this, dest); + RegisterSizeDependentOps::storeReturnValue(this, dest, barrier); } void storeReturnValue(IR::Expr *target) @@ -1101,22 +1246,19 @@ public: if (!target) return; - if (IR::Temp *temp = target->asTemp()) { - if (temp->kind == IR::Temp::PhysicalRegister) { - if (temp->type == IR::DoubleType) - storeReturnValue((FPRegisterID) temp->index); - else if (temp->type == IR::UInt32Type) - storeUInt32ReturnValue((RegisterID) temp->index); - else - storeReturnValue((RegisterID) temp->index); - return; - } else { - Pointer addr = loadTempAddress(temp); - storeReturnValue(addr); - } - } else if (IR::ArgLocal *al = target->asArgLocal()) { - Pointer addr = loadArgLocalAddress(ScratchRegister, al); - storeReturnValue(addr); + IR::Temp *temp = target->asTemp(); + if (temp && temp->kind == IR::Temp::PhysicalRegister) { + if (temp->type == IR::DoubleType) + storeReturnValue((FPRegisterID) temp->index); + else if (temp->type == IR::UInt32Type) + storeUInt32ReturnValue((RegisterID) temp->index); + else + storeReturnValue((RegisterID) temp->index); + return; + } else { + WriteBarrier::Type barrier; + Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); + storeReturnValue(addr, barrier); } } @@ -1153,7 +1295,7 @@ public: void loadArgumentOnStack(PointerToValue temp, int argumentNumber) { if (temp.value) { - Pointer ptr = toAddress(ScratchRegister, temp.value, argumentNumber); + Pointer ptr = toAddress(ScratchRegister, temp.value, argumentNumber, 0); loadArgumentOnStack<StackSlot>(ptr, argumentNumber); } else { RegisterSizeDependentOps::zeroStackSlot(this, StackSlot); @@ -1173,7 +1315,7 @@ public: { Q_ASSERT (temp.value); - Pointer ptr = loadAddress(ScratchRegister, temp.value); + Pointer ptr = loadAddressForReading(ScratchRegister, temp.value); loadArgumentOnStack<StackSlot>(ptr, argumentNumber); } @@ -1184,7 +1326,7 @@ public: moveDouble((FPRegisterID) sourceTemp->index, dest); return; } - Pointer ptr = loadAddress(ScratchRegister, source); + Pointer ptr = loadAddressForReading(ScratchRegister, source); loadDouble(ptr, dest); } @@ -1203,45 +1345,57 @@ public: RegisterSizeDependentOps::loadDouble(this, addr, dest); } - void storeDouble(FPRegisterID source, Address addr) + void storeDouble(FPRegisterID source, Address addr, WriteBarrier::Type barrier) { - RegisterSizeDependentOps::storeDouble(this, source, addr); + RegisterSizeDependentOps::storeDouble(this, source, addr, barrier); } template <typename Result, typename Source> - void copyValue(Result result, Source source); + void copyValue(Result result, Source source, WriteBarrier::Type barrier); template <typename Result> - void copyValue(Result result, IR::Expr* source); + void copyValue(Result result, IR::Expr* source, WriteBarrier::Type barrier); // The scratch register is used to calculate the temp address for the source. - void memcopyValue(Pointer target, IR::Expr *source, RegisterID scratchRegister) + void memcopyValue(Pointer target, IR::Expr *source, RegisterID scratchRegister, WriteBarrier::Type barrier) { Q_ASSERT(!source->asTemp() || source->asTemp()->kind != IR::Temp::PhysicalRegister); Q_ASSERT(target.base != scratchRegister); - TargetConfiguration::MacroAssembler::loadDouble(loadAddress(scratchRegister, source), FPGpr0); - TargetConfiguration::MacroAssembler::storeDouble(FPGpr0, target); + loadRawValue(loadAddressForReading(scratchRegister, source), FPGpr0); + storeRawValue(FPGpr0, target, barrier); } // The scratch register is used to calculate the temp address for the source. void memcopyValue(IR::Expr *target, Pointer source, FPRegisterID fpScratchRegister, RegisterID scratchRegister) { - TargetConfiguration::MacroAssembler::loadDouble(source, fpScratchRegister); - TargetConfiguration::MacroAssembler::storeDouble(fpScratchRegister, loadAddress(scratchRegister, target)); + loadRawValue(source, fpScratchRegister); + WriteBarrier::Type barrier; + Pointer dest = loadAddressForWriting(scratchRegister, target, &barrier); + storeRawValue(fpScratchRegister, dest, barrier); } - void storeValue(QV4::Primitive value, RegisterID destination) + void loadRawValue(Pointer source, FPRegisterID dest) { - Q_UNUSED(value); - Q_UNUSED(destination); - Q_UNREACHABLE(); + TargetConfiguration::MacroAssembler::loadDouble(source, dest); + } + + void storeRawValue(FPRegisterID source, Pointer dest, WriteBarrier::Type barrier) + { + TargetConfiguration::MacroAssembler::storeDouble(source, dest); + if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) + RegisterSizeDependentOps::emitWriteBarrier(this, dest); } - void storeValue(QV4::Primitive value, Address destination) + void storeValue(TargetPrimitive value, Address destination, WriteBarrier::Type barrier) { - RegisterSizeDependentOps::storeValue(this, value, destination); + RegisterSizeDependentOps::storeValue(this, value, destination, barrier); } - void storeValue(QV4::Primitive value, IR::Expr* temp); + void storeValue(TargetPrimitive value, IR::Expr* temp); + + void emitWriteBarrier(Address addr, WriteBarrier::Type barrier) { + if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) + RegisterSizeDependentOps::emitWriteBarrier(this, addr); + } void enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, const RegisterInformation &fpRegistersToSave); @@ -1249,7 +1403,7 @@ public: const RegisterInformation &fpRegistersToSave); void checkException() { - load32(Address(EngineRegister, qOffsetOf(QV4::ExecutionEngine, hasException)), ScratchRegister); + this->load8(Address(EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, hasException))), ScratchRegister); Jump exceptionThrown = branch32(RelationalCondition::NotEqual, ScratchRegister, TrustedImm32(0)); if (catchBlock) addPatch(catchBlock, exceptionThrown); @@ -1270,13 +1424,7 @@ public: if (argumentNumber < RegisterArgumentCount) loadArgumentInRegister(value, registerForArgument(argumentNumber), argumentNumber); else -#if OS(WINDOWS) && CPU(X86_64) - loadArgumentOnStack<argumentNumber>(value, argumentNumber); -#elif CPU(MIPS) // Stack space for 4 arguments needs to be allocated for MIPS platforms. - loadArgumentOnStack<argumentNumber>(value, argumentNumber + 4); -#else // Sanity: - loadArgumentOnStack<argumentNumber - RegisterArgumentCount>(value, argumentNumber); -#endif + loadArgumentOnStack<argumentNumber - RegisterArgumentCount + (StackShadowSpace / RegisterSize)>(value, argumentNumber); } template <int argumentNumber> @@ -1300,7 +1448,7 @@ public: template <int ArgumentIndex, typename Parameter> struct SizeOnStack { - enum { Size = Select<ArgumentIndex >= RegisterArgumentCount, sizeof(void*), 0>::Chosen }; + enum { Size = Select<ArgumentIndex >= RegisterArgumentCount, RegisterSize, 0>::Chosen }; }; template <int ArgumentIndex> @@ -1317,8 +1465,8 @@ public: // IMPORTANT! See generateLookupCall in qv4isel_masm_p.h for details! // load the table from the context - loadPtr(Address(EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), ScratchRegister); - loadPtr(Address(ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, lookups)), + loadPtr(Address(EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), ScratchRegister); + loadPtr(Address(ScratchRegister, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, lookups))), lookupCall.addr.base); // pre-calculate the indirect address for the lookupCall table: if (lookupCall.addr.offset) @@ -1411,15 +1559,17 @@ public: generateFunctionCallImp(needsExceptionCheck, r, functionName, function, arg1, VoidType(), VoidType(), VoidType(), VoidType(), VoidType()); } - Pointer toAddress(RegisterID tmpReg, IR::Expr *e, int offset) + Pointer toAddress(RegisterID tmpReg, IR::Expr *e, int offset, WriteBarrier::Type *barrier) { + if (barrier) + *barrier = WriteBarrier::NoBarrier; if (IR::Const *c = e->asConst()) { Address addr = _stackLayout->savedRegPointer(offset); Address tagAddr = addr; tagAddr.offset += 4; - QV4::Primitive v = convertToValue(c); - store32(TrustedImm32(v.int_32()), addr); + auto v = convertToValue<TargetPrimitive>(c); + store32(TrustedImm32(v.value()), addr); store32(TrustedImm32(v.tag()), tagAddr); return Pointer(addr); } @@ -1428,14 +1578,16 @@ public: if (t->kind == IR::Temp::PhysicalRegister) return Pointer(_stackLayout->savedRegPointer(offset)); - return loadAddress(tmpReg, e); + return loadAddressForWriting(tmpReg, e, barrier); } - void storeBool(RegisterID reg, Pointer addr) + void storeBool(RegisterID reg, Pointer addr, WriteBarrier::Type barrier) { store32(reg, addr); addr.offset += 4; - store32(TrustedImm32(QV4::Primitive::fromBoolean(0).tag()), addr); + store32(TrustedImm32(TargetPrimitive::fromBoolean(0).tag()), addr); + if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) + RegisterSizeDependentOps::emitWriteBarrier(this, addr); } void storeBool(RegisterID src, RegisterID dest) @@ -1452,8 +1604,9 @@ public: } } - Pointer addr = loadAddress(ScratchRegister, target); - storeBool(reg, addr); + WriteBarrier::Type barrier; + Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); + storeBool(reg, addr, barrier); } void storeBool(bool value, IR::Expr *target) { @@ -1475,25 +1628,24 @@ public: move(src, dest); } - void storeInt32(RegisterID reg, Pointer addr) + void storeInt32(RegisterID reg, Pointer addr, WriteBarrier::Type barrier) { store32(reg, addr); addr.offset += 4; - store32(TrustedImm32(QV4::Primitive::fromInt32(0).tag()), addr); + store32(TrustedImm32(TargetPrimitive::fromInt32(0).tag()), addr); + if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) + RegisterSizeDependentOps::emitWriteBarrier(this, addr); } void storeInt32(RegisterID reg, IR::Expr *target) { - if (IR::Temp *targetTemp = target->asTemp()) { - if (targetTemp->kind == IR::Temp::PhysicalRegister) { - move(reg, (RegisterID) targetTemp->index); - } else { - Pointer addr = loadTempAddress(targetTemp); - storeInt32(reg, addr); - } - } else if (IR::ArgLocal *al = target->asArgLocal()) { - Pointer addr = loadArgLocalAddress(ScratchRegister, al); - storeInt32(reg, addr); + IR::Temp *targetTemp = target->asTemp(); + if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { + move(reg, (RegisterID) targetTemp->index); + } else { + WriteBarrier::Type barrier; + Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); + storeInt32(reg, addr, barrier); } } @@ -1502,15 +1654,15 @@ public: move(src, dest); } - void storeUInt32(RegisterID reg, Pointer addr) + void storeUInt32(RegisterID reg, Pointer addr, WriteBarrier::Type barrier) { // The UInt32 representation in QV4::Value is really convoluted. See also toUInt32Register. Jump intRange = branch32(RelationalCondition::GreaterThanOrEqual, reg, TrustedImm32(0)); convertUInt32ToDouble(reg, FPGpr0, ReturnValueRegister); - storeDouble(FPGpr0, addr); + storeDouble(FPGpr0, addr, barrier); Jump done = jump(); intRange.link(this); - storeInt32(reg, addr); + storeInt32(reg, addr, barrier); done.link(this); } @@ -1520,8 +1672,9 @@ public: if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { move(reg, (RegisterID) targetTemp->index); } else { - Pointer addr = loadAddress(ScratchRegister, target); - storeUInt32(reg, addr); + WriteBarrier::Type barrier; + Pointer addr = loadAddressForWriting(ScratchRegister, target, &barrier); + storeUInt32(reg, addr, barrier); } } @@ -1548,7 +1701,7 @@ public: RegisterID toInt32Register(IR::Expr *e, RegisterID scratchReg) { if (IR::Const *c = e->asConst()) { - move(TrustedImm32(convertToValue(c).int_32()), scratchReg); + move(TrustedImm32(convertToValue<Primitive>(c).int_32()), scratchReg); return scratchReg; } @@ -1556,7 +1709,7 @@ public: if (t->kind == IR::Temp::PhysicalRegister) return (RegisterID) t->index; - return toInt32Register(loadAddress(scratchReg, e), scratchReg); + return toInt32Register(loadAddressForReading(scratchReg, e), scratchReg); } RegisterID toInt32Register(Pointer addr, RegisterID scratchReg) @@ -1576,7 +1729,7 @@ public: if (t->kind == IR::Temp::PhysicalRegister) return (RegisterID) t->index; - return toUInt32Register(loadAddress(scratchReg, e), scratchReg); + return toUInt32Register(loadAddressForReading(scratchReg, e), scratchReg); } RegisterID toUInt32Register(Pointer addr, RegisterID scratchReg) @@ -1587,11 +1740,11 @@ public: Pointer tagAddr = addr; tagAddr.offset += 4; load32(tagAddr, scratchReg); - Jump inIntRange = branch32(RelationalCondition::Equal, scratchReg, TrustedImm32(QV4::Value::Integer_Type_Internal)); + Jump inIntRange = branch32(RelationalCondition::Equal, scratchReg, TrustedImm32(quint32(ValueTypeInternal::Integer))); // it's not in signed int range, so load it as a double, and truncate it down loadDouble(addr, FPGpr0); - Address inversionAddress = loadConstant(QV4::Primitive::fromDouble(double(INT_MAX) + 1), scratchReg); + Address inversionAddress = loadConstant(TargetPrimitive::fromDouble(double(INT_MAX) + 1), scratchReg); subDouble(inversionAddress, FPGpr0); Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg); canNeverHappen.link(this); @@ -1616,9 +1769,9 @@ public: const int locals = _stackLayout->calculateJSStackFrameSize(); if (locals <= 0) return; - loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)), JITTargetPlatform::LocalsRegister); + loadPtr(Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop))), JITTargetPlatform::LocalsRegister); RegisterSizeDependentOps::initializeLocalVariables(this, locals); - storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop))); + storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, targetStructureOffset(offsetof(EngineBase, jsStackTop)))); } Label exceptionReturnLabel; @@ -1647,40 +1800,44 @@ private: }; template <typename TargetConfiguration> +const typename Assembler<TargetConfiguration>::VoidType Assembler<TargetConfiguration>::Void; + +template <typename TargetConfiguration> template <typename Result, typename Source> -void Assembler<TargetConfiguration>::copyValue(Result result, Source source) +void Assembler<TargetConfiguration>::copyValue(Result result, Source source, WriteBarrier::Type barrier) { - RegisterSizeDependentOps::copyValueViaRegisters(this, source, result); + RegisterSizeDependentOps::copyValueViaRegisters(this, source, result, barrier); } template <typename TargetConfiguration> template <typename Result> -void Assembler<TargetConfiguration>::copyValue(Result result, IR::Expr* source) +void Assembler<TargetConfiguration>::copyValue(Result result, IR::Expr* source, WriteBarrier::Type barrier) { if (source->type == IR::BoolType) { RegisterID reg = toInt32Register(source, ScratchRegister); - storeBool(reg, result); + storeBool(reg, result, barrier); } else if (source->type == IR::SInt32Type) { RegisterID reg = toInt32Register(source, ScratchRegister); - storeInt32(reg, result); + storeInt32(reg, result, barrier); } else if (source->type == IR::UInt32Type) { RegisterID reg = toUInt32Register(source, ScratchRegister); - storeUInt32(reg, result); + storeUInt32(reg, result, barrier); } else if (source->type == IR::DoubleType) { - storeDouble(toDoubleRegister(source), result); + storeDouble(toDoubleRegister(source), result, barrier); } else if (source->asTemp() || source->asArgLocal()) { - RegisterSizeDependentOps::copyValueViaRegisters(this, source, result); + RegisterSizeDependentOps::copyValueViaRegisters(this, source, result, barrier); } else if (IR::Const *c = source->asConst()) { - QV4::Primitive v = convertToValue(c); - storeValue(v, result); + auto v = convertToValue<TargetPrimitive>(c); + storeValue(v, result, barrier); } else { Q_UNREACHABLE(); } } template <typename TargetConfiguration> -inline Assembler<TargetConfiguration>::RuntimeCall::RuntimeCall(uint offset) - : addr(Assembler::EngineRegister, offset + qOffsetOf(QV4::ExecutionEngine, runtime)) +inline Assembler<TargetConfiguration>::RuntimeCall::RuntimeCall(Runtime::RuntimeMethods method) + : addr(Assembler::EngineRegister, + method == Runtime::InvalidRuntimeMethod ? -1 : (Assembler<TargetConfiguration>::targetStructureOffset(offsetof(EngineBase, runtime) + Runtime::runtimeMethodOffset(method)))) { } diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index 22067bbb13..a1c65f644c 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -165,17 +165,17 @@ struct ArchitectureSpecificBinaryOperation<Assembler<AssemblerTargetConfiguratio #endif #define OP(op) \ - { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } + { "Runtime::" isel_stringIfy(op), QV4::Runtime::op, QV4::Runtime::InvalidRuntimeMethod, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define OPCONTEXT(op) \ - { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } + { "Runtime::" isel_stringIfy(op), QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::op, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define INLINE_OP(op, memOp, immOp) \ - { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } + { "Runtime::" isel_stringIfy(op), QV4::Runtime::op, QV4::Runtime::InvalidRuntimeMethod, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define INLINE_OPCONTEXT(op, memOp, immOp) \ - { "Runtime::" isel_stringIfy(op), INT_MIN, offsetof(QV4::Runtime, op), memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } + { "Runtime::" isel_stringIfy(op), QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::op, memOp, immOp, QV4::Runtime::Method_##op##_NeedsExceptionCheck } #define NULL_OP \ - { 0, 0, 0, 0, 0, false } + { 0, QV4::Runtime::InvalidRuntimeMethod, QV4::Runtime::InvalidRuntimeMethod, 0, 0, false } template <typename JITAssembler> const typename Binop<JITAssembler>::OpInfo Binop<JITAssembler>::operations[IR::LastAluOp + 1] = { @@ -492,7 +492,7 @@ bool Binop<JITAssembler>::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource return false; } } else if (inplaceOpWithAddress) { // All cases of X = X op [address-of-Y] - Pointer rhsAddr = as->loadAddress(JITAssembler::ScratchRegister, rightSource); + Pointer rhsAddr = as->loadAddressForReading(JITAssembler::ScratchRegister, rightSource); switch (op) { case IR::OpBitAnd: as->and32(rhsAddr, targetReg); break; case IR::OpBitOr: as->or32 (rhsAddr, targetReg); break; diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h index d2d9ba7753..1b1ab7f24d 100644 --- a/src/qml/jit/qv4binop_p.h +++ b/src/qml/jit/qv4binop_p.h @@ -88,8 +88,8 @@ struct Binop { struct OpInfo { const char *name; - int fallbackImplementation; // offsetOf(Runtime,...) - int contextImplementation; // offsetOf(Runtime,...) + Runtime::RuntimeMethods fallbackImplementation; + Runtime::RuntimeMethods contextImplementation; MemRegOp inlineMemRegOp; ImmRegOp inlineImmRegOp; bool needsExceptionCheck; diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 69d6951bb9..599370f73d 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -132,8 +132,8 @@ void InstructionSelection<JITAssembler>::run(int functionIndex) for (IR::Stmt *s : _block->statements()) { if (s->location.isValid()) { if (int(s->location.startLine) != lastLine) { - _as->loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister); - Address lineAddr(JITTargetPlatform::ScratchRegister, qOffsetOf(QV4::ExecutionContext::Data, lineNumber)); + _as->loadPtr(Address(JITTargetPlatform::EngineRegister, JITAssembler::targetStructureOffset(offsetof(QV4::EngineBase, current))), JITTargetPlatform::ScratchRegister); + Address lineAddr(JITTargetPlatform::ScratchRegister, JITAssembler::targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, lineNumber))); _as->store32(TrustedImm32(s->location.startLine), lineAddr); lastLine = s->location.startLine; } @@ -256,7 +256,7 @@ void InstructionSelection<JITAssembler>::callBuiltinDeleteName(const QString &na template <typename JITAssembler> void InstructionSelection<JITAssembler>::callBuiltinDeleteValue(IR::Expr *result) { - _as->storeValue(Primitive::fromBoolean(false), result); + _as->storeValue(JITAssembler::TargetPrimitive::fromBoolean(false), result); } template <typename JITAssembler> @@ -350,11 +350,11 @@ void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr bool isData = it->expr->asConst()->value; it = it->next; - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr); + _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); if (!isData) { it = it->next; - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr); + _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); } } @@ -376,10 +376,10 @@ void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr ++arrayValueCount; // Index - _as->storeValue(QV4::Primitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++)); + _as->storeValue(JITAssembler::TargetPrimitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier); // Value - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr); + _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); it = it->next; } @@ -400,14 +400,14 @@ void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr ++arrayGetterSetterCount; // Index - _as->storeValue(QV4::Primitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++)); + _as->storeValue(JITAssembler::TargetPrimitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier); // Getter - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr); + _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); it = it->next; // Setter - _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr); + _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); it = it->next; } @@ -447,9 +447,11 @@ void InstructionSelection<JITAssembler>::callValue(IR::Expr *value, IR::ExprList template <typename JITAssembler> void InstructionSelection<JITAssembler>::loadThisObject(IR::Expr *temp) { - _as->loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister); - _as->loadPtr(Address(JITTargetPlatform::ScratchRegister, qOffsetOf(ExecutionContext::Data, callData)), JITTargetPlatform::ScratchRegister); - _as->copyValue(temp, Address(JITTargetPlatform::ScratchRegister, qOffsetOf(CallData, thisObject))); + WriteBarrier::Type barrier; + Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, temp, &barrier); + _as->loadPtr(Address(JITTargetPlatform::EngineRegister, JITAssembler::targetStructureOffset(offsetof(QV4::EngineBase, current))), JITTargetPlatform::ReturnValueRegister); + _as->loadPtr(Address(JITTargetPlatform::ReturnValueRegister,JITAssembler::targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, callData))), JITTargetPlatform::ReturnValueRegister); + _as->copyValue(addr, Address(JITTargetPlatform::ReturnValueRegister, offsetof(CallData, thisObject)), barrier); } template <typename JITAssembler> @@ -486,7 +488,7 @@ void InstructionSelection<JITAssembler>::loadConst(IR::Const *sourceConst, IR::E _as->toUInt32Register(sourceConst, (RegisterID) targetTemp->index); } else if (targetTemp->type == IR::BoolType) { Q_ASSERT(sourceConst->type == IR::BoolType); - _as->move(TrustedImm32(convertToValue(sourceConst).int_32()), + _as->move(TrustedImm32(convertToValue<Primitive>(sourceConst).int_32()), (RegisterID) targetTemp->index); } else { Q_UNREACHABLE(); @@ -495,7 +497,7 @@ void InstructionSelection<JITAssembler>::loadConst(IR::Const *sourceConst, IR::E } } - _as->storeValue(convertToValue(sourceConst), target); + _as->storeValue(convertToValue<typename JITAssembler::TargetPrimitive>(sourceConst), target); } template <typename JITAssembler> @@ -503,8 +505,9 @@ void InstructionSelection<JITAssembler>::loadString(const QString &str, IR::Expr { Pointer srcAddr = _as->loadStringAddress(JITTargetPlatform::ReturnValueRegister, str); _as->loadPtr(srcAddr, JITTargetPlatform::ReturnValueRegister); - Pointer destAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, target); - JITAssembler::RegisterSizeDependentOps::loadManagedPointer(_as, JITTargetPlatform::ReturnValueRegister, destAddr); + WriteBarrier::Type barrier; + Pointer destAddr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, target, &barrier); + JITAssembler::RegisterSizeDependentOps::loadManagedPointer(_as, JITTargetPlatform::ReturnValueRegister, destAddr, barrier); } template <typename JITAssembler> @@ -519,7 +522,7 @@ void InstructionSelection<JITAssembler>::getActivationProperty(const IR::Name *n { if (useFastLookups && name->global) { uint index = registerGlobalGetterLookup(*name->id); - generateLookupCall(target, index, qOffsetOf(QV4::Lookup, globalGetter), JITTargetPlatform::EngineRegister, JITAssembler::Void); + generateLookupCall(target, index, offsetof(QV4::Lookup, globalGetter), JITTargetPlatform::EngineRegister, JITAssembler::Void); return; } generateRuntimeCall(_as, target, getActivationProperty, JITTargetPlatform::EngineRegister, StringToIndex(*name->id)); @@ -545,7 +548,7 @@ void InstructionSelection<JITAssembler>::getProperty(IR::Expr *base, const QStri { if (useFastLookups) { uint index = registerGetterLookup(name); - generateLookupCall(target, index, qOffsetOf(QV4::Lookup, getter), JITTargetPlatform::EngineRegister, PointerToValue(base), JITAssembler::Void); + generateLookupCall(target, index, offsetof(QV4::Lookup, getter), JITTargetPlatform::EngineRegister, PointerToValue(base), JITAssembler::Void); } else { generateRuntimeCall(_as, target, getProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), StringToIndex(name)); @@ -584,7 +587,7 @@ void InstructionSelection<JITAssembler>::setProperty(IR::Expr *source, IR::Expr { if (useFastLookups) { uint index = registerSetterLookup(targetName); - generateLookupCall(JITAssembler::Void, index, qOffsetOf(QV4::Lookup, setter), + generateLookupCall(JITAssembler::Void, index, offsetof(QV4::Lookup, setter), JITTargetPlatform::EngineRegister, PointerToValue(targetBase), PointerToValue(source)); @@ -620,7 +623,8 @@ void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *in { if (useFastLookups) { uint lookup = registerIndexedGetterLookup(); - generateLookupCall(target, lookup, qOffsetOf(QV4::Lookup, indexedGetter), + generateLookupCall(target, lookup, offsetof(QV4::Lookup, indexedGetter), + JITTargetPlatform::EngineRegister, PointerToValue(base), PointerToValue(index)); return; @@ -635,7 +639,8 @@ void InstructionSelection<JITAssembler>::setElement(IR::Expr *source, IR::Expr * { if (useFastLookups) { uint lookup = registerIndexedSetterLookup(); - generateLookupCall(JITAssembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter), + generateLookupCall(JITAssembler::Void, lookup, offsetof(QV4::Lookup, indexedSetter), + JITTargetPlatform::EngineRegister, PointerToValue(targetBase), PointerToValue(targetIndex), PointerToValue(source)); return; @@ -711,8 +716,10 @@ void InstructionSelection<JITAssembler>::copyValue(IR::Expr *source, IR::Expr *t } } + WriteBarrier::Type barrier; + Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, target, &barrier); // The target is not a physical register, nor is the source. So we can do a memory-to-memory copy: - _as->memcopyValue(_as->loadAddress(JITTargetPlatform::ReturnValueRegister, target), source, JITTargetPlatform::ScratchRegister); + _as->memcopyValue(addr, source, JITTargetPlatform::ScratchRegister, barrier); } template <typename JITAssembler> @@ -739,14 +746,13 @@ void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr * } else if (!sourceTemp || sourceTemp->kind == IR::Temp::StackSlot) { if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { // Note: a swap for two stack-slots can involve different types. - Pointer sAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); - Pointer tAddr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, target); - // use the implementation in JSC::MacroAssembler, as it doesn't do bit swizzling - auto platformAs = static_cast<typename JITAssembler::MacroAssembler*>(_as); - platformAs->loadDouble(sAddr, JITTargetPlatform::FPGpr0); - platformAs->loadDouble(tAddr, JITTargetPlatform::FPGpr1); - platformAs->storeDouble(JITTargetPlatform::FPGpr1, sAddr); - platformAs->storeDouble(JITTargetPlatform::FPGpr0, tAddr); + WriteBarrier::Type barrierForSource, barrierForTarget; + Pointer sAddr = _as->loadAddressForWriting(JITTargetPlatform::ScratchRegister, source, &barrierForSource); + Pointer tAddr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, target, &barrierForTarget); + _as->loadRawValue(sAddr, JITTargetPlatform::FPGpr0); + _as->loadRawValue(tAddr, JITTargetPlatform::FPGpr1); + _as->storeRawValue(JITTargetPlatform::FPGpr1, sAddr, barrierForSource); + _as->storeRawValue(JITTargetPlatform::FPGpr0, tAddr, barrierForTarget); return; } } @@ -757,14 +763,15 @@ void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr * Q_ASSERT(memExpr); Q_ASSERT(regTemp); - Pointer addr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, memExpr); + WriteBarrier::Type barrier; + Pointer addr = _as->loadAddressForWriting(JITTargetPlatform::ReturnValueRegister, memExpr, &barrier); if (regTemp->type == IR::DoubleType) { _as->loadDouble(addr, JITTargetPlatform::FPGpr0); - _as->storeDouble((FPRegisterID) regTemp->index, addr); + _as->storeDouble((FPRegisterID) regTemp->index, addr, barrier); _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) regTemp->index); } else if (regTemp->type == IR::UInt32Type) { _as->toUInt32Register(addr, JITTargetPlatform::ScratchRegister); - _as->storeUInt32((RegisterID) regTemp->index, addr); + _as->storeUInt32((RegisterID) regTemp->index, addr, barrier); _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); } else { _as->load32(addr, JITTargetPlatform::ScratchRegister); @@ -774,16 +781,17 @@ void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr * quint32 tag; switch (regTemp->type) { case IR::BoolType: - tag = QV4::Value::Boolean_Type_Internal; + tag = quint32(JITAssembler::ValueTypeInternal::Boolean); break; case IR::SInt32Type: - tag = QV4::Value::Integer_Type_Internal; + tag = quint32(JITAssembler::ValueTypeInternal::Integer); break; default: tag = 31337; // bogus value Q_UNREACHABLE(); } _as->store32(TrustedImm32(tag), addr); + _as->emitWriteBarrier(addr, barrier); } _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); } @@ -791,12 +799,12 @@ void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr * #define setOp(op, opName, operation) \ do { \ - op = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + op = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); opName = "Runtime::" isel_stringIfy(operation); \ needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) #define setOpContext(op, opName, operation) \ do { \ - opContext = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + opContext = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); opName = "Runtime::" isel_stringIfy(operation); \ needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) @@ -913,19 +921,19 @@ void InstructionSelection<JITAssembler>::convertTypeToDouble(IR::Expr *source, I convertUIntToDouble(source, target); break; case IR::UndefinedType: - _as->loadDouble(_as->loadAddress(JITTargetPlatform::ScratchRegister, source), JITTargetPlatform::FPGpr0); + _as->loadDouble(_as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source), JITTargetPlatform::FPGpr0); _as->storeDouble(JITTargetPlatform::FPGpr0, target); break; case IR::StringType: case IR::VarType: { // load the tag: - Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); tagAddr.offset += 4; _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); // check if it's an int32: Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(Value::Integer_Type_Internal)); + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); convertIntToDouble(source, target); Jump intDone = _as->jump(); @@ -938,7 +946,7 @@ void InstructionSelection<JITAssembler>::convertTypeToDouble(IR::Expr *source, I // it is a double: isDbl.link(_as); - Pointer addr2 = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + Pointer addr2 = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); IR::Temp *targetTemp = target->asTemp(); if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { _as->memcopyValue(target, addr2, JITTargetPlatform::FPGpr0, JITTargetPlatform::ReturnValueRegister); @@ -996,20 +1004,20 @@ void InstructionSelection<JITAssembler>::convertTypeToBool(IR::Expr *source, IR: _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); case IR::VarType: default: - Pointer addr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + Pointer addr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); Pointer tagAddr = addr; tagAddr.offset += 4; _as->load32(tagAddr, JITTargetPlatform::ReturnValueRegister); // checkif it's a bool: Jump notBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, - TrustedImm32(Value::Boolean_Type_Internal)); + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); _as->load32(addr, JITTargetPlatform::ReturnValueRegister); Jump boolDone = _as->jump(); // check if it's an int32: notBool.link(_as); Jump fallback = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, - TrustedImm32(Value::Integer_Type_Internal)); + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); _as->load32(addr, JITTargetPlatform::ReturnValueRegister); Jump isZero = _as->branch32(RelationalCondition::Equal, JITTargetPlatform::ReturnValueRegister, TrustedImm32(0)); @@ -1061,7 +1069,7 @@ void InstructionSelection<JITAssembler>::convertTypeToSInt32(IR::Expr *source, I case IR::StringType: default: generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toInt, - _as->loadAddress(JITTargetPlatform::ScratchRegister, source)); + _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source)); _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); break; } // switch (source->type) @@ -1073,21 +1081,21 @@ void InstructionSelection<JITAssembler>::convertTypeToUInt32(IR::Expr *source, I switch (source->type) { case IR::VarType: { // load the tag: - Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); tagAddr.offset += 4; _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); // check if it's an int32: Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(Value::Integer_Type_Internal)); - Pointer addr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); + Pointer addr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); _as->storeUInt32(_as->toInt32Register(addr, JITTargetPlatform::ScratchRegister), target); Jump intDone = _as->jump(); // not an int: isNoInt.link(_as); generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, - _as->loadAddress(JITTargetPlatform::ScratchRegister, source)); + _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source)); _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); intDone.link(_as); @@ -1192,10 +1200,11 @@ void InstructionSelection<JITAssembler>::visitCJump(IR::CJump *s) reg = JITTargetPlatform::ReturnValueRegister; _as->toInt32Register(t, reg); } else { - Address temp = _as->loadAddress(JITTargetPlatform::ScratchRegister, s->cond); + Address temp = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, s->cond); Address tag = temp; tag.offset += QV4::Value::tagOffset(); - Jump booleanConversion = _as->branch32(RelationalCondition::NotEqual, tag, TrustedImm32(QV4::Value::Boolean_Type_Internal)); + Jump booleanConversion = _as->branch32(RelationalCondition::NotEqual, tag, + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); Address data = temp; data.offset += QV4::Value::valueOffset(); @@ -1297,9 +1306,9 @@ int InstructionSelection<JITAssembler>::prepareVariableArguments(IR::ExprList* a Q_ASSERT(arg != 0); Pointer dst(_as->stackLayout().argumentAddressForCall(i)); if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister); + _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister, WriteBarrier::NoBarrier); else - _as->copyValue(dst, arg); + _as->copyValue(dst, arg, WriteBarrier::NoBarrier); } return argc; @@ -1313,15 +1322,15 @@ int InstructionSelection<JITAssembler>::prepareCallData(IR::ExprList* args, IR:: ++argc; } - Pointer p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, tag)); - _as->store32(TrustedImm32(QV4::Value::Integer_Type_Internal), p); - p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, argc)); + Pointer p = _as->stackLayout().callDataAddress(offsetof(CallData, tag)); + _as->store32(TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer)), p); + p = _as->stackLayout().callDataAddress(offsetof(CallData, argc)); _as->store32(TrustedImm32(argc), p); - p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, thisObject)); + p = _as->stackLayout().callDataAddress(offsetof(CallData, thisObject)); if (!thisObject) - _as->storeValue(QV4::Primitive::undefinedValue(), p); + _as->storeValue(JITAssembler::TargetPrimitive::undefinedValue(), p, WriteBarrier::NoBarrier); else - _as->copyValue(p, thisObject); + _as->copyValue(p, thisObject, WriteBarrier::NoBarrier); int i = 0; for (IR::ExprList *it = args; it; it = it->next, ++i) { @@ -1329,9 +1338,9 @@ int InstructionSelection<JITAssembler>::prepareCallData(IR::ExprList* args, IR:: Q_ASSERT(arg != 0); Pointer dst(_as->stackLayout().argumentAddressForCall(i)); if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister); + _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister, WriteBarrier::NoBarrier); else - _as->copyValue(dst, arg); + _as->copyValue(dst, arg, WriteBarrier::NoBarrier); } return argc; } @@ -1449,14 +1458,14 @@ bool InstructionSelection<JITAssembler>::visitCJumpStrictNull(IR::Binop *binop, return true; } - Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, varSrc); + Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, varSrc); tagAddr.offset += 4; const RegisterID tagReg = JITTargetPlatform::ScratchRegister; _as->load32(tagAddr, tagReg); RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal : RelationalCondition::NotEqual; - const TrustedImm32 tag(QV4::Value::Null_Type_Internal); + const TrustedImm32 tag{quint32(JITAssembler::ValueTypeInternal::Null)}; _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); return true; } @@ -1532,13 +1541,13 @@ bool InstructionSelection<JITAssembler>::visitCJumpStrictBool(IR::Binop *binop, return true; } - Pointer otherAddr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, otherSrc); + Pointer otherAddr = _as->loadAddressForReading(JITTargetPlatform::ReturnValueRegister, otherSrc); otherAddr.offset += 4; // tag address // check if the tag of the var operand is indicates 'boolean' _as->load32(otherAddr, JITTargetPlatform::ScratchRegister); Jump noBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(QV4::Value::Boolean_Type_Internal)); + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); if (binop->op == IR::OpStrictEqual) _as->addPatch(falseBlock, noBool); else @@ -1581,14 +1590,14 @@ bool InstructionSelection<JITAssembler>::visitCJumpNullUndefined(IR::Type nullOr return true; } - Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, varSrc); + Pointer tagAddr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, varSrc); tagAddr.offset += 4; const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister; _as->load32(tagAddr, tagReg); if (binop->op == IR::OpNotEqual) qSwap(trueBlock, falseBlock); - Jump isNull = _as->branch32(RelationalCondition::Equal, tagReg, TrustedImm32(int(QV4::Value::Null_Type_Internal))); + Jump isNull = _as->branch32(RelationalCondition::Equal, tagReg, TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Null))); Jump isNotUndefinedTag = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(int(QV4::Value::Managed_Type_Internal))); tagAddr.offset -= 4; _as->load32(tagAddr, tagReg); @@ -1640,18 +1649,18 @@ Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &arch using ARMv7CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; using ARM64CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>; - if (architecture == QLatin1String("armv7")) + if (architecture == QLatin1String("arm")) return new ISelFactory<ARMv7CrossAssembler>; - else if (architecture == QLatin1String("armv8")) + else if (architecture == QLatin1String("arm64")) return new ISelFactory<ARM64CrossAssembler>; QString hostArch; #if CPU(ARM_THUMB2) - hostArch = QStringLiteral("armv7"); + hostArch = QStringLiteral("arm"); #elif CPU(MIPS) hostArch = QStringLiteral("mips"); #elif CPU(X86) - hostArch = QStringLiteral("x86"); + hostArch = QStringLiteral("i386"); #elif CPU(X86_64) hostArch = QStringLiteral("x86_64"); #endif diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 5c046cb397..7019a117a2 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -160,7 +160,7 @@ protected: // FramePointerRegister points to its old value on the stack, and above // it we have the return address, hence the need to step over two // values before reaching the first argument. - return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * sizeof(void*)); + return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * JITTargetPlatform::RegisterSize); } Pointer baseAddressForCallArguments() @@ -209,7 +209,7 @@ private: _as->convertInt32ToDouble((RegisterID) sourceTemp->index, (FPRegisterID) targetTemp->index); } else { - _as->convertInt32ToDouble(_as->loadAddress(JITTargetPlatform::ReturnValueRegister, sourceTemp), + _as->convertInt32ToDouble(_as->loadAddressForReading(JITTargetPlatform::ReturnValueRegister, sourceTemp), (FPRegisterID) targetTemp->index); } } else { @@ -223,7 +223,7 @@ private: _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), JITTargetPlatform::FPGpr0); - _as->storeDouble(JITTargetPlatform::FPGpr0, _as->loadAddress(JITTargetPlatform::ReturnValueRegister, target)); + _as->storeDouble(JITTargetPlatform::FPGpr0, target); } void convertUIntToDouble(IR::Expr *source, IR::Expr *target) @@ -240,7 +240,7 @@ private: _as->convertUInt32ToDouble(_as->toUInt32Register(source, tmpReg), JITTargetPlatform::FPGpr0, tmpReg); - _as->storeDouble(JITTargetPlatform::FPGpr0, _as->loadAddress(tmpReg, target)); + _as->storeDouble(JITTargetPlatform::FPGpr0, target); } void convertIntToBool(IR::Expr *source, IR::Expr *target) @@ -260,8 +260,8 @@ private: void calculateRegistersToSave(const RegisterInformation &used); - template <typename Retval, typename Arg1, typename Arg2, typename Arg3> - void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3) + template <typename Retval, typename Arg1, typename Arg2, typename Arg3, typename Arg4> + void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4) { // Note: using the return value register is intentional: for ABIs where the first parameter // goes into the same register as the return value (currently only ARM), the prepareCall @@ -271,7 +271,7 @@ private: _as->generateFunctionCallImp(true, retval, "lookup getter/setter", typename JITAssembler::LookupCall(lookupAddr, getterSetterOffset), lookupAddr, - arg1, arg2, arg3); + arg1, arg2, arg3, arg4); } template <typename Retval, typename Arg1, typename Arg2> @@ -280,6 +280,12 @@ private: generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, typename JITAssembler::VoidType()); } + template <typename Retval, typename Arg1, typename Arg2, typename Arg3> + void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2, Arg3 arg3) + { + generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, arg3, typename JITAssembler::VoidType()); + } + IR::BasicBlock *_block; BitVector _removableJumps; JITAssembler* _as; diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h index d9f8034b1f..6d788f4a93 100644 --- a/src/qml/jit/qv4targetplatform_p.h +++ b/src/qml/jit/qv4targetplatform_p.h @@ -370,7 +370,7 @@ public: // There are two designated frame-pointer registers on ARM, depending on which instruction set // is used for the subroutine: r7 for Thumb or Thumb2, and r11 for ARM. We assign the constants // accordingly, and assign the locals-register to the "other" register. -#if CPU(ARM_THUMB2) +#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7; static const RegisterID LocalsRegister = JSC::ARMRegisters::r11; #else // Thumbs down @@ -397,7 +397,7 @@ public: << RI(JSC::ARMRegisters::r4, QStringLiteral("r4"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::ARMRegisters::r5, QStringLiteral("r5"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) << RI(JSC::ARMRegisters::r6, QStringLiteral("r6"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#if !CPU(ARM_THUMB2) +#if !CPU(ARM_THUMB2) && !defined(V4_BOOTSTRAP) << RI(JSC::ARMRegisters::r7, QStringLiteral("r7"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) #endif << RI(JSC::ARMRegisters::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) @@ -405,7 +405,7 @@ public: << RI(JSC::ARMRegisters::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) #endif << RI(JSC::ARMRegisters::r10, QStringLiteral("r10"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#if CPU(ARM_THUMB2) +#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) << RI(JSC::ARMRegisters::r11, QStringLiteral("r11"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) #endif << RI(JSC::ARMRegisters::d2, QStringLiteral("d2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp index 76c6457d67..896be07ed5 100644 --- a/src/qml/jit/qv4unop.cpp +++ b/src/qml/jit/qv4unop.cpp @@ -48,7 +48,7 @@ using namespace JIT; #define stringIfy(s) stringIfyx(s) #define setOp(operation) \ do { \ - call = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); \ + call = typename JITAssembler::RuntimeCall(QV4::Runtime::operation); name = "Runtime::" stringIfy(operation); \ needsExceptionCheck = Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h index ab20a2607d..56bd64eec1 100644 --- a/src/qml/jsapi/qjsvalue.h +++ b/src/qml/jsapi/qjsvalue.h @@ -133,9 +133,9 @@ public: bool deleteProperty(const QString &name); bool isCallable() const; - QJSValue call(const QJSValueList &args = QJSValueList()); - QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &args = QJSValueList()); - QJSValue callAsConstructor(const QJSValueList &args = QJSValueList()); + QJSValue call(const QJSValueList &args = QJSValueList()); // ### Qt6: Make const + QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &args = QJSValueList()); // ### Qt6: Make const + QJSValue callAsConstructor(const QJSValueList &args = QJSValueList()); // ### Qt6: Make const #ifdef QT_DEPRECATED QT_DEPRECATED QJSEngine *engine() const; diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 7c1cc92a13..318db4f904 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -48,32 +48,33 @@ DEFINE_OBJECT_VTABLE(ArgumentsObject); void Heap::ArgumentsObject::init(QV4::CallContext *context) { + ExecutionEngine *v4 = context->d()->engine; + Object::init(); fullyCreated = false; - this->context = context->d(); + this->context.set(v4, context->d()); Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable()); - ExecutionEngine *v4 = context->d()->engine; Scope scope(v4); 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())); - *args->propertyData(CalleePropertyIndex + QV4::Object::GetterOffset) = v4->thrower(); - *args->propertyData(CalleePropertyIndex + QV4::Object::SetterOffset) = v4->thrower(); - *args->propertyData(CallerPropertyIndex + QV4::Object::GetterOffset) = v4->thrower(); - *args->propertyData(CallerPropertyIndex + QV4::Object::SetterOffset) = v4->thrower(); + args->setProperty(CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); + args->setProperty(CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); + args->setProperty(CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); + args->setProperty(CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower()); args->arrayReserve(context->argc()); args->arrayPut(0, context->args(), context->argc()); args->d()->fullyCreated = true; } else { Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(context->d()->engine->id_callee())); - *args->propertyData(CalleePropertyIndex) = context->d()->function->asReturnedValue(); + args->setProperty(CalleePropertyIndex, context->d()->function); } Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(context->d()->engine->id_length())); - *args->propertyData(LengthPropertyIndex) = Primitive::fromInt32(context->d()->callData->argc); + args->setProperty(LengthPropertyIndex, Primitive::fromInt32(context->d()->callData->argc)); } void ArgumentsObject::fullyCreate() @@ -89,9 +90,9 @@ void ArgumentsObject::fullyCreate() Scope scope(engine()); Scoped<MemberData> md(scope, d()->mappedArguments); if (numAccessors) { - d()->mappedArguments = md->allocate(engine(), numAccessors); + d()->mappedArguments.set(scope.engine, md->allocate(engine(), numAccessors)); for (uint i = 0; i < numAccessors; ++i) { - d()->mappedArguments->data[i] = context()->callData->args[i]; + d()->mappedArguments->values.set(scope.engine, i, context()->callData->args[i]); arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor); } } @@ -107,22 +108,22 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con fullyCreate(); Scope scope(engine); - Property *pd = arrayData() ? arrayData()->getProperty(index) : 0; ScopedProperty map(scope); PropertyAttributes mapAttrs; + uint numAccessors = qMin(context()->formalParameterCount(), static_cast<uint>(context()->callData->argc)); bool isMapped = false; - uint numAccessors = qMin((int)context()->formalParameterCount(), context()->callData->argc); - if (pd && index < (uint)numAccessors) - isMapped = arrayData()->attributes(index).isAccessor() && - pd->getter() == context()->engine->argumentsAccessors[index].getter(); + if (arrayData() && index < numAccessors && + arrayData()->attributes(index).isAccessor() && + arrayData()->get(index) == context()->engine->argumentsAccessors[index].getter()->asReturnedValue()) + isMapped = true; if (isMapped) { Q_ASSERT(arrayData()); mapAttrs = arrayData()->attributes(index); - map->copy(pd, mapAttrs); + arrayData()->getProperty(index, map, &mapAttrs); setArrayAttributes(index, Attr_Data); - pd = arrayData()->getProperty(index); - pd->value = d()->mappedArguments->data[index]; + ArrayData::Index arrayIndex{ arrayData(), arrayData()->mappedIndex(index) }; + arrayIndex.set(scope.engine, d()->mappedArguments->values[index]); } bool strict = engine->current->strictMode; @@ -140,8 +141,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con if (attrs.isWritable()) { setArrayAttributes(index, mapAttrs); - pd = arrayData()->getProperty(index); - pd->copy(map, mapAttrs); + arrayData()->setProperty(engine, index, map); } } @@ -235,17 +235,6 @@ void ArgumentsSetterFunction::call(const Managed *setter, Scope &scope, CallData scope.result = Encode::undefined(); } -void ArgumentsObject::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - ArgumentsObject::Data *o = static_cast<ArgumentsObject::Data *>(that); - if (o->context) - o->context->mark(e); - if (o->mappedArguments) - o->mappedArguments->mark(e); - - Object::markObjects(that, e); -} - uint ArgumentsObject::getLength(const Managed *m) { const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m); diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index f80ade9611..46e1f884e8 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -59,26 +59,35 @@ namespace QV4 { namespace Heap { -struct ArgumentsGetterFunction : FunctionObject { +#define ArgumentsGetterFunctionMembers(class, Member) \ + Member(class, NoMark, uint, index) + +DECLARE_HEAP_OBJECT(ArgumentsGetterFunction, FunctionObject) { + DECLARE_MARK_TABLE(ArgumentsGetterFunction); inline void init(QV4::ExecutionContext *scope, uint index); - uint index; }; -struct ArgumentsSetterFunction : FunctionObject { +#define ArgumentsSetterFunctionMembers(class, Member) \ + Member(class, NoMark, uint, index) + +DECLARE_HEAP_OBJECT(ArgumentsSetterFunction, FunctionObject) { + DECLARE_MARK_TABLE(ArgumentsSetterFunction); inline void init(QV4::ExecutionContext *scope, uint index); - uint index; }; -struct ArgumentsObject : Object { +#define ArgumentsObjectMembers(class, Member) \ + Member(class, Pointer, CallContext *, context) \ + Member(class, Pointer, MemberData *, mappedArguments) \ + Member(class, NoMark, bool, fullyCreated) + +DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { + DECLARE_MARK_TABLE(ArgumentsObject); enum { LengthPropertyIndex = 0, CalleePropertyIndex = 1, CallerPropertyIndex = 3 }; void init(QV4::CallContext *context); - Pointer<CallContext> context; - bool fullyCreated; - Pointer<MemberData> mappedArguments; }; } @@ -131,7 +140,6 @@ struct ArgumentsObject: Object { static bool putIndexed(Managed *m, uint index, const Value &value); static bool deleteIndexedProperty(Managed *m, uint index); static PropertyAttributes queryIndexed(const Managed *m, uint index); - static void markObjects(Heap::Base *that, ExecutionEngine *e); static uint getLength(const Managed *m); void fullyCreate(); diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index d8a7de5466..4a619858b4 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -50,6 +50,7 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_ON const QV4::VTable QV4::ArrayData::static_vtbl = { 0, + 0, QV4::ArrayData::IsExecutionContext, QV4::ArrayData::IsString, QV4::ArrayData::IsObject, @@ -128,7 +129,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt if (d->type() < Heap::ArrayData::Sparse) { offset = d->d()->offset; - toCopy = d->d()->len; + toCopy = d->d()->values.size; } else { toCopy = d->alloc(); } @@ -149,7 +150,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt Heap::SimpleArrayData *n = scope.engine->memoryManager->allocManaged<SimpleArrayData>(size); n->init(); n->offset = 0; - n->len = d ? d->d()->len : 0; + n->values.size = d ? d->d()->values.size : 0; newData = n; } else { Heap::SparseArrayData *n = scope.engine->memoryManager->allocManaged<SparseArrayData>(size); @@ -158,7 +159,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt } newData->setAlloc(alloc); newData->setType(newType); - newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->d()->arrayData + alloc) : 0); + newData->setAttrs(enforceAttributes ? reinterpret_cast<PropertyAttributes *>(newData->d()->values.values + alloc) : 0); o->setArrayData(newData); if (d) { @@ -170,12 +171,14 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt newData->attrs()[i] = Attr_Data; } - if (toCopy > d->d()->alloc - offset) { - uint copyFromStart = toCopy - (d->d()->alloc - offset); - memcpy(newData->d()->arrayData + toCopy - copyFromStart, d->d()->arrayData, sizeof(Value)*copyFromStart); + if (toCopy > d->d()->values.alloc - offset) { + uint copyFromStart = toCopy - (d->d()->values.alloc - offset); + // no write barrier required here + memcpy(newData->d()->values.values + toCopy - copyFromStart, d->d()->values.values, sizeof(Value)*copyFromStart); toCopy -= copyFromStart; } - memcpy(newData->d()->arrayData, d->d()->arrayData + offset, sizeof(Value)*toCopy); + // no write barrier required here + memcpy(newData->d()->values.values, d->d()->values.values + offset, sizeof(Value)*toCopy); } if (newType != Heap::ArrayData::Sparse) @@ -195,22 +198,22 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt lastFree = &sparse->freeList; storeValue(lastFree, 0); for (uint i = 0; i < toCopy; ++i) { - if (!sparse->arrayData[i].isEmpty()) { + if (!sparse->values[i].isEmpty()) { SparseArrayNode *n = sparse->sparse->insert(i); n->value = i; } else { storeValue(lastFree, i); - sparse->arrayData[i].setEmpty(); - lastFree = &sparse->arrayData[i].rawValueRef(); + sparse->values.values[i].setEmpty(); + lastFree = &sparse->values.values[i].rawValueRef(); } } } - if (toCopy < sparse->alloc) { - for (uint i = toCopy; i < sparse->alloc; ++i) { + if (toCopy < sparse->values.alloc) { + for (uint i = toCopy; i < sparse->values.alloc; ++i) { storeValue(lastFree, i); - sparse->arrayData[i].setEmpty(); - lastFree = &sparse->arrayData[i].rawValueRef(); + sparse->values.values[i].setEmpty(); + lastFree = &sparse->values.values[i].rawValueRef(); } storeValue(lastFree, UINT_MAX); } @@ -233,24 +236,10 @@ void ArrayData::ensureAttributes(Object *o) ArrayData::realloc(o, Heap::ArrayData::Simple, 0, true); } - -void SimpleArrayData::markObjects(Heap::Base *d, ExecutionEngine *e) -{ - Heap::SimpleArrayData *dd = static_cast<Heap::SimpleArrayData *>(d); - uint end = dd->offset + dd->len; - if (end > dd->alloc) { - for (uint i = 0; i < end - dd->alloc; ++i) - dd->arrayData[i].mark(e); - end = dd->alloc; - } - for (uint i = dd->offset; i < end; ++i) - dd->arrayData[i].mark(e); -} - ReturnedValue SimpleArrayData::get(const Heap::ArrayData *d, uint index) { const Heap::SimpleArrayData *dd = static_cast<const Heap::SimpleArrayData *>(d); - if (index >= dd->len) + if (index >= dd->values.size) return Primitive::emptyValue().asReturnedValue(); return dd->data(index).asReturnedValue(); } @@ -258,13 +247,13 @@ ReturnedValue SimpleArrayData::get(const Heap::ArrayData *d, uint index) bool SimpleArrayData::put(Object *o, uint index, const Value &value) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - Q_ASSERT(index >= dd->len || !dd->attrs || !dd->attrs[index].isAccessor()); + Q_ASSERT(index >= dd->values.size || !dd->attrs || !dd->attrs[index].isAccessor()); // ### honour attributes - dd->data(index) = value; - if (index >= dd->len) { + dd->setData(o->engine(), index, value); + if (index >= dd->values.size) { if (dd->attrs) dd->attrs[index] = Attr_Data; - dd->len = index + 1; + dd->values.size = index + 1; } return true; } @@ -272,11 +261,11 @@ bool SimpleArrayData::put(Object *o, uint index, const Value &value) bool SimpleArrayData::del(Object *o, uint index) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (index >= dd->len) + if (index >= dd->values.size) return true; if (!dd->attrs || dd->attrs[index].isConfigurable()) { - dd->data(index) = Primitive::emptyValue(); + dd->setData(o->engine(), index, Primitive::emptyValue()); if (dd->attrs) dd->attrs[index] = Attr_Data; return true; @@ -295,8 +284,8 @@ void SimpleArrayData::push_front(Object *o, const Value *values, uint n) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Q_ASSERT(!dd->attrs); - if (dd->len + n > dd->alloc) { - realloc(o, Heap::ArrayData::Simple, dd->len + n, false); + if (dd->values.size + n > dd->values.alloc) { + realloc(o, Heap::ArrayData::Simple, dd->values.size + n, false); Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Simple); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } @@ -304,70 +293,71 @@ void SimpleArrayData::push_front(Object *o, const Value *values, uint n) dd->offset -= n; // there is enough space left in front } else { // we need to wrap around, so: - dd->offset = dd->alloc - // start at the back, but subtract: + dd->offset = dd->values.alloc - // start at the back, but subtract: (n - dd->offset); // the number of items we can put in the free space at the start of the allocated array } - dd->len += n; + dd->values.size += n; for (uint i = 0; i < n; ++i) - dd->data(i) = values[i].asReturnedValue(); + dd->setData(o->engine(), i, values[i]); } ReturnedValue SimpleArrayData::pop_front(Object *o) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); Q_ASSERT(!dd->attrs); - if (!dd->len) + if (!dd->values.size) return Encode::undefined(); ReturnedValue v = dd->data(0).isEmpty() ? Encode::undefined() : dd->data(0).asReturnedValue(); - dd->offset = (dd->offset + 1) % dd->alloc; - --dd->len; + dd->offset = (dd->offset + 1) % dd->values.alloc; + --dd->values.size; return v; } uint SimpleArrayData::truncate(Object *o, uint newLen) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (dd->len < newLen) + if (dd->values.size < newLen) return newLen; if (!dd->attrs) { - dd->len = newLen; + dd->values.size = newLen; return newLen; } - while (dd->len > newLen) { - if (!dd->data(dd->len - 1).isEmpty() && !dd->attrs[dd->len - 1].isConfigurable()) - return dd->len; - --dd->len; + while (dd->values.size > newLen) { + if (!dd->data(dd->values.size - 1).isEmpty() && !dd->attrs[dd->values.size - 1].isConfigurable()) + return dd->values.size; + --dd->values.size; } - return dd->len; + return dd->values.size; } uint SimpleArrayData::length(const Heap::ArrayData *d) { - return d->len; + return d->values.size; } bool SimpleArrayData::putArray(Object *o, uint index, const Value *values, uint n) { Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (index + n > dd->alloc) { + if (index + n > dd->values.alloc) { reallocate(o, index + n + 1, false); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } - for (uint i = dd->len; i < index; ++i) - dd->data(i) = Primitive::emptyValue(); + QV4::ExecutionEngine *e = o->engine(); + for (uint i = dd->values.size; i < index; ++i) + dd->setData(e, i, Primitive::emptyValue()); for (uint i = 0; i < n; ++i) - dd->data(index + i) = values[i]; - dd->len = qMax(dd->len, index + n); + dd->setData(e, index + i, values[i]); + dd->values.size = qMax(dd->values.size, index + n); return true; } void SparseArrayData::free(Heap::ArrayData *d, uint idx) { Q_ASSERT(d && d->type == Heap::ArrayData::Sparse); - Value *v = d->arrayData + idx; + Value *v = d->values.values + idx; if (d->attrs && d->attrs[idx].isAccessor()) { // double slot, free both. Order is important, so we have a double slot for allocation again afterwards. v[1].setEmpty(Value::fromReturnedValue(d->freeList).emptyValue()); @@ -380,15 +370,6 @@ void SparseArrayData::free(Heap::ArrayData *d, uint idx) d->attrs[idx].clear(); } - -void SparseArrayData::markObjects(Heap::Base *d, ExecutionEngine *e) -{ - Heap::SparseArrayData *dd = static_cast<Heap::SparseArrayData *>(d); - uint l = dd->alloc; - for (uint i = 0; i < l; ++i) - dd->arrayData[i].mark(e); -} - Heap::ArrayData *SparseArrayData::reallocate(Object *o, uint n, bool enforceAttributes) { realloc(o, Heap::ArrayData::Sparse, n, enforceAttributes); @@ -404,32 +385,32 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot) ReturnedValue *last = &dd->freeList; while (1) { if (Value::fromReturnedValue(*last).value() == UINT_MAX) { - reallocate(o, dd->alloc + 2, true); + reallocate(o, dd->values.alloc + 2, true); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); last = &dd->freeList; Q_ASSERT(Value::fromReturnedValue(*last).value() != UINT_MAX); } - Q_ASSERT(dd->arrayData[Value::fromReturnedValue(*last).value()].value() != Value::fromReturnedValue(*last).value()); - if (dd->arrayData[Value::fromReturnedValue(*last).value()].value() == (Value::fromReturnedValue(*last).value() + 1)) { + Q_ASSERT(dd->values[Value::fromReturnedValue(*last).value()].value() != Value::fromReturnedValue(*last).value()); + if (dd->values[Value::fromReturnedValue(*last).value()].value() == (Value::fromReturnedValue(*last).value() + 1)) { // found two slots in a row uint idx = Value::fromReturnedValue(*last).emptyValue(); Value lastV = Value::fromReturnedValue(*last); - lastV.setEmpty(dd->arrayData[lastV.emptyValue() + 1].value()); + lastV.setEmpty(dd->values[lastV.emptyValue() + 1].value()); *last = lastV.rawValue(); dd->attrs[idx] = Attr_Accessor; return idx; } - last = &dd->arrayData[Value::fromReturnedValue(*last).value()].rawValueRef(); + last = &dd->values.values[Value::fromReturnedValue(*last).value()].rawValueRef(); } } else { if (Value::fromReturnedValue(dd->freeList).value() == UINT_MAX) { - reallocate(o, dd->alloc + 1, false); + reallocate(o, dd->values.alloc + 1, false); dd = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } uint idx = Value::fromReturnedValue(dd->freeList).value(); Q_ASSERT(idx != UINT_MAX); - dd->freeList = dd->arrayData[idx].asReturnedValue(); + dd->freeList = dd->values[idx].asReturnedValue(); Q_ASSERT(Value::fromReturnedValue(dd->freeList).isEmpty()); if (dd->attrs) dd->attrs[idx] = Attr_Data; @@ -443,7 +424,7 @@ ReturnedValue SparseArrayData::get(const Heap::ArrayData *d, uint index) index = s->mappedIndex(index); if (index == UINT_MAX) return Primitive::emptyValue().asReturnedValue(); - return s->arrayData[index].asReturnedValue(); + return s->values[index].asReturnedValue(); } bool SparseArrayData::put(Object *o, uint index, const Value &value) @@ -457,7 +438,7 @@ bool SparseArrayData::put(Object *o, uint index, const Value &value) if (n->value == UINT_MAX) n->value = allocate(o); s = o->d()->arrayData.cast<Heap::SparseArrayData>(); - s->arrayData[n->value] = value; + s->setArrayData(o->engine(), n->value, value); if (s->attrs) s->attrs[n->value] = Attr_Data; return true; @@ -472,7 +453,7 @@ bool SparseArrayData::del(Object *o, uint index) return true; uint pidx = n->value; - Q_ASSERT(!dd->arrayData[pidx].isEmpty()); + Q_ASSERT(!dd->values[pidx].isEmpty()); bool isAccessor = false; if (dd->attrs) { @@ -485,11 +466,11 @@ bool SparseArrayData::del(Object *o, uint index) if (isAccessor) { // free up both indices - dd->arrayData[pidx + 1].setEmpty(Value::fromReturnedValue(dd->freeList).emptyValue()); - dd->arrayData[pidx].setEmpty(pidx + 1); + dd->values.values[pidx + 1].setEmpty(Value::fromReturnedValue(dd->freeList).emptyValue()); + dd->values.values[pidx].setEmpty(pidx + 1); } else { Q_ASSERT(dd->type == Heap::ArrayData::Sparse); - dd->arrayData[pidx].setEmpty(Value::fromReturnedValue(dd->freeList).emptyValue()); + dd->values.values[pidx].setEmpty(Value::fromReturnedValue(dd->freeList).emptyValue()); } dd->freeList = Primitive::emptyValue(pidx).asReturnedValue(); @@ -518,10 +499,10 @@ void SparseArrayData::push_front(Object *o, const Value *values, uint n) { Heap::SparseArrayData *d = o->d()->arrayData.cast<Heap::SparseArrayData>(); Q_ASSERT(!d->attrs); - for (int i = n - 1; i >= 0; --i) { + for (int i = static_cast<int>(n) - 1; i >= 0; --i) { uint idx = allocate(o); d = o->d()->arrayData.cast<Heap::SparseArrayData>(); - d->arrayData[idx] = values[i]; + d->setArrayData(o->engine(), idx, values[i]); d->sparse->push_front(idx); } } @@ -533,7 +514,7 @@ ReturnedValue SparseArrayData::pop_front(Object *o) uint idx = d->sparse->pop_front(); ReturnedValue v; if (idx != UINT_MAX) { - v = d->arrayData[idx].asReturnedValue(); + v = d->values[idx].asReturnedValue(); free(o->arrayData(), idx); } else { v = Encode::undefined(); @@ -611,24 +592,24 @@ uint ArrayData::append(Object *obj, ArrayObject *otherObj, uint n) ScopedValue v(scope); for (const SparseArrayNode *it = os->sparse->begin(); it != os->sparse->end(); it = it->nextNode()) { - v = otherObj->getValue(os->arrayData[it->value], other->d()->attrs[it->value]); + v = otherObj->getValue(os->values[it->value], other->d()->attrs[it->value]); obj->arraySet(oldSize + it->key(), v); } } else { for (const SparseArrayNode *it = other->d()->sparse->begin(); it != os->sparse->end(); it = it->nextNode()) - obj->arraySet(oldSize + it->key(), os->arrayData[it->value]); + obj->arraySet(oldSize + it->key(), os->values[it->value]); } } else { Heap::SimpleArrayData *os = static_cast<Heap::SimpleArrayData *>(other->d()); uint toCopy = n; uint chunk = toCopy; - if (chunk > os->alloc - os->offset) - chunk -= os->alloc - os->offset; - obj->arrayPut(oldSize, os->arrayData + os->offset, chunk); + if (chunk > os->values.alloc - os->offset) + chunk -= os->values.alloc - os->offset; + obj->arrayPut(oldSize, os->values.data() + os->offset, chunk); toCopy -= chunk; if (toCopy) - obj->arrayPut(oldSize + chunk, os->arrayData, toCopy); + obj->arrayPut(oldSize + chunk, os->values.data(), toCopy); } return oldSize + n; @@ -638,18 +619,18 @@ void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor) { if (!isAccessor && o->d()->arrayData->type != Heap::ArrayData::Sparse) { Heap::SimpleArrayData *d = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (index < 0x1000 || index < d->len + (d->len >> 2)) { - if (index >= d->alloc) { + if (index < 0x1000 || index < d->values.size + (d->values.size >> 2)) { + if (index >= d->values.alloc) { o->arrayReserve(index + 1); d = o->d()->arrayData.cast<Heap::SimpleArrayData>(); } - if (index >= d->len) { + if (index >= d->values.size) { // mark possible hole in the array - for (uint i = d->len; i < index; ++i) - d->data(i) = Primitive::emptyValue(); - d->len = index + 1; + for (uint i = d->values.size; i < index; ++i) + d->setData(o->engine(), i, Primitive::emptyValue()); + d->values.size = index + 1; } - d->arrayData[d->mappedIndex(index)] = *v; + d->setData(o->engine(), index, *v); return; } } @@ -660,9 +641,9 @@ void ArrayData::insert(Object *o, uint index, const Value *v, bool isAccessor) if (n->value == UINT_MAX) n->value = SparseArrayData::allocate(o, isAccessor); s = o->d()->arrayData.cast<Heap::SparseArrayData>(); - s->arrayData[n->value] = *v; + s->setArrayData(o->engine(), n->value, *v); if (isAccessor) - s->arrayData[n->value + Object::SetterOffset] = v[Object::SetterOffset]; + s->setArrayData(o->engine(), n->value + Object::SetterOffset, v[Object::SetterOffset]); } @@ -697,7 +678,7 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const callData->thisObject = Primitive::undefinedValue(); callData->args[0] = v1; callData->args[1] = v2; - result = scope.engine->runtime.callValue(scope.engine, m_comparefn, callData); + result = QV4::Runtime::method_callValue(scope.engine, m_comparefn, callData); return result->toNumber() < 0; } @@ -799,7 +780,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c break; PropertyAttributes a = sparse->attrs() ? sparse->attrs()[n->value] : Attr_Data; - d->data(i) = thisObject->getValue(sparse->arrayData()[n->value], a); + d->setData(engine, i, Value::fromReturnedValue(thisObject->getValue(sparse->arrayData()[n->value], a))); d->attrs[i] = a.isAccessor() ? Attr_Data : a; n = n->nextNode(); @@ -809,12 +790,12 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c while (n != sparse->sparse()->end()) { if (n->value >= len) break; - d->data(i) = sparse->arrayData()[n->value]; + d->setData(engine, i, sparse->arrayData()[n->value]); n = n->nextNode(); ++i; } } - d->len = i; + d->values.size = i; if (len > i) len = i; if (n != sparse->sparse()->end()) { @@ -822,7 +803,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c thisObject->initSparseArray(); while (n != sparse->sparse()->end()) { PropertyAttributes a = sparse->attrs() ? sparse->attrs()[n->value] : Attr_Data; - thisObject->arraySet(n->value, reinterpret_cast<Property *>(sparse->arrayData() + n->value), a); + thisObject->arraySet(n->value, reinterpret_cast<const Property *>(sparse->arrayData() + n->value), a); n = n->nextNode(); } @@ -830,8 +811,8 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c } } else { Heap::SimpleArrayData *d = thisObject->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (len > d->len) - len = d->len; + if (len > d->values.size) + len = d->values.size; // sort empty values to the end for (uint i = 0; i < len; i++) { @@ -840,8 +821,8 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c if (!d->data(len).isEmpty()) break; Q_ASSERT(!d->attrs || !d->attrs[len].isAccessor()); - d->data(i) = d->data(len); - d->data(len) = Primitive::emptyValue(); + d->setData(engine, i, d->data(len)); + d->setData(engine, len, Primitive::emptyValue()); } } @@ -852,7 +833,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c ArrayElementLessThan lessThan(engine, thisObject, comparefn); - Value *begin = thisObject->arrayData()->arrayData; + Value *begin = thisObject->arrayData()->values.values; sortHelper(begin, begin + len, *begin, lessThan); #ifdef CHECK_SPARSE_ARRAYS diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index 24b948f01e..c2c81e886b 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -90,27 +90,31 @@ struct ArrayVTable namespace Heap { -struct ArrayData : public Base { - enum Type { - Simple = 0, - Complex = 1, - Sparse = 2, - Custom = 3 +#define ArrayDataMembers(class, Member) \ + Member(class, NoMark, uint, type) \ + Member(class, NoMark, uint, offset) \ + Member(class, NoMark, PropertyAttributes *, attrs) \ + Member(class, NoMark, ReturnedValue, freeList) \ + Member(class, NoMark, SparseArray *, sparse) \ + Member(class, ValueArray, ValueArray, values) + +DECLARE_HEAP_OBJECT(ArrayData, Base) { + DECLARE_MARK_TABLE(ArrayData); + + enum Type { Simple = 0, Complex = 1, Sparse = 2, Custom = 3 }; + + struct Index { + Heap::ArrayData *arrayData; + uint index; + + void set(ExecutionEngine *e, Value newVal) { + arrayData->values.set(e, index, newVal); + } + const Value *operator->() const { return &arrayData->values[index]; } + const Value &operator*() const { return arrayData->values[index]; } + bool isNull() const { return !arrayData; } }; - uint alloc; - Type type; - PropertyAttributes *attrs; - union { - uint len; - ReturnedValue freeList; - }; - union { - uint offset; - SparseArray *sparse; - }; - Value arrayData[1]; - bool isSparse() const { return type == Sparse; } const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable()); } @@ -118,35 +122,32 @@ struct ArrayData : public Base { inline ReturnedValue get(uint i) const { return vtable()->get(this, i); } - inline void getProperty(uint index, Property *p, PropertyAttributes *attrs); - inline void setProperty(uint index, const Property *p); - inline Property *getProperty(uint index); - inline Value *getValueOrSetter(uint index, PropertyAttributes *attrs); + inline bool getProperty(uint index, Property *p, PropertyAttributes *attrs); + inline void setProperty(ExecutionEngine *e, uint index, const Property *p); + inline Index getValueOrSetter(uint index, PropertyAttributes *attrs); inline PropertyAttributes attributes(uint i) const; bool isEmpty(uint i) const { return get(i) == Primitive::emptyValue().asReturnedValue(); } - inline ReturnedValue length() const { + inline uint length() const { return vtable()->length(this); } + void setArrayData(ExecutionEngine *e, uint index, Value newVal) { + values.set(e, index, newVal); + } + + uint mappedIndex(uint index) const; }; V4_ASSERT_IS_TRIVIAL(ArrayData) struct SimpleArrayData : public ArrayData { - uint mappedIndex(uint index) const { return (index + offset) % alloc; } - Value data(uint index) const { return arrayData[mappedIndex(index)]; } - Value &data(uint index) { return arrayData[mappedIndex(index)]; } - - Property *getProperty(uint index) { - if (index >= len) - return 0; - index = mappedIndex(index); - if (arrayData[index].isEmpty()) - return 0; - return reinterpret_cast<Property *>(arrayData + index); + 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) { + values.set(e, mappedIndex(index), newVal); } PropertyAttributes attributes(uint i) const { @@ -168,13 +169,6 @@ struct SparseArrayData : public ArrayData { return n->value; } - Property *getProperty(uint index) { - SparseArrayNode *n = sparse->findNode(index); - if (!n) - return 0; - return reinterpret_cast<Property *>(arrayData + n->value); - } - PropertyAttributes attributes(uint i) const { if (!attrs) return Attr_Data; @@ -189,16 +183,23 @@ struct Q_QML_EXPORT ArrayData : public Managed { typedef Heap::ArrayData::Type Type; V4_MANAGED(ArrayData, Managed) + enum { + IsArrayData = true + }; - uint alloc() const { return d()->alloc; } - uint &alloc() { return d()->alloc; } - void setAlloc(uint a) { d()->alloc = a; } - Type type() const { return d()->type; } + typedef Heap::ArrayData::Index Index; + + uint alloc() const { return d()->values.alloc; } + uint &alloc() { return d()->values.alloc; } + void setAlloc(uint a) { d()->values.alloc = a; } + Type type() const { return static_cast<Type>(d()->type); } void setType(Type t) { d()->type = t; } PropertyAttributes *attrs() const { return d()->attrs; } void setAttrs(PropertyAttributes *a) { d()->attrs = a; } - const Value *arrayData() const { return &d()->arrayData[0]; } - Value *arrayData() { return &d()->arrayData[0]; } + const Value *arrayData() const { return d()->values.data(); } + void setArrayData(ExecutionEngine *e, uint index, Value newVal) { + d()->setArrayData(e, index, newVal); + } const ArrayVTable *vtable() const { return d()->vtable(); } bool isSparse() const { return type() == Heap::ArrayData::Sparse; } @@ -221,9 +222,6 @@ struct Q_QML_EXPORT ArrayData : public Managed ReturnedValue get(uint i) const { return d()->get(i); } - inline Property *getProperty(uint index) { - return d()->getProperty(index); - } static void ensureAttributes(Object *o); static void realloc(Object *o, Type newType, uint alloc, bool enforceAttributes); @@ -239,15 +237,12 @@ struct Q_QML_EXPORT SimpleArrayData : public ArrayData uint mappedIndex(uint index) const { return d()->mappedIndex(index); } Value data(uint index) const { return d()->data(index); } - Value &data(uint index) { return d()->data(index); } - uint &len() { return d()->len; } - uint len() const { return d()->len; } + uint &len() { return d()->values.size; } + uint len() const { return d()->values.size; } static Heap::ArrayData *reallocate(Object *o, uint n, bool enforceAttributes); - static void markObjects(Heap::Base *d, ExecutionEngine *e); - static ReturnedValue get(const Heap::ArrayData *d, uint index); static bool put(Object *o, uint index, const Value &value); static bool putArray(Object *o, uint index, const Value *values, uint n); @@ -274,8 +269,6 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData uint mappedIndex(uint index) const { return d()->mappedIndex(index); } - static void markObjects(Heap::Base *d, ExecutionEngine *e); - static Heap::ArrayData *reallocate(Object *o, uint n, bool enforceAttributes); static ReturnedValue get(const Heap::ArrayData *d, uint index); static bool put(Object *o, uint index, const Value &value); @@ -290,30 +283,38 @@ struct Q_QML_EXPORT SparseArrayData : public ArrayData namespace Heap { -void ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs) +inline uint ArrayData::mappedIndex(uint index) const { - Property *pd = getProperty(index); - Q_ASSERT(pd); - *attrs = attributes(index); - p->value = pd->value; - if (attrs->isAccessor()) - p->set = pd->set; + if (isSparse()) + return static_cast<const SparseArrayData *>(this)->mappedIndex(index); + if (index >= values.size) + return UINT_MAX; + uint idx = static_cast<const SimpleArrayData *>(this)->mappedIndex(index); + return values[idx].isEmpty() ? UINT_MAX : idx; } -void ArrayData::setProperty(uint index, const Property *p) +bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs) { - Property *pd = getProperty(index); - Q_ASSERT(pd); - pd->value = p->value; - if (attributes(index).isAccessor()) - pd->set = p->set; + uint mapped = mappedIndex(index); + if (mapped == UINT_MAX) { + *attrs = Attr_Invalid; + return false; + } + + *attrs = attributes(index); + p->value = *(Index{ this, mapped }); + if (attrs->isAccessor()) + p->set = *(Index{ this, mapped + 1 /*Object::SetterOffset*/ }); + return true; } -inline Property *ArrayData::getProperty(uint index) +void ArrayData::setProperty(QV4::ExecutionEngine *e, uint index, const Property *p) { - if (isSparse()) - return static_cast<SparseArrayData *>(this)->getProperty(index); - return static_cast<SimpleArrayData *>(this)->getProperty(index); + uint mapped = mappedIndex(index); + Q_ASSERT(mapped != UINT_MAX); + values.set(e, mapped, p->value); + if (attributes(index).isAccessor()) + values.set(e, mapped + 1 /*QV4::Object::SetterOffset*/, p->set); } inline PropertyAttributes ArrayData::attributes(uint i) const @@ -323,16 +324,16 @@ inline PropertyAttributes ArrayData::attributes(uint i) const return static_cast<const SimpleArrayData *>(this)->attributes(i); } -Value *ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs) +ArrayData::Index ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs) { - Property *p = getProperty(index); - if (!p) { + uint idx = mappedIndex(index); + if (idx == UINT_MAX) { *attrs = Attr_Invalid; - return 0; + return { 0, 0 }; } *attrs = attributes(index); - return attrs->isAccessor() ? &p->set : &p->value; + return { this, attrs->isAccessor() ? idx + 1 /* QV4::Object::SetterOffset*/ : idx }; } diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index 759354f4e2..a2c19e1f2d 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -690,8 +690,8 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD } else { Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple || instance->arrayType() == Heap::ArrayData::Complex); Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (len > sa->len) - len = sa->len; + if (len > sa->values.size) + len = sa->values.size; uint idx = fromIndex; while (idx < len) { value = sa->data(idx); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 740ebbe359..02d3af619e 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -54,58 +54,49 @@ using namespace QV4; DEFINE_MANAGED_VTABLE(ExecutionContext); +DEFINE_MANAGED_VTABLE(SimpleCallContext); DEFINE_MANAGED_VTABLE(CallContext); DEFINE_MANAGED_VTABLE(WithContext); DEFINE_MANAGED_VTABLE(CatchContext); DEFINE_MANAGED_VTABLE(GlobalContext); -/* Function *f, int argc */ -#define requiredMemoryForExecutionContect(f, argc) \ - ((sizeof(CallContext::Data) + 7) & ~7) + \ - sizeof(Value) * (f->compiledFunction->nLocals + qMax((uint)argc, f->nFormals)) + sizeof(CallData) - Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData *callData) { - Heap::CallContext *c = d()->engine->memoryManager->allocManaged<CallContext>( - requiredMemoryForExecutionContect(function, callData->argc)); + 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); c->v4Function = function; c->strictMode = function->isStrict(); - c->outer = this->d(); - - c->activation = 0; + c->outer.set(d()->engine, this->d()); c->compilationUnit = function->compilationUnit; - c->lookups = c->compilationUnit->runtimeLookups; - c->constantTable = c->compilationUnit->constants; - c->locals = (Value *)((quintptr(c + 1) + 7) & ~7); + c->lookups = function->compilationUnit->runtimeLookups; + c->constantTable = function->compilationUnit->constants; const CompiledData::Function *compiledFunction = function->compiledFunction; - int nLocals = compiledFunction->nLocals; + uint nLocals = compiledFunction->nLocals; + c->locals.size = nLocals; + c->locals.alloc = localsAndFormals; +#if QT_POINTER_SIZE == 8 + // memory allocated from the JS heap is 0 initialized, so skip the std::fill() below + Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0); +#else if (nLocals) - std::fill(c->locals, c->locals + nLocals, Primitive::undefinedValue()); + std::fill(c->locals.values, c->locals.values + nLocals, Primitive::undefinedValue()); +#endif - c->callData = reinterpret_cast<CallData *>(c->locals + nLocals); - ::memcpy(c->callData, callData, sizeof(CallData) + (callData->argc - 1) * sizeof(Value)); + c->callData = reinterpret_cast<CallData *>(c->locals.values + nLocals); + ::memcpy(c->callData, callData, sizeof(CallData) - sizeof(Value) + static_cast<uint>(callData->argc) * sizeof(Value)); if (callData->argc < static_cast<int>(compiledFunction->nFormals)) std::fill(c->callData->args + c->callData->argc, c->callData->args + compiledFunction->nFormals, Primitive::undefinedValue()); return c; } -Heap::CallContext *Heap::CallContext::createSimpleContext(ExecutionEngine *v4) -{ - Heap::CallContext *ctxt = v4->memoryManager->allocSimpleCallContext(v4); - return ctxt; -} - -void Heap::CallContext::freeSimpleCallContext() -{ - engine->memoryManager->freeSimpleCallContext(); -} - Heap::WithContext *ExecutionContext::newWithContext(Heap::Object *with) { return d()->engine->memoryManager->alloc<WithContext>(d(), with); @@ -129,10 +120,10 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) switch (ctx->d()->type) { case Heap::ExecutionContext::Type_CallContext: case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); + Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); if (!activation) { if (!c->activation) - c->activation = scope.engine->newObject(); + c->activation.set(scope.engine, scope.engine->newObject()); activation = c->activation; } break; @@ -166,41 +157,52 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) void Heap::GlobalContext::init(ExecutionEngine *eng) { Heap::ExecutionContext::init(eng, Heap::ExecutionContext::Type_GlobalContext); - global = eng->globalObject->d(); + 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 = outerContext; + outer.set(engine, outerContext); strictMode = outer->strictMode; callData = outer->callData; lookups = outer->lookups; constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; - this->exceptionVarName = exceptionVarName; - this->exceptionValue = exceptionValue; + this->exceptionVarName.set(engine, exceptionVarName); + this->exceptionValue.set(engine, exceptionValue); } +void Heap::WithContext::init(ExecutionContext *outerContext, Object *with) +{ + Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_WithContext); + outer.set(engine, outerContext); + callData = outer->callData; + lookups = outer->lookups; + constantTable = outer->constantTable; + compilationUnit = outer->compilationUnit; + + withObject.set(engine, with); +} -Identifier * const *CallContext::formals() const +Identifier * const *SimpleCallContext::formals() const { return d()->v4Function ? d()->v4Function->internalClass->nameMap.constData() : 0; } -unsigned int CallContext::formalCount() const +unsigned int SimpleCallContext::formalCount() const { return d()->v4Function ? d()->v4Function->nFormals : 0; } -Identifier * const *CallContext::variables() const +Identifier * const *SimpleCallContext::variables() const { return d()->v4Function ? d()->v4Function->internalClass->nameMap.constData() + d()->v4Function->nFormals : 0; } -unsigned int CallContext::variableCount() const +unsigned int SimpleCallContext::variableCount() const { return d()->v4Function ? d()->v4Function->compiledFunction->nLocals : 0; } @@ -210,7 +212,6 @@ unsigned int CallContext::variableCount() const bool ExecutionContext::deleteProperty(String *name) { Scope scope(this); - bool hasWith = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { switch (ctx->d()->type) { @@ -221,7 +222,6 @@ bool ExecutionContext::deleteProperty(String *name) break; } case Heap::ExecutionContext::Type_WithContext: { - hasWith = true; ScopedObject withObject(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); if (withObject->hasProperty(name)) return withObject->deleteProperty(name); @@ -233,15 +233,16 @@ bool ExecutionContext::deleteProperty(String *name) return global->deleteProperty(name); break; } - case Heap::ExecutionContext::Type_CallContext: - case Heap::ExecutionContext::Type_SimpleCallContext: { + case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - if (c->v4Function && (c->v4Function->needsActivation() || hasWith)) { - uint index = c->v4Function->internalClass->find(name); - if (index < UINT_MAX) - // ### throw in strict mode? - return false; - } + uint index = c->v4Function->internalClass->find(name); + if (index < UINT_MAX) + // ### throw in strict mode? + return false; + Q_FALLTHROUGH(); + } + case Heap::ExecutionContext::Type_SimpleCallContext: { + Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); ScopedObject qml(scope, c->activation); if (qml && qml->hasProperty(name)) return qml->deleteProperty(name); @@ -258,61 +259,6 @@ bool ExecutionContext::deleteProperty(String *name) return true; } -bool CallContext::needsOwnArguments() const -{ - QV4::Function *f = d()->v4Function; - return (f && f->needsActivation()) || (argc() < (f ? static_cast<int>(f->nFormals) : 0)); -} - -void ExecutionContext::markObjects(Heap::Base *m, ExecutionEngine *engine) -{ - ExecutionContext::Data *ctx = static_cast<ExecutionContext::Data *>(m); - - if (ctx->outer) - ctx->outer->mark(engine); - - switch (ctx->type) { - case Heap::ExecutionContext::Type_CatchContext: { - CatchContext::Data *c = static_cast<CatchContext::Data *>(ctx); - c->exceptionVarName->mark(engine); - c->exceptionValue.mark(engine); - break; - } - case Heap::ExecutionContext::Type_WithContext: { - WithContext::Data *w = static_cast<WithContext::Data *>(ctx); - if (w->withObject) - w->withObject->mark(engine); - break; - } - case Heap::ExecutionContext::Type_GlobalContext: { - GlobalContext::Data *g = static_cast<GlobalContext::Data *>(ctx); - g->global->mark(engine); - break; - } - case Heap::ExecutionContext::Type_SimpleCallContext: - break; - case Heap::ExecutionContext::Type_CallContext: { - QV4::Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); - Q_ASSERT(c->v4Function); - ctx->callData->thisObject.mark(engine); - for (int arg = 0; arg < qMax(ctx->callData->argc, (int)c->v4Function->nFormals); ++arg) - ctx->callData->args[arg].mark(engine); - for (unsigned local = 0, lastLocal = c->v4Function->compiledFunction->nLocals; local < lastLocal; ++local) - c->locals[local].mark(engine); - if (c->activation) - c->activation->mark(engine); - if (c->function) - c->function->mark(engine); - break; - } - case Heap::ExecutionContext::Type_QmlContext: { - QmlContext::Data *g = static_cast<QmlContext::Data *>(ctx); - g->qml->mark(engine); - break; - } - } -} - // Do a standard call with this execution context as the outer scope void ExecutionContext::call(Scope &scope, CallData *callData, Function *function, const FunctionObject *f) { @@ -320,7 +266,7 @@ void ExecutionContext::call(Scope &scope, CallData *callData, Function *function Scoped<CallContext> ctx(scope, newCallContext(function, callData)); if (f) - ctx->d()->function = f->d(); + ctx->d()->function.set(scope.engine, f->d()); scope.engine->pushContext(ctx); scope.result = Q_V4_PROFILE(scope.engine, function); @@ -336,7 +282,7 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio ExecutionContextSaver ctxSaver(scope); - CallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(scope.engine); + SimpleCallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(scope.engine); ctx->strictMode = function->isStrict(); ctx->callData = callData; @@ -344,8 +290,7 @@ void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Functio ctx->compilationUnit = function->compilationUnit; ctx->lookups = function->compilationUnit->runtimeLookups; ctx->constantTable = function->compilationUnit->constants; - ctx->outer = this->d(); - ctx->locals = scope.alloc(function->compiledFunction->nLocals); + ctx->outer.set(scope.engine, this->d()); for (int i = callData->argc; i < (int)function->nFormals; ++i) callData->args[i] = Encode::undefined(); @@ -371,7 +316,7 @@ void ExecutionContext::setProperty(String *name, const Value &value) case Heap::ExecutionContext::Type_CatchContext: { Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); if (c->exceptionVarName->isEqualTo(name->d())) { - c->exceptionValue = value; + c->exceptionValue.set(scope.engine, value); return; } break; @@ -390,15 +335,16 @@ void ExecutionContext::setProperty(String *name, const Value &value) } case Heap::ExecutionContext::Type_CallContext: case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); + Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); if (c->v4Function) { uint index = c->v4Function->internalClass->find(name); if (index < UINT_MAX) { if (index < c->v4Function->nFormals) { c->callData->args[c->v4Function->nFormals - index - 1] = value; } else { + Q_ASSERT(c->type == Heap::ExecutionContext::Type_CallContext); index -= c->v4Function->nFormals; - c->locals[index] = value; + static_cast<Heap::CallContext *>(c)->locals.set(scope.engine, index, value); } return; } @@ -439,13 +385,10 @@ ReturnedValue ExecutionContext::getProperty(String *name) if (name->equals(d()->engine->id_this())) return thisObject().asReturnedValue(); - bool hasWith = false; - bool hasCatchScope = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { switch (ctx->d()->type) { case Heap::ExecutionContext::Type_CatchContext: { - hasCatchScope = true; Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); if (c->exceptionVarName->isEqualTo(name->d())) return c->exceptionValue.asReturnedValue(); @@ -453,7 +396,6 @@ ReturnedValue ExecutionContext::getProperty(String *name) } case Heap::ExecutionContext::Type_WithContext: { ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); - hasWith = true; bool hasProperty = false; v = w->get(name, &hasProperty); if (hasProperty) { @@ -469,17 +411,23 @@ ReturnedValue ExecutionContext::getProperty(String *name) return v->asReturnedValue(); break; } - case Heap::ExecutionContext::Type_CallContext: - case Heap::ExecutionContext::Type_SimpleCallContext: { + case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - if (c->v4Function && (c->v4Function->needsActivation() || hasWith || hasCatchScope)) { - uint index = c->v4Function->internalClass->find(name); - 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(); - } + uint index = c->v4Function->internalClass->find(name); + 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(); + } + 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; @@ -487,9 +435,6 @@ ReturnedValue ExecutionContext::getProperty(String *name) if (hasProperty) return v->asReturnedValue(); } - if (c->function && c->v4Function->isNamedExpression() - && name->equals(ScopedString(scope, c->v4Function->name()))) - return c->function->asReturnedValue(); break; } case Heap::ExecutionContext::Type_QmlContext: { @@ -516,13 +461,10 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) if (name->equals(d()->engine->id_this())) return thisObject().asReturnedValue(); - bool hasWith = false; - bool hasCatchScope = false; ScopedContext ctx(scope, this); for (; ctx; ctx = ctx->d()->outer) { switch (ctx->d()->type) { case Heap::ExecutionContext::Type_CatchContext: { - hasCatchScope = true; Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); if (c->exceptionVarName->isEqualTo(name->d())) return c->exceptionValue.asReturnedValue(); @@ -530,7 +472,6 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) } case Heap::ExecutionContext::Type_WithContext: { ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); - hasWith = true; bool hasProperty = false; v = w->get(name, &hasProperty); if (hasProperty) { @@ -547,17 +488,22 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) return v->asReturnedValue(); break; } - case Heap::ExecutionContext::Type_CallContext: - case Heap::ExecutionContext::Type_SimpleCallContext: { + case Heap::ExecutionContext::Type_CallContext: { Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); - if (c->v4Function && (c->v4Function->needsActivation() || hasWith || hasCatchScope)) { - uint index = c->v4Function->internalClass->find(name); - 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(); - } + uint index = c->v4Function->internalClass->find(name); + 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(); + } + 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; @@ -565,9 +511,6 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) if (hasProperty) return v->asReturnedValue(); } - if (c->function && c->v4Function->isNamedExpression() - && name->equals(ScopedString(scope, c->v4Function->name()))) - return c->function->asReturnedValue(); break; } case Heap::ExecutionContext::Type_QmlContext: { @@ -591,7 +534,7 @@ Function *ExecutionContext::getFunction() const Scope scope(d()->engine); ScopedContext it(scope, this->d()); for (; it; it = it->d()->outer) { - if (const CallContext *callCtx = it->asCallContext()) + if (const SimpleCallContext *callCtx = it->asSimpleCallContext()) return callCtx->d()->v4Function; else if (it->asCatchContext() || it->asWithContext()) continue; // look in the parent context for a FunctionObject diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index bcfee2e1f8..3b37ea69dc 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -61,18 +61,21 @@ class QQmlContextData; namespace QV4 { namespace CompiledData { -struct CompilationUnit; +struct CompilationUnitBase; struct Function; } struct Function; struct Identifier; struct CallContext; +struct SimpleCallContext; struct CatchContext; struct WithContext; struct QmlContext; struct QmlContextWrapper; +// Attention: Make sure that this structure is the same size on 32-bit and 64-bit +// architecture or you'll have to change the JIT code. struct CallData { // below is to be compatible with Value. Initialize tag to 0 @@ -91,11 +94,27 @@ struct CallData Value args[1]; }; +Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); +Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 8); +Q_STATIC_ASSERT(offsetof(CallData, args) == 16); + namespace Heap { struct QmlContext; -struct ExecutionContext : Base { +#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) \ + Member(class, NoMark, CompiledData::CompilationUnitBase *, compilationUnit) \ + Member(class, NoMark, int, lineNumber) // as member of non-pointer size this has to come last to preserve the ability to + // translate offsetof of it between 64-bit and 32-bit. + +DECLARE_HEAP_OBJECT(ExecutionContext, Base) { + DECLARE_MARK_TABLE(ExecutionContext); + enum ContextType { Type_GlobalContext = 0x1, Type_CatchContext = 0x2, @@ -114,23 +133,32 @@ struct ExecutionContext : Base { lineNumber = -1; } - CallData *callData; - - ExecutionEngine *engine; - Pointer<ExecutionContext> outer; - Lookup *lookups; - const QV4::Value *constantTable; - CompiledData::CompilationUnit *compilationUnit; - - ContextType type : 8; + quint8 type; bool strictMode : 8; - int lineNumber; +#if QT_POINTER_SIZE == 8 + quint8 padding_[6]; +#else + quint8 padding_[2]; +#endif }; V4_ASSERT_IS_TRIVIAL(ExecutionContext) +Q_STATIC_ASSERT(sizeof(ExecutionContext) == sizeof(Base) + sizeof(ExecutionContextData) + QT_POINTER_SIZE); + +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, 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); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, lineNumber) == offsetof(ExecutionContextData, compilationUnit) + QT_POINTER_SIZE); -struct CallContext : ExecutionContext { - static CallContext *createSimpleContext(ExecutionEngine *v4); - void freeSimpleCallContext(); +#define SimpleCallContextMembers(class, Member) \ + Member(class, Pointer, Object *, activation) \ + Member(class, NoMark, QV4::Function *, v4Function) + +DECLARE_HEAP_OBJECT(SimpleCallContext, ExecutionContext) { + DECLARE_MARK_TABLE(SimpleCallContext); void init(ExecutionEngine *engine, ContextType t = Type_SimpleCallContext) { @@ -139,40 +167,66 @@ struct CallContext : ExecutionContext { inline unsigned int formalParameterCount() const; - Pointer<FunctionObject> function; - QV4::Function *v4Function; - Value *locals; - Pointer<Object> activation; }; -V4_ASSERT_IS_TRIVIAL(CallContext) +V4_ASSERT_IS_TRIVIAL(SimpleCallContext) +Q_STATIC_ASSERT(std::is_standard_layout<SimpleCallContextData>::value); +Q_STATIC_ASSERT(offsetof(SimpleCallContextData, activation) == 0); +Q_STATIC_ASSERT(offsetof(SimpleCallContextData, v4Function) == offsetof(SimpleCallContextData, activation) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(sizeof(SimpleCallContextData) == 2 * QT_POINTER_SIZE); +Q_STATIC_ASSERT(sizeof(SimpleCallContext) == sizeof(ExecutionContext) + sizeof(SimpleCallContextData)); + +#if QT_POINTER_SIZE == 8 +#define CallContextMembers(class, Member) \ + Member(class, Pointer, FunctionObject *, function) \ + Member(class, ValueArray, ValueArray, locals) +#else +#define CallContextMembers(class, Member) \ + Member(class, Pointer, FunctionObject *, function) \ + Member(class, NoMark, void *, padding) \ + Member(class, ValueArray, ValueArray, locals) +#endif + +DECLARE_HEAP_OBJECT(CallContext, SimpleCallContext) { + DECLARE_MARK_TABLE(CallContext); + + using SimpleCallContext::formalParameterCount; +}; + +Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value); +Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0); +// IMPORTANT: we cannot do offsetof(CallContextData, locals) in the JIT as the offset does not scale with +// the pointer size. On 32-bit ARM the offset of the ValueArray is aligned to 8 bytes, on 32-bit x86 for +// example it is not. Therefore we have a padding in place and always have a distance of 8 bytes. +Q_STATIC_ASSERT(offsetof(CallContextData, locals) == offsetof(CallContextData, function) + 8); + +#define GlobalContextMembers(class, Member) \ + Member(class, Pointer, Object *, global) + +DECLARE_HEAP_OBJECT(GlobalContext, ExecutionContext) { + DECLARE_MARK_TABLE(GlobalContext); -struct GlobalContext : ExecutionContext { void init(ExecutionEngine *engine); - Pointer<Object> global; }; V4_ASSERT_IS_TRIVIAL(GlobalContext) -struct CatchContext : ExecutionContext { +#define CatchContextMembers(class, Member) \ + Member(class, Pointer, String *, exceptionVarName) \ + Member(class, HeapValue, HeapValue, exceptionValue) + +DECLARE_HEAP_OBJECT(CatchContext, ExecutionContext) { + DECLARE_MARK_TABLE(CatchContext); + void init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue); - Pointer<String> exceptionVarName; - Value exceptionValue; }; V4_ASSERT_IS_TRIVIAL(CatchContext) -struct WithContext : ExecutionContext { - void init(ExecutionContext *outerContext, Object *with) - { - Heap::ExecutionContext::init(outerContext->engine, Heap::ExecutionContext::Type_WithContext); - outer = outerContext; - callData = outer->callData; - lookups = outer->lookups; - constantTable = outer->constantTable; - compilationUnit = outer->compilationUnit; - - withObject = with; - } +#define WithContextMembers(class, Member) \ + Member(class, Pointer, Object *, withObject) - Pointer<Object> withObject; +DECLARE_HEAP_OBJECT(WithContext, ExecutionContext) { + DECLARE_MARK_TABLE(WithContext); + + void init(ExecutionContext *outerContext, Object *with); }; V4_ASSERT_IS_TRIVIAL(WithContext) @@ -200,15 +254,13 @@ struct Q_QML_EXPORT ExecutionContext : public Managed ReturnedValue getPropertyAndBase(String *name, Value *base); bool deleteProperty(String *name); - inline CallContext *asCallContext(); - inline const CallContext *asCallContext() const; + inline SimpleCallContext *asSimpleCallContext(); + inline const SimpleCallContext *asSimpleCallContext() const; inline const CatchContext *asCatchContext() const; inline const WithContext *asWithContext() const; Function *getFunction() const; - static void markObjects(Heap::Base *m, ExecutionEngine *e); - Value &thisObject() const { return d()->callData->thisObject; } @@ -226,9 +278,9 @@ struct Q_QML_EXPORT ExecutionContext : public Managed void simpleCall(Scope &scope, CallData *callData, QV4::Function *function); }; -struct Q_QML_EXPORT CallContext : public ExecutionContext +struct Q_QML_EXPORT SimpleCallContext : public ExecutionContext { - V4_MANAGED(CallContext, ExecutionContext) + V4_MANAGED(SimpleCallContext, ExecutionContext) // formals are in reverse order Identifier * const *formals() const; @@ -237,14 +289,17 @@ struct Q_QML_EXPORT CallContext : public ExecutionContext unsigned int variableCount() const; inline ReturnedValue argument(int i) const; - bool needsOwnArguments() const; - }; -inline ReturnedValue CallContext::argument(int i) const { +inline ReturnedValue SimpleCallContext::argument(int i) const { return i < argc() ? args()[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); } +struct Q_QML_EXPORT CallContext : public SimpleCallContext +{ + V4_MANAGED(CallContext, SimpleCallContext) +}; + struct GlobalContext : public ExecutionContext { V4_MANAGED(GlobalContext, ExecutionContext) @@ -261,14 +316,14 @@ struct WithContext : public ExecutionContext V4_MANAGED(WithContext, ExecutionContext) }; -inline CallContext *ExecutionContext::asCallContext() +inline SimpleCallContext *ExecutionContext::asSimpleCallContext() { - return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<CallContext *>(this) : 0; + return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<SimpleCallContext *>(this) : 0; } -inline const CallContext *ExecutionContext::asCallContext() const +inline const SimpleCallContext *ExecutionContext::asSimpleCallContext() const { - return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<const CallContext *>(this) : 0; + return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<const SimpleCallContext *>(this) : 0; } inline const CatchContext *ExecutionContext::asCatchContext() const diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index a810b38f24..f1405e08ee 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -73,7 +73,7 @@ void DataViewCtor::construct(const Managed *, Scope &scope, CallData *callData) } Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>()); - a->d()->buffer = buffer->d(); + a->d()->buffer.set(scope.engine, buffer->d()); a->d()->byteLength = byteLength; a->d()->byteOffset = byteOffset; scope.result = a.asReturnedValue(); @@ -84,13 +84,6 @@ void DataViewCtor::call(const Managed *that, Scope &scope, CallData *callData) construct(that, scope, callData); } - -void DataView::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - DataView::Data *v = static_cast<DataView::Data *>(that); - v->buffer->mark(e); -} - void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) { Scope scope(engine); diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 11cc0a6bd9..5c50df4655 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -63,11 +63,14 @@ struct DataViewCtor : FunctionObject { void init(QV4::ExecutionContext *scope); }; -struct DataView : Object { +#define DataViewMembers(class, Member) \ + Member(class, Pointer, ArrayBuffer *, buffer) \ + Member(class, NoMark, uint, byteLength) \ + Member(class, NoMark, uint, byteOffset) + +DECLARE_HEAP_OBJECT(DataView, Object) { + DECLARE_MARK_TABLE(DataView); void init() { Object::init(); } - Pointer<ArrayBuffer> buffer; - uint byteLength; - uint byteOffset; }; } @@ -84,8 +87,6 @@ struct DataView : Object { V4_OBJECT2(DataView, Object) V4_PROTOTYPE(dataViewPrototype) - - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; struct DataViewPrototype: Object diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 39b433e5f9..806a614e95 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -130,15 +130,13 @@ QQmlEngine *ExecutionEngine::qmlEngine() const qint32 ExecutionEngine::maxCallDepth = -1; ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) - : current(0) - , hasException(false) - , callDepth(0) - , memoryManager(new QV4::MemoryManager(this)) + : callDepth(0) , executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) , currentContext(0) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , jsStack(new WTF::PageAllocation) + , gcStack(new WTF::PageAllocation) , globalCode(0) , v8Engine(0) , argumentsAccessors(0) @@ -151,6 +149,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , m_profiler(0) #endif { + memoryManager = new QV4::MemoryManager(this); + if (maxCallDepth == -1) { bool ok = false; maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok); @@ -189,18 +189,22 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) iselFactory.reset(factory); // reserve space for the JS stack - // we allow it to grow to 2 times JSStackLimit, as we can overshoot due to garbage collection - // and ScopedValues allocated outside of JIT'ed methods. - *jsStack = WTF::PageAllocation::allocate(2 * JSStackLimit, WTF::OSAllocator::JSVMStackPages, + // we allow it to grow to a bit more than JSStackLimit, as we can overshoot due to ScopedValues + // allocated outside of JIT'ed methods. + *jsStack = WTF::PageAllocation::allocate(JSStackLimit + 256*1024, WTF::OSAllocator::JSVMStackPages, /* writable */ true, /* executable */ false, /* includesGuardPages */ true); jsStackBase = (Value *)jsStack->base(); #ifdef V4_USE_VALGRIND - VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, 2*JSStackLimit); + VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, JSStackLimit + 256*1024); #endif jsStackTop = jsStackBase; + *gcStack = WTF::PageAllocation::allocate(GCStackLimit, WTF::OSAllocator::JSVMStackPages, + /* writable */ true, /* executable */ false, + /* includesGuardPages */ true); + exceptionValue = jsAlloca(1); globalObject = static_cast<Object *>(jsAlloca(1)); jsObjects = jsAlloca(NJSObjects); @@ -398,7 +402,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) // // set up the global object // - rootContext()->d()->global = globalObject->d(); + rootContext()->d()->global.set(scope.engine, globalObject->d()); rootContext()->d()->callData->thisObject = globalObject; Q_ASSERT(globalObject->d()->vtable()); @@ -494,6 +498,8 @@ ExecutionEngine::~ExecutionEngine() delete executableAllocator; jsStack->deallocate(); delete jsStack; + gcStack->deallocate(); + delete gcStack; delete [] argumentsAccessors; } @@ -518,7 +524,7 @@ void ExecutionEngine::initRootContext() sizeof(GlobalContext::Data) + sizeof(CallData))); r->d_unchecked()->init(this); r->d()->callData = reinterpret_cast<CallData *>(r->d() + 1); - r->d()->callData->tag = QV4::Value::Integer_Type_Internal; + r->d()->callData->tag = quint32(Value::ValueTypeInternal::Integer); r->d()->callData->argc = 0; r->d()->callData->thisObject = globalObject; r->d()->callData->args[0] = Encode::undefined(); @@ -600,12 +606,14 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int leng size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value); Heap::SimpleArrayData *d = scope.engine->memoryManager->allocManaged<SimpleArrayData>(size); d->init(); - d->alloc = length; d->type = Heap::ArrayData::Simple; d->offset = 0; - d->len = length; - memcpy(&d->arrayData, values, length*sizeof(Value)); - a->d()->arrayData = d; + d->values.alloc = length; + d->values.size = length; + // this doesn't require a write barrier, things will be ok, when the new array data gets inserted into + // the parent object + memcpy(&d->values.values, values, length*sizeof(Value)); + a->d()->arrayData.set(this, d); a->setArrayLengthUnchecked(length); } return a->d(); @@ -886,7 +894,7 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) QUrl base; ExecutionContext *c = currentContext; while (c) { - CallContext *callCtx = c->asCallContext(); + SimpleCallContext *callCtx = c->asSimpleCallContext(); if (callCtx && callCtx->d()->v4Function) { base.setUrl(callCtx->d()->v4Function->sourceFile()); break; @@ -929,23 +937,23 @@ void ExecutionEngine::requireArgumentsAccessors(int n) } } -void ExecutionEngine::markObjects() +void ExecutionEngine::markObjects(MarkStack *markStack) { - identifierTable->mark(this); + identifierTable->mark(markStack); for (int i = 0; i < nArgumentsAccessors; ++i) { const Property &pd = argumentsAccessors[i]; if (Heap::FunctionObject *getter = pd.getter()) - getter->mark(this); + getter->mark(markStack); if (Heap::FunctionObject *setter = pd.setter()) - setter->mark(this); + setter->mark(markStack); } - classPool->markObjects(this); + classPool->markObjects(markStack); for (QSet<CompiledData::CompilationUnit*>::ConstIterator it = compilationUnits.constBegin(), end = compilationUnits.constEnd(); it != end; ++it) - (*it)->markObjects(this); + (*it)->markObjects(markStack); } ReturnedValue ExecutionEngine::throwError(const Value &value) @@ -1563,12 +1571,6 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return 0; } -void ExecutionEngine::assertObjectBelongsToEngine(const Heap::Base &baseObject) -{ - Q_ASSERT(!baseObject.vtable()->isObject || static_cast<const Heap::Object&>(baseObject).internalClass->engine == this); - Q_UNUSED(baseObject); -} - void ExecutionEngine::failStackLimitCheck(Scope &scope) { scope.result = throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 69aa389c44..a2c774c295 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -54,7 +54,6 @@ #include "private/qv4isel_p.h" #include "qv4managed_p.h" #include "qv4context_p.h" -#include "qv4runtimeapi_p.h" #include <private/qintrusivelist_p.h> #ifndef V4_BOOTSTRAP @@ -88,7 +87,7 @@ struct CompilationUnit; struct InternalClass; struct InternalClassPool; -struct Q_QML_EXPORT ExecutionEngine +struct Q_QML_EXPORT ExecutionEngine : public EngineBase { private: static qint32 maxCallDepth; @@ -97,13 +96,8 @@ private: friend struct ExecutionContext; friend struct Heap::ExecutionContext; public: - Heap::ExecutionContext *current; - - Value *jsStackTop; - quint32 hasException; qint32 callDepth; - MemoryManager *memoryManager; ExecutableAllocator *executableAllocator; ExecutableAllocator *regExpAllocator; QScopedPointer<EvalISelFactory> iselFactory; @@ -112,22 +106,16 @@ public: Value *jsStackLimit; - Runtime runtime; - WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. - enum { JSStackLimit = 4*1024*1024 }; + enum { + JSStackLimit = 4*1024*1024, + GCStackLimit = 2*1024*1024 + }; WTF::PageAllocation *jsStack; Value *jsStackBase; - void pushForGC(Heap::Base *m) { - *jsStackTop = m; - ++jsStackTop; - } - Heap::Base *popForGC() { - --jsStackTop; - return jsStackTop->heapObject(); - } + WTF::PageAllocation *gcStack; QML_NEARLY_ALWAYS_INLINE Value *jsAlloca(int nValues) { Value *ptr = jsStackTop; @@ -454,7 +442,7 @@ public: void requireArgumentsAccessors(int n); - void markObjects(); + void markObjects(MarkStack *markStack); void initRootContext(); @@ -491,8 +479,6 @@ public: bool metaTypeFromJS(const Value *value, int type, void *data); QV4::ReturnedValue metaTypeToJS(int type, const void *data); - void assertObjectBelongsToEngine(const Heap::Base &baseObject); - bool checkStackLimits(Scope &scope); private: @@ -551,23 +537,32 @@ inline ExecutionContext *ExecutionEngine::parentContext(ExecutionContext *contex } inline -void Heap::Base::mark(QV4::ExecutionEngine *engine) +void Heap::Base::mark(QV4::MarkStack *markStack) { Q_ASSERT(inUse()); - if (isMarked()) - return; -#ifndef QT_NO_DEBUG - engine->assertObjectBelongsToEngine(*this); -#endif - setMarkBit(); - engine->pushForGC(this); + const HeapItem *h = reinterpret_cast<const HeapItem *>(this); + Chunk *c = h->chunk(); + size_t index = h - c->realBase(); + Q_ASSERT(!Chunk::testBit(c->extendsBitmap, index)); + quintptr *bitmap = c->blackBitmap + Chunk::bitmapIndex(index); + quintptr bit = Chunk::bitForIndex(index); + if (!(*bitmap & bit)) { + *bitmap |= bit; + markStack->push(this); + } } -inline void Value::mark(ExecutionEngine *e) +inline void Value::mark(MarkStack *markStack) { Heap::Base *o = heapObject(); if (o) - o->mark(e); + o->mark(markStack); +} + +inline void Managed::mark(MarkStack *markStack) +{ + Q_ASSERT(m()); + m()->mark(markStack); } #define CHECK_STACK_LIMITS(v4, scope) if ((v4)->checkStackLimits(scope)) return; \ diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index f290bc5136..58742a0b84 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -78,10 +78,10 @@ void Heap::ErrorObject::init() if (internalClass == scope.engine->errorProtoClass) return; - *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); - *propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined(); - *propertyData(QV4::ErrorObject::Index_FileName) = Encode::undefined(); - *propertyData(QV4::ErrorObject::Index_LineNumber) = Encode::undefined(); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d()); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset, Primitive::undefinedValue()); + setProperty(scope.engine, QV4::ErrorObject::Index_FileName, Primitive::undefinedValue()); + setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::undefinedValue()); } void Heap::ErrorObject::init(const Value &message, ErrorType t) @@ -92,17 +92,17 @@ void Heap::ErrorObject::init(const Value &message, ErrorType t) Scope scope(internalClass->engine); Scoped<QV4::ErrorObject> e(scope, this); - *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); - *propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined(); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d()); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset, Primitive::undefinedValue()); e->d()->stackTrace = new StackTrace(scope.engine->stackTrace()); if (!e->d()->stackTrace->isEmpty()) { - *propertyData(QV4::ErrorObject::Index_FileName) = scope.engine->newString(e->d()->stackTrace->at(0).source); - *propertyData(QV4::ErrorObject::Index_LineNumber) = Primitive::fromInt32(e->d()->stackTrace->at(0).line); + setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source)); + setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line)); } if (!message.isUndefined()) - *propertyData(QV4::ErrorObject::Index_Message) = message; + setProperty(scope.engine, QV4::ErrorObject::Index_Message, message); } void Heap::ErrorObject::init(const Value &message, const QString &fileName, int line, int column, ErrorObject::ErrorType t) @@ -113,8 +113,8 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int Scope scope(internalClass->engine); Scoped<QV4::ErrorObject> e(scope, this); - *propertyData(QV4::ErrorObject::Index_Stack) = scope.engine->getStackFunction(); - *propertyData(QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset) = Encode::undefined(); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d()); + setProperty(scope.engine, QV4::ErrorObject::Index_Stack + QV4::Object::SetterOffset, Primitive::undefinedValue()); e->d()->stackTrace = new StackTrace(scope.engine->stackTrace()); StackFrame frame; @@ -124,12 +124,12 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int e->d()->stackTrace->prepend(frame); if (!e->d()->stackTrace->isEmpty()) { - *propertyData(QV4::ErrorObject::Index_FileName) = scope.engine->newString(e->d()->stackTrace->at(0).source); - *propertyData(QV4::ErrorObject::Index_LineNumber) = Primitive::fromInt32(e->d()->stackTrace->at(0).line); + setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source)); + setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line)); } if (!message.isUndefined()) - *propertyData(QV4::ErrorObject::Index_Message) = message; + setProperty(scope.engine, QV4::ErrorObject::Index_Message, message); } const char *ErrorObject::className(Heap::ErrorObject::ErrorType t) @@ -168,19 +168,11 @@ void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallDa if (frame.line >= 0) trace += QLatin1Char(':') + QString::number(frame.line); } - This->d()->stack = scope.engine->newString(trace); + This->d()->stack.set(scope.engine, scope.engine->newString(trace)); } scope.result = This->d()->stack; } -void ErrorObject::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - ErrorObject::Data *This = static_cast<ErrorObject::Data *>(that); - if (This->stack) - This->stack->mark(e); - Object::markObjects(that, e); -} - DEFINE_OBJECT_VTABLE(ErrorObject); DEFINE_OBJECT_VTABLE(SyntaxErrorObject); @@ -327,9 +319,9 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = obj)); ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); - *obj->propertyData(Index_Constructor) = ctor; - *obj->propertyData(Index_Message) = engine->id_empty(); - *obj->propertyData(Index_Name) = engine->newString(QString::fromLatin1(ErrorObject::className(t))); + 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); } diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index 9ba9f05234..5afd9efcba 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -62,7 +62,12 @@ struct SyntaxErrorObject; namespace Heap { -struct ErrorObject : Object { + +#define ErrorObjectMembers(class, Member) \ + Member(class, Pointer, String *, stack) + +DECLARE_HEAP_OBJECT(ErrorObject, Object) { + DECLARE_MARK_TABLE(ErrorObject); enum ErrorType { Error, EvalError, @@ -72,6 +77,8 @@ struct ErrorObject : Object { TypeError, URIError }; + StackTrace *stackTrace; + ErrorType errorType; void init(); void init(const Value &message, ErrorType t = Error); @@ -80,10 +87,6 @@ struct ErrorObject : Object { delete stackTrace; Object::destroy(); } - - ErrorType errorType; - StackTrace *stackTrace; - Pointer<String> stack; }; struct EvalErrorObject : ErrorObject { @@ -173,7 +176,6 @@ struct ErrorObject: Object { static const char *className(Heap::ErrorObject::ErrorType t); static void method_get_stack(const BuiltinFunction *, Scope &scope, CallData *callData); - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; template<> diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 358c2d079c..ed9e3699f2 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -83,10 +83,7 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); - activationRequired = compiledFunction->nInnerFunctions > 0 || (compiledFunction->flags & (CompiledData::Function::HasDirectEval | CompiledData::Function::UsesArgumentsObject)); - - canUseSimpleCall = !needsActivation() && !(compiledFunction->flags & CompiledData::Function::HasCatchOrWith) && - !(compiledFunction->nFormals > QV4::Global::ReservedArgumentCount) && !isNamedExpression(); + canUseSimpleCall = compiledFunction->flags & CompiledData::Function::CanUseSimpleCall; } Function::~Function() @@ -118,7 +115,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr for (quint32 i = 0; i < compiledFunction->nLocals; ++i) internalClass = internalClass->addMember(compilationUnit->runtimeStrings[localsIndices[i]]->identifier, Attr_NotConfigurable); - activationRequired = true; + canUseSimpleCall = false; } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 54d0528c42..b11c8af94a 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -69,7 +69,6 @@ struct Q_QML_EXPORT Function { // first nArguments names in internalClass are the actual arguments InternalClass *internalClass; uint nFormals; - bool activationRequired; bool hasQmlDependencies; bool canUseSimpleCall; @@ -89,9 +88,6 @@ struct Q_QML_EXPORT Function { inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } inline bool isNamedExpression() const { return compiledFunction->flags & CompiledData::Function::IsNamedExpression; } - inline bool needsActivation() const - { return activationRequired; } - inline bool canUseSimpleFunction() const { return canUseSimpleCall; } QQmlSourceLocation sourceLocation() const @@ -102,7 +98,7 @@ struct Q_QML_EXPORT Function { }; -inline unsigned int Heap::CallContext::formalParameterCount() const +inline unsigned int Heap::SimpleCallContext::formalParameterCount() const { return v4Function ? v4Function->nFormals : 0; } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index e9431ed25e..5c8f03dc72 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -69,11 +69,13 @@ using namespace QV4; DEFINE_OBJECT_VTABLE(FunctionObject); +Q_STATIC_ASSERT((Heap::FunctionObject::markTable & Heap::Object::markTable) == Heap::Object::markTable); + void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto) { Object::init(); function = nullptr; - this->scope = scope->d(); + this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedFunctionObject f(s, this); f->init(name, createProto); @@ -84,7 +86,7 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function Object::init(); this->function = function; function->compilationUnit->addref(); - this->scope = scope->d(); + this->scope.set(scope->engine(), scope->d()); Scope s(scope->engine()); ScopedString name(s, function->name()); ScopedFunctionObject f(s, this); @@ -102,9 +104,9 @@ void Heap::FunctionObject::init() { Object::init(); function = nullptr; - this->scope = internalClass->engine->rootContext()->d(); + this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d()); Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype); - *propertyData(Index_Prototype) = Encode::undefined(); + setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue()); } @@ -124,10 +126,10 @@ void FunctionObject::init(String *n, bool createProto) 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); - *proto->propertyData(Heap::FunctionObject::Index_ProtoConstructor) = this->asReturnedValue(); - *propertyData(Heap::FunctionObject::Index_Prototype) = proto.asReturnedValue(); + proto->setProperty(Heap::FunctionObject::Index_ProtoConstructor, d()); + setProperty(Heap::FunctionObject::Index_Prototype, proto); } else { - *propertyData(Heap::FunctionObject::Index_Prototype) = Encode::undefined(); + setProperty(Heap::FunctionObject::Index_Prototype, Primitive::undefinedValue()); } if (n) @@ -149,15 +151,6 @@ void FunctionObject::call(const Managed *, Scope &scope, CallData *) scope.result = Encode::undefined(); } -void FunctionObject::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - Heap::FunctionObject *o = static_cast<Heap::FunctionObject *>(that); - if (o->scope) - o->scope->mark(e); - - Object::markObjects(that, e); -} - Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) { return scope->d()->engine->memoryManager->allocObject<ScriptFunction>(scope, function); @@ -309,7 +302,7 @@ void FunctionPrototype::method_apply(const BuiltinFunction *, Scope &scope, Call cData->args[i] = Primitive::undefinedValue(); } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData()); - uint alen = sad ? sad->len : 0; + uint alen = sad ? sad->values.size : 0; if (alen > len) alen = len; for (uint i = 0; i < alen; ++i) @@ -352,8 +345,9 @@ void FunctionPrototype::method_bind(const BuiltinFunction *, Scope &scope, CallD Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)0); if (callData->argc > 1) { boundArgs = MemberData::allocate(scope.engine, callData->argc - 1); - boundArgs->d()->size = callData->argc - 1; - memcpy(boundArgs->data(), callData->args + 1, (callData->argc - 1)*sizeof(Value)); + boundArgs->d()->values.size = callData->argc - 1; + for (uint i = 0; i < static_cast<uint>(callData->argc - 1); ++i) + boundArgs->set(scope.engine, i, callData->args[i + 1]); } ExecutionContext *global = scope.engine->rootContext(); @@ -420,7 +414,7 @@ void ScriptFunction::call(const Managed *that, Scope &scope, CallData *callData) void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function) { FunctionObject::init(); - this->scope = scope->d(); + this->scope.set(scope->engine(), scope->d()); this->function = function; function->compilationUnit->addref(); @@ -433,7 +427,7 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function ScopedString name(s, function->name()); f->init(name, true); Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length); - *propertyData(Index_Length) = Primitive::fromInt32(f->formalParameterCount()); + setProperty(s.engine, Index_Length, Primitive::fromInt32(f->formalParameterCount())); if (scope->d()->strictMode) { ScopedProperty pd(s); @@ -479,7 +473,7 @@ void OldBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callD ExecutionContextSaver ctxSaver(scope); - CallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); + SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? ctx->callData = callData; v4->pushContext(ctx); @@ -526,7 +520,7 @@ void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *c ExecutionContextSaver ctxSaver(scope); - CallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); + SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(v4); ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? ctx->callData = callData; v4->pushContext(ctx); @@ -543,12 +537,12 @@ DEFINE_OBJECT_VTABLE(BoundFunction); void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs) { + Scope s(scope); Heap::FunctionObject::init(scope, QStringLiteral("__bound function__")); - this->target = target->d(); - this->boundArgs = boundArgs ? boundArgs->d() : 0; - this->boundThis = boundThis; + this->target.set(s.engine, target->d()); + this->boundArgs.set(s.engine, boundArgs ? boundArgs->d() : 0); + this->boundThis.set(scope->engine(), boundThis); - Scope s(scope); ScopedObject f(s, this); ScopedValue l(s, target->get(s.engine->id_length())); @@ -606,14 +600,3 @@ void BoundFunction::construct(const Managed *that, Scope &scope, CallData *dd) ScopedFunctionObject t(scope, f->target()); t->construct(scope, callData); } - -void BoundFunction::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - BoundFunction::Data *o = static_cast<BoundFunction::Data *>(that); - if (o->target) - o->target->mark(e); - o->boundThis.mark(e); - if (o->boundArgs) - o->boundArgs->mark(e); - FunctionObject::markObjects(that, e); -} diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 45d7485f1b..d8929026ca 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -65,7 +65,12 @@ struct BuiltinFunction; namespace Heap { -struct Q_QML_PRIVATE_EXPORT FunctionObject : Object { +#define FunctionObjectMembers(class, Member) \ + Member(class, Pointer, ExecutionContext *, scope) \ + Member(class, NoMark, Function *, function) + +DECLARE_HEAP_OBJECT(FunctionObject, Object) { + DECLARE_MARK_TABLE(FunctionObject); enum { Index_Prototype = 0, Index_ProtoConstructor = 0 @@ -79,12 +84,8 @@ struct Q_QML_PRIVATE_EXPORT FunctionObject : Object { unsigned int formalParameterCount() { return function ? function->nFormals : 0; } unsigned int varCount() { return function ? function->compiledFunction->nLocals : 0; } - bool needsActivation() const { return function ? function->needsActivation() : false; } - - const QV4::Object *protoProperty() const { return propertyData(Index_Prototype)->cast<QV4::Object>(); } - Pointer<ExecutionContext> scope; - Function *function; + const QV4::Object *protoProperty() const { return propertyData(Index_Prototype)->as<QV4::Object>(); } }; struct FunctionCtor : FunctionObject { @@ -119,11 +120,15 @@ struct ScriptFunction : FunctionObject { void init(QV4::ExecutionContext *scope, Function *function); }; -struct BoundFunction : FunctionObject { +#define BoundFunctionMembers(class, Member) \ + Member(class, Pointer, FunctionObject *, target) \ + Member(class, HeapValue, HeapValue, boundThis) \ + Member(class, Pointer, MemberData *, boundArgs) + +DECLARE_HEAP_OBJECT(BoundFunction, FunctionObject) { + DECLARE_MARK_TABLE(BoundFunction); + void init(QV4::ExecutionContext *scope, QV4::FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs); - Pointer<FunctionObject> target; - Value boundThis; - Pointer<MemberData> boundArgs; }; } @@ -154,14 +159,11 @@ struct Q_QML_EXPORT FunctionObject: Object { static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); - bool needsActivation() const { return d()->needsActivation(); } bool strictMode() const { return d()->function ? d()->function->isStrict() : false; } bool isBinding() const; bool isBoundFunction() const; QQmlSourceLocation sourceLocation() const; - - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; template<> @@ -259,8 +261,6 @@ struct BoundFunction: FunctionObject { static void construct(const Managed *, Scope &scope, CallData *d); static void call(const Managed *that, Scope &scope, CallData *dd); - - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; } diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index b0d14fc2b4..0665295287 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -89,8 +89,6 @@ inline bool signbit(double d) { return _copysign(1.0, d) < 0; } inline double trunc(double d) { return d > 0 ? floor(d) : ceil(d); } #endif -#define qOffsetOf(s, m) ((size_t)((((char *)&(((s *)64)->m)) - 64))) - // Decide whether to enable or disable the JIT // White list architectures @@ -184,6 +182,7 @@ namespace Heap { struct DataView; struct TypedArray; + template <typename T, size_t> struct Pointer; } class MemoryManager; @@ -198,9 +197,12 @@ struct ScriptFunction; struct InternalClass; struct Property; struct Value; +template<size_t> struct HeapValue; +template<size_t> struct ValueArray; struct Lookup; struct ArrayData; struct VTable; +struct Function; struct BooleanObject; struct NumberObject; diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 1bc91f832b..f0630660d4 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -394,6 +394,7 @@ void EvalFunction::evalCall(Scope &scope, CallData *callData, bool directCall) c // set the correct strict mode flag on the context ctx->d()->strictMode = false; ctx->d()->compilationUnit = function->compilationUnit; + ctx->d()->constantTable = function->compilationUnit->constants; scope.result = Q_V4_PROFILE(ctx->engine(), function); } diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index 89af5db731..b0b08f1e54 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -93,14 +93,14 @@ public: Heap::String *stringFromIdentifier(Identifier *i); - void mark(ExecutionEngine *e) { + void mark(MarkStack *markStack) { for (int i = 0; i < alloc; ++i) { Heap::String *entry = entries[i]; if (!entry || entry->isMarked()) continue; entry->setMarkBit(); Q_ASSERT(entry->vtable()->markObjects); - entry->vtable()->markObjects(entry, e); + entry->vtable()->markObjects(entry, markStack); } } }; diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index bac71b4537..3d9a672f2f 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -126,26 +126,6 @@ InternalClass::InternalClass(const QV4::InternalClass &other) Q_ASSERT(extensible); } -static void insertHoleIntoPropertyData(Object *object, int idx) -{ - int icSize = object->internalClass()->size; - int from = idx; - int to = from + 1; - if (from < icSize) - memmove(object->propertyData(to), object->propertyData(from), - (icSize - from - 1) * sizeof(Value)); -} - -static void removeFromPropertyData(Object *object, int idx, bool accessor = false) -{ - int delta = (accessor ? 2 : 1); - int oldSize = object->internalClass()->size + delta; - int to = idx; - int from = to + delta; - if (from < oldSize) - memmove(object->propertyData(to), object->d()->propertyData(from), (oldSize - to)*sizeof(Value)); -} - void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index) { uint idx; @@ -157,10 +137,10 @@ void InternalClass::changeMember(Object *object, String *string, PropertyAttribu object->setInternalClass(newClass); if (newClass->size > oldClass->size) { Q_ASSERT(newClass->size == oldClass->size + 1); - insertHoleIntoPropertyData(object, idx + 1); + object->d()->memberData->values.insertData(newClass->engine, idx + 1, Primitive::emptyValue()); } else if (newClass->size < oldClass->size) { Q_ASSERT(newClass->size == oldClass->size - 1); - removeFromPropertyData(object, idx + 1); + object->d()->memberData->values.removeData(newClass->engine, idx + 1); } } @@ -318,7 +298,7 @@ void InternalClass::removeMember(Object *object, Identifier *id) Q_ASSERT(object->internalClass()->size == oldClass->size - (accessor ? 2 : 1)); // remove the entry in the property data - removeFromPropertyData(object, propIdx, accessor); + object->d()->memberData->values.removeData(oldClass->engine, propIdx, accessor ? 2 : 1); t.lookup = object->internalClass(); Q_ASSERT(t.lookup); @@ -408,9 +388,9 @@ void InternalClass::destroy() } } -void InternalClassPool::markObjects(ExecutionEngine *engine) +void InternalClassPool::markObjects(MarkStack *markStack) { - Q_UNUSED(engine); + Q_UNUSED(markStack); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 1d8ef4b0fb..a29ce5b5ff 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -291,7 +291,7 @@ inline uint InternalClass::find(const String *string) struct InternalClassPool : public QQmlJS::MemoryPool { - void markObjects(ExecutionEngine *engine); + void markObjects(MarkStack *markStack); }; } diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 52ed449664..11d7767e05 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -59,7 +59,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu if (index != UINT_MAX) { level = i; *attrs = obj->internalClass->propertyData.at(index); - Value *v = obj->propertyData(index); + const Value *v = obj->propertyData(index); return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs); } @@ -72,7 +72,7 @@ ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttribu index = obj->internalClass->find(name); if (index != UINT_MAX) { *attrs = obj->internalClass->propertyData.at(index); - Value *v = obj->propertyData(index); + const Value *v = obj->propertyData(index); return !attrs->isAccessor() ? v->asReturnedValue() : Object::getValue(thisObject, *v, *attrs); } @@ -94,7 +94,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs if (index != UINT_MAX) { level = i; *attrs = obj->internalClass->propertyData.at(index); - Value *v = obj->propertyData(index); + const Value *v = obj->propertyData(index); return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs); } @@ -107,7 +107,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs index = obj->internalClass->find(name); if (index != UINT_MAX) { *attrs = obj->internalClass->propertyData.at(index); - Value *v = obj->propertyData(index); + const Value *v = obj->propertyData(index); return !attrs->isAccessor() ? v->asReturnedValue() : thisObject->getValue(*v, *attrs); } @@ -116,20 +116,20 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs return Primitive::emptyValue().asReturnedValue(); } -ReturnedValue Lookup::indexedGetterGeneric(Lookup *l, const Value &object, const Value &index) +ReturnedValue Lookup::indexedGetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) { uint idx; if (object.isObject() && index.asArrayIndex(idx)) { l->indexedGetter = indexedGetterObjectInt; - return indexedGetterObjectInt(l, object, index); + return indexedGetterObjectInt(l, engine, object, index); } - return indexedGetterFallback(l, object, index); + return indexedGetterFallback(l, engine, object, index); } -ReturnedValue Lookup::indexedGetterFallback(Lookup *l, const Value &object, const Value &index) +ReturnedValue Lookup::indexedGetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) { Q_UNUSED(l); - Scope scope(l->engine); + Scope scope(engine); uint idx = 0; bool isInt = index.asArrayIndex(idx); @@ -147,7 +147,7 @@ ReturnedValue Lookup::indexedGetterFallback(Lookup *l, const Value &object, cons if (object.isNullOrUndefined()) { QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow()); - return l->engine->throwTypeError(message); + return engine->throwTypeError(message); } o = RuntimeHelpers::convertToObject(scope.engine, object); @@ -173,7 +173,7 @@ ReturnedValue Lookup::indexedGetterFallback(Lookup *l, const Value &object, cons } -ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const Value &object, const Value &index) +ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) { uint idx; if (index.asArrayIndex(idx)) { @@ -182,7 +182,7 @@ ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const Value &object, con 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->len) + if (idx < s->values.size) if (!s->data(idx).isEmpty()) return s->data(idx).asReturnedValue(); } @@ -190,25 +190,25 @@ ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, const Value &object, con } } - return indexedGetterFallback(l, object, index); + return indexedGetterFallback(l, engine, object, index); } -void Lookup::indexedSetterGeneric(Lookup *l, const Value &object, const Value &index, const Value &v) +void Lookup::indexedSetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v) { if (Object *o = object.objectValue()) { uint idx; if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple && index.asArrayIndex(idx)) { l->indexedSetter = indexedSetterObjectInt; - indexedSetterObjectInt(l, object, index, v); + indexedSetterObjectInt(l, engine, object, index, v); return; } } - indexedSetterFallback(l, object, index, v); + indexedSetterFallback(l, engine, object, index, v); } -void Lookup::indexedSetterFallback(Lookup *l, const Value &object, const Value &index, const Value &value) +void Lookup::indexedSetterFallback(Lookup *, ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { - Scope scope(l->engine); + Scope scope(engine); ScopedObject o(scope, object.toObject(scope.engine)); if (scope.engine->hasException) return; @@ -217,8 +217,8 @@ void Lookup::indexedSetterFallback(Lookup *l, const Value &object, const Value & 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->len) { - s->data(idx) = value; + if (idx < s->values.size) { + s->setData(engine, idx, value); return; } } @@ -230,7 +230,7 @@ void Lookup::indexedSetterFallback(Lookup *l, const Value &object, const Value & o->put(name, value); } -void Lookup::indexedSetterObjectInt(Lookup *l, const Value &object, const Value &index, const Value &v) +void Lookup::indexedSetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v) { uint idx; if (index.asArrayIndex(idx)) { @@ -239,15 +239,15 @@ void Lookup::indexedSetterObjectInt(Lookup *l, const Value &object, const Value 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->len) { - s->data(idx) = v; + if (idx < s->values.size) { + s->setData(engine, idx, v); return; } } } } } - indexedSetterFallback(l, object, index, v); + indexedSetterFallback(l, engine, object, index, v); } ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object) @@ -772,7 +772,7 @@ void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Va { Object *o = object.as<Object>(); if (o && o->internalClass() == l->classList[0]) { - *o->propertyData(l->index) = value; + o->setProperty(engine, l->index, value); return; } @@ -785,7 +785,7 @@ void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, co if (o && o->internalClass() == l->classList[0]) { if (!o->prototype()) { o->setInternalClass(l->classList[3]); - *o->propertyData(l->index) = value; + o->setProperty(l->index, value); return; } } @@ -801,7 +801,7 @@ void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, co Heap::Object *p = o->prototype(); if (p && p->internalClass == l->classList[1]) { o->setInternalClass(l->classList[3]); - *o->propertyData(l->index) = value; + o->setProperty(l->index, value); return; } } @@ -819,7 +819,7 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, co p = p->prototype; if (p && p->internalClass == l->classList[2]) { o->setInternalClass(l->classList[3]); - *o->propertyData(l->index) = value; + o->setProperty(l->index, value); return; } } @@ -834,11 +834,11 @@ void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, c Object *o = object.as<Object>(); if (o) { if (o->internalClass() == l->classList[0]) { - *o->propertyData(l->index) = value; + o->setProperty(l->index, value); return; } if (o->internalClass() == l->classList[1]) { - *o->propertyData(l->index2) = value; + o->setProperty(l->index2, value); return; } } diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index c5ee92fedd..daf3c71e27 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -67,14 +67,13 @@ namespace QV4 { struct Lookup { enum { Size = 4 }; union { - ReturnedValue (*indexedGetter)(Lookup *l, const Value &object, const Value &index); - void (*indexedSetter)(Lookup *l, const Value &object, const Value &index, const Value &v); + ReturnedValue (*indexedGetter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); + void (*indexedSetter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); void (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); }; union { - ExecutionEngine *engine; InternalClass *classList[Size]; struct { void *dummy0; @@ -90,13 +89,13 @@ struct Lookup { uint index; uint nameIndex; - static ReturnedValue indexedGetterGeneric(Lookup *l, const Value &object, const Value &index); - static ReturnedValue indexedGetterFallback(Lookup *l, const Value &object, const Value &index); - static ReturnedValue indexedGetterObjectInt(Lookup *l, const Value &object, const Value &index); + static ReturnedValue indexedGetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); + static ReturnedValue indexedGetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); + static ReturnedValue indexedGetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); - static void indexedSetterGeneric(Lookup *l, const Value &object, const Value &index, const Value &v); - static void indexedSetterFallback(Lookup *l, const Value &object, const Value &index, const Value &value); - static void indexedSetterObjectInt(Lookup *l, const Value &object, const Value &index, const Value &v); + static void indexedSetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); + static void indexedSetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &value); + static void indexedSetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -141,6 +140,12 @@ struct Lookup { }; +Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value); +// Ensure that these offsets are always at this point to keep generated code compatible +// across 32-bit and 64-bit (matters when cross-compiling). +Q_STATIC_ASSERT(offsetof(Lookup, indexedGetter) == 0); +Q_STATIC_ASSERT(offsetof(Lookup, getter) == 0); + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp index 3a84a83b9c..1b43fd86e8 100644 --- a/src/qml/jsruntime/qv4managed.cpp +++ b/src/qml/jsruntime/qv4managed.cpp @@ -47,6 +47,7 @@ using namespace QV4; const VTable Managed::static_vtbl = { 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 5c764e7ff0..f97771831c 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -53,6 +53,7 @@ #include "qv4global_p.h" #include "qv4value_p.h" #include <private/qv4heap_p.h> +#include <private/qv4writebarrier_p.h> QT_BEGIN_NAMESPACE @@ -91,6 +92,7 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} dptr->_checkIsInitialized(); \ return dptr; \ } \ + static Q_CONSTEXPR quint64 markTable = QV4::Heap::DataClass::markTable; \ V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass) #define V4_MANAGED(DataClass, superClass) \ @@ -129,6 +131,7 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} #define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \ { \ parentVTable, \ + markTable, \ classname::IsExecutionContext, \ classname::IsString, \ classname::IsObject, \ @@ -139,7 +142,7 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {} classname::MyType, \ #classname, \ Q_VTABLE_FUNCTION(classname, destroy), \ - markObjects, \ + Q_VTABLE_FUNCTION(classname, markObjects), \ isEqualTo \ } @@ -204,8 +207,10 @@ public: bool inUse() const { return d()->inUse(); } bool markBit() const { return d()->isMarked(); } + inline void mark(MarkStack *markStack); static void destroy(Heap::Base *) {} + static void markObjects(Heap::Base *, MarkStack *) {} Q_ALWAYS_INLINE Heap::Base *heapObject() const { return m(); diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp index db45c77472..8f862d63e9 100644 --- a/src/qml/jsruntime/qv4memberdata.cpp +++ b/src/qml/jsruntime/qv4memberdata.cpp @@ -45,24 +45,19 @@ using namespace QV4; DEFINE_MANAGED_VTABLE(MemberData); -void MemberData::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - Heap::MemberData *m = static_cast<Heap::MemberData *>(that); - for (uint i = 0; i < m->size; ++i) - m->data[i].mark(e); -} - Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberData *old) { - Q_ASSERT(!old || old->size < n); + Q_ASSERT(!old || old->values.size < n); Q_ASSERT(n); size_t alloc = MemoryManager::align(sizeof(Heap::MemberData) + (n - 1)*sizeof(Value)); Heap::MemberData *m = e->memoryManager->allocManaged<MemberData>(alloc); if (old) - memcpy(m, old, sizeof(Heap::MemberData) + (old->size - 1)* sizeof(Value)); + // no write barrier required here + memcpy(m, old, sizeof(Heap::MemberData) + (old->values.size - 1) * sizeof(Value)); else m->init(); - m->size = static_cast<uint>((alloc - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); + m->values.alloc = static_cast<uint>((alloc - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); + m->values.size = m->values.alloc; return m; } diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h index 5c89dfe8ec..fbe66757e0 100644 --- a/src/qml/jsruntime/qv4memberdata_p.h +++ b/src/qml/jsruntime/qv4memberdata_p.h @@ -59,12 +59,11 @@ namespace QV4 { namespace Heap { -struct MemberData : Base { - union { - uint size; - double _dummy; - }; - Value data[1]; +#define MemberDataMembers(class, Member) \ + Member(class, ValueArray, ValueArray, values) + +DECLARE_HEAP_OBJECT(MemberData, Base) { + DECLARE_MARK_TABLE(MemberData); }; V4_ASSERT_IS_TRIVIAL(MemberData) @@ -74,14 +73,26 @@ struct MemberData : Managed { V4_MANAGED(MemberData, Managed) - Value &operator[] (uint idx) { return d()->data[idx]; } - const Value *data() const { return d()->data; } - Value *data() { return d()->data; } - inline uint size() const { return d()->size; } + struct Index { + Heap::MemberData *memberData; + uint index; - static Heap::MemberData *allocate(QV4::ExecutionEngine *e, uint n, Heap::MemberData *old = 0); + void set(ExecutionEngine *e, Value newVal) { + memberData->values.set(e, index, newVal); + } + const Value *operator->() const { return &memberData->values[index]; } + const Value &operator*() const { return memberData->values[index]; } + bool isNull() const { return !memberData; } + }; - static void markObjects(Heap::Base *that, ExecutionEngine *e); + 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); } + + inline uint size() const { return d()->values.size; } + + static Heap::MemberData *allocate(QV4::ExecutionEngine *e, uint n, Heap::MemberData *old = 0); }; } diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index dd3bbccde3..d400c2ae64 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -62,8 +62,8 @@ void Object::setInternalClass(InternalClass *ic) { d()->internalClass = ic; bool hasMD = d()->memberData != nullptr; - if ((!hasMD && ic->size) || (hasMD && d()->memberData->size < ic->size)) - d()->memberData = MemberData::allocate(ic->engine, ic->size, d()->memberData); + if ((!hasMD && ic->size) || (hasMD && d()->memberData->values.size < ic->size)) + d()->memberData.set(engine(), MemberData::allocate(ic->engine, ic->size, d()->memberData)); } void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) const @@ -76,9 +76,9 @@ void Object::getProperty(uint index, Property *p, PropertyAttributes *attrs) con void Object::setProperty(uint index, const Property *p) { - *propertyData(index) = p->value; + setProperty(index, p->value); if (internalClass()->propertyData.at(index).isAccessor()) - *propertyData(index + SetterOffset) = p->set; + setProperty(index + SetterOffset, p->set); } bool Object::setPrototype(Object *proto) @@ -89,7 +89,7 @@ bool Object::setPrototype(Object *proto) return false; pp = pp->prototype; } - d()->prototype = proto ? proto->d() : 0; + d()->prototype.set(engine(), proto ? proto->d() : 0); return true; } @@ -117,7 +117,7 @@ bool Object::putValue(uint memberIndex, const Value &value) PropertyAttributes attrs = ic->propertyData[memberIndex]; if (attrs.isAccessor()) { - FunctionObject *set = propertyData(memberIndex + SetterOffset)->as<FunctionObject>(); + const FunctionObject *set = propertyData(memberIndex + SetterOffset)->as<FunctionObject>(); if (set) { Scope scope(ic->engine); ScopedFunctionObject setter(scope, set); @@ -133,7 +133,7 @@ bool Object::putValue(uint memberIndex, const Value &value) if (!attrs.isWritable()) goto reject; - *propertyData(memberIndex) = value; + setProperty(memberIndex, value); return true; reject: @@ -258,28 +258,16 @@ void Object::defineReadonlyConfigurableProperty(String *name, const Value &value insertMember(name, value, Attr_ReadOnly_ButConfigurable); } -void Object::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - Heap::Object *o = static_cast<Heap::Object *>(that); - - if (o->memberData) - o->memberData->mark(e); - if (o->arrayData) - o->arrayData->mark(e); - if (o->prototype) - o->prototype->mark(e); -} - void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes) { uint idx; InternalClass::addMember(this, s, attributes, &idx); if (attributes.isAccessor()) { - *propertyData(idx + GetterOffset) = p->value; - *propertyData(idx + SetterOffset) = p->set; + setProperty(idx + GetterOffset, p->value); + setProperty(idx + SetterOffset, p->set); } else { - *propertyData(idx) = p->value; + setProperty(idx, p->value); } } @@ -308,12 +296,9 @@ void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p) { - Property *pd = arrayData() ? arrayData()->getProperty(index) : 0; - if (pd) { - *attrs = arrayData()->attributes(index); - if (p) - p->copy(pd, *attrs); - return; + if (arrayData()) { + if (arrayData()->getProperty(index, p, attrs)) + return; } if (isStringObject()) { *attrs = Attr_NotConfigurable|Attr_NotWritable; @@ -328,7 +313,7 @@ void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p) } // Section 8.12.2 -Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) +MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *attrs) { Q_ASSERT(name->asArrayIndex() == UINT_MAX); @@ -337,36 +322,38 @@ Value *Object::getValueOrSetter(String *name, PropertyAttributes *attrs) uint idx = o->internalClass->find(name); if (idx < UINT_MAX) { *attrs = o->internalClass->propertyData[idx]; - return o->propertyData(attrs->isAccessor() ? idx + SetterOffset : idx); + return MemberData::Index{ o->memberData, attrs->isAccessor() ? idx + SetterOffset : idx }; } o = o->prototype; } *attrs = Attr_Invalid; - return 0; + return { 0, 0 }; } -Value *Object::getValueOrSetter(uint index, PropertyAttributes *attrs) +ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs) { Heap::Object *o = d(); while (o) { - Property *p = o->arrayData ? o->arrayData->getProperty(index) : 0; - if (p) { - *attrs = o->arrayData->attributes(index); - return attrs->isAccessor() ? &p->set : &p->value; + if (o->arrayData) { + uint idx = o->arrayData->mappedIndex(index); + if (idx != UINT_MAX) { + *attrs = o->arrayData->attributes(index); + return { o->arrayData , attrs->isAccessor() ? idx + SetterOffset : idx }; + } } if (o->vtable()->type == Type_StringObject) { if (index < static_cast<const Heap::StringObject *>(o)->length()) { // this is an evil hack, but it works, as the method is only ever called from putIndexed, // where we don't use the returned pointer there for non writable attributes *attrs = (Attr_NotWritable|Attr_NotConfigurable); - return reinterpret_cast<Value *>(0x1); + return { reinterpret_cast<Heap::ArrayData *>(0x1), 0 }; } } o = o->prototype; } *attrs = Attr_Invalid; - return 0; + return { 0, 0 }; } bool Object::hasProperty(String *name) const @@ -539,7 +526,7 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) l->classList[0] = o->internalClass(); l->index = idx; l->setter = Lookup::setter0; - *o->propertyData(idx) = value; + o->setProperty(idx, value); return; } @@ -594,7 +581,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * int k = it->arrayNode->key(); uint pidx = it->arrayNode->value; Heap::SparseArrayData *sa = o->d()->arrayData.cast<Heap::SparseArrayData>(); - Property *p = reinterpret_cast<Property *>(sa->arrayData + pidx); + const Property *p = reinterpret_cast<const Property *>(sa->values.data() + pidx); it->arrayNode = it->arrayNode->nextNode(); PropertyAttributes a = sa->attrs ? sa->attrs[pidx] : Attr_Data; if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) { @@ -609,9 +596,9 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint * it->arrayIndex = UINT_MAX; } // dense arrays - while (it->arrayIndex < o->d()->arrayData->len) { + while (it->arrayIndex < o->d()->arrayData->values.size) { Heap::SimpleArrayData *sa = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - Value &val = sa->data(it->arrayIndex); + const Value &val = sa->data(it->arrayIndex); PropertyAttributes a = o->arrayData()->attributes(it->arrayIndex); ++it->arrayIndex; if (!val.isEmpty() @@ -677,15 +664,14 @@ ReturnedValue Object::internalGet(String *name, bool *hasProperty) const ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const { - Property *pd = 0; PropertyAttributes attrs; Scope scope(engine()); ScopedObject o(scope, this); + ScopedProperty pd(scope); + bool exists = false; while (o) { - Property *p = o->arrayData() ? o->arrayData()->getProperty(index) : 0; - if (p) { - pd = p; - attrs = o->arrayData()->attributes(index); + if (o->arrayData() && o->arrayData()->getProperty(index, pd, &attrs)) { + exists = true; break; } if (o->isStringObject()) { @@ -700,7 +686,7 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const o = o->prototype(); } - if (pd) { + if (exists) { if (hasProperty) *hasProperty = true; return getValue(pd->value, attrs); @@ -715,43 +701,44 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const // Section 8.12.5 bool Object::internalPut(String *name, const Value &value) { - if (internalClass()->engine->hasException) + ExecutionEngine *engine = this->engine(); + if (engine->hasException) return false; uint idx = name->asArrayIndex(); if (idx != UINT_MAX) return putIndexed(idx, value); - name->makeIdentifier(engine()); + name->makeIdentifier(engine); + MemberData::Index memberIndex{0, 0}; uint member = internalClass()->find(name); - Value *v = 0; PropertyAttributes attrs; if (member < UINT_MAX) { attrs = internalClass()->propertyData[member]; - v = propertyData(attrs.isAccessor() ? member + SetterOffset : member); + memberIndex = { d()->memberData, (attrs.isAccessor() ? member + SetterOffset : member) }; } // clause 1 - if (v) { + if (!memberIndex.isNull()) { if (attrs.isAccessor()) { - if (v->as<FunctionObject>()) + if (memberIndex->as<FunctionObject>()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; - else if (isArrayObject() && name->equals(engine()->id_length())) { + else if (isArrayObject() && name->equals(engine->id_length())) { bool ok; uint l = value.asArrayLength(&ok); if (!ok) { - engine()->throwRangeError(value); + engine->throwRangeError(value); return false; } ok = setArrayLength(l); if (!ok) goto reject; } else { - *v = value; + memberIndex.set(engine, value); } return true; } else if (!prototype()) { @@ -759,10 +746,11 @@ bool Object::internalPut(String *name, const Value &value) goto reject; } else { // clause 4 - Scope scope(engine()); - if ((v = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs))) { + Scope scope(engine); + memberIndex = ScopedObject(scope, prototype())->getValueOrSetter(name, &attrs); + if (!memberIndex.isNull()) { if (attrs.isAccessor()) { - if (!v->as<FunctionObject>()) + if (!memberIndex->as<FunctionObject>()) goto reject; } else if (!isExtensible() || !attrs.isWritable()) { goto reject; @@ -775,11 +763,11 @@ bool Object::internalPut(String *name, const Value &value) cont: // Clause 5 - if (v && attrs.isAccessor()) { - Q_ASSERT(v->as<FunctionObject>()); + if (!memberIndex.isNull() && attrs.isAccessor()) { + Q_ASSERT(memberIndex->as<FunctionObject>()); - Scope scope(engine()); - ScopedFunctionObject setter(scope, *v); + Scope scope(engine); + ScopedFunctionObject setter(scope, *memberIndex); ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; @@ -792,49 +780,51 @@ bool Object::internalPut(String *name, const Value &value) reject: // ### this should be removed once everything is ported to use Object::set() - if (engine()->current->strictMode) { + if (engine->current->strictMode) { QString message = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); - engine()->throwTypeError(message); + engine->throwTypeError(message); } return false; } bool Object::internalPutIndexed(uint index, const Value &value) { - if (internalClass()->engine->hasException) + ExecutionEngine *engine = this->engine(); + if (engine->hasException) return false; PropertyAttributes attrs; - Value *v = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : 0; + ArrayData::Index arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : ArrayData::Index{ 0, 0 }; - if (!v && isStringObject()) { + if (arrayIndex.isNull() && isStringObject()) { if (index < static_cast<StringObject *>(this)->length()) // not writable goto reject; } // clause 1 - if (v) { + if (!arrayIndex.isNull()) { if (attrs.isAccessor()) { - if (v->as<FunctionObject>()) + if (arrayIndex->as<FunctionObject>()) goto cont; goto reject; } else if (!attrs.isWritable()) goto reject; - else - *v = value; + + arrayIndex.set(engine, value); return true; } else if (!prototype()) { if (!isExtensible()) goto reject; } else { // clause 4 - Scope scope(engine()); - if ((v = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs))) { + Scope scope(engine); + arrayIndex = ScopedObject(scope, prototype())->getValueOrSetter(index, &attrs); + if (!arrayIndex.isNull()) { if (attrs.isAccessor()) { - if (!v->as<FunctionObject>()) + if (!arrayIndex->as<FunctionObject>()) goto reject; } else if (!isExtensible() || !attrs.isWritable()) { goto reject; @@ -847,11 +837,11 @@ bool Object::internalPutIndexed(uint index, const Value &value) cont: // Clause 5 - if (v && attrs.isAccessor()) { - Q_ASSERT(v->as<FunctionObject>()); + if (!arrayIndex.isNull() && attrs.isAccessor()) { + Q_ASSERT(arrayIndex->as<FunctionObject>()); - Scope scope(engine()); - ScopedFunctionObject setter(scope, *v); + Scope scope(engine); + ScopedFunctionObject setter(scope, *arrayIndex); ScopedCallData callData(scope, 1); callData->args[0] = value; callData->thisObject = this; @@ -864,8 +854,8 @@ bool Object::internalPutIndexed(uint index, const Value &value) reject: // ### this should be removed once everything is ported to use Object::setIndexed() - if (engine()->current->strictMode) - engine()->throwTypeError(); + if (engine->current->strictMode) + engine->throwTypeError(); return false; } @@ -995,8 +985,8 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope // Clause 1 if (arrayData()) { - hasProperty = arrayData()->getProperty(index); - if (!hasProperty && isStringObject()) + hasProperty = arrayData()->mappedIndex(index) != UINT_MAX; + if (!hasProperty && isStringObject()) hasProperty = (index < static_cast<StringObject *>(this)->length()); } @@ -1108,7 +1098,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * setProperty(index, current); } else { setArrayAttributes(index, cattrs); - arrayData()->setProperty(index, current); + arrayData()->setProperty(scope.engine, index, current); } return true; reject: @@ -1144,7 +1134,8 @@ void Object::copyArrayData(Object *other) ; } else { Q_ASSERT(!arrayData() && other->arrayData()); - ArrayData::realloc(this, other->d()->arrayData->type, other->d()->arrayData->alloc, false); + ArrayData::realloc(this, static_cast<ArrayData::Type>(other->d()->arrayData->type), + other->d()->arrayData->values.alloc, false); if (other->arrayType() == Heap::ArrayData::Sparse) { Heap::ArrayData *od = other->d()->arrayData; Heap::ArrayData *dd = d()->arrayData; @@ -1152,10 +1143,11 @@ void Object::copyArrayData(Object *other) dd->freeList = od->freeList; } else { Heap::ArrayData *dd = d()->arrayData; - dd->len = other->d()->arrayData->len; + dd->values.size = other->d()->arrayData->values.size; dd->offset = other->d()->arrayData->offset; } - memcpy(d()->arrayData->arrayData, other->d()->arrayData->arrayData, other->d()->arrayData->alloc*sizeof(Value)); + // ### need a write barrier + memcpy(d()->arrayData->values.values, other->d()->arrayData->values.values, other->d()->arrayData->values.alloc*sizeof(Value)); } setArrayLengthUnchecked(other->getLength()); } diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 0d17afbf41..df9d68525d 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -67,19 +67,24 @@ struct BuiltinFunction; namespace Heap { -struct Object : Base { +#define ObjectMembers(class, Member) \ + Member(class, NoMark, InternalClass *, internalClass) \ + Member(class, Pointer, Object *, prototype) \ + Member(class, Pointer, MemberData *, memberData) \ + Member(class, Pointer, ArrayData *, arrayData) + +DECLARE_HEAP_OBJECT(Object, Base) { + DECLARE_MARK_TABLE(Object); void init() { Base::init(); } void destroy() { Base::destroy(); } - const Value *propertyData(uint index) const { return memberData->data + index; } - Value *propertyData(uint index) { return memberData->data + index; } - - InternalClass *internalClass; - Pointer<Object> prototype; - Pointer<MemberData> memberData; - Pointer<ArrayData> arrayData; + 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); } }; +Q_STATIC_ASSERT(Object::markTable == ((2 << 4) | (2 << 6) | (2 << 8))); + } #define V4_OBJECT(superClass) \ @@ -114,7 +119,8 @@ struct Object : Base { dptr->_checkIsInitialized(); \ return dptr; \ } \ - V4_ASSERT_IS_TRIVIAL(QV4::Heap::DataClass); + 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) \ @@ -190,13 +196,16 @@ struct Q_QML_EXPORT Object: Managed { void setInternalClass(InternalClass *ic); const Value *propertyData(uint index) const { return d()->propertyData(index); } - Value *propertyData(uint index) { return d()->propertyData(index); } Heap::ArrayData *arrayData() const { return d()->arrayData; } - void setArrayData(ArrayData *a) { d()->arrayData = a->d(); } + void setArrayData(ArrayData *a) { d()->arrayData.set(engine(), a->d()); } void getProperty(uint index, Property *p, PropertyAttributes *attrs) const; void setProperty(uint index, const Property *p); + void setProperty(uint index, Value v) const { d()->setProperty(engine(), index, v); } + void setProperty(uint index, Heap::Base *b) const { d()->setProperty(engine(), index, b); } + void setProperty(ExecutionEngine *engine, uint index, Value v) const { d()->setProperty(engine, index, v); } + 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; } @@ -205,8 +214,8 @@ struct Q_QML_EXPORT Object: Managed { void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = 0); void getOwnProperty(uint index, PropertyAttributes *attrs, Property *p = 0); - Value *getValueOrSetter(String *name, PropertyAttributes *attrs); - Value *getValueOrSetter(uint index, PropertyAttributes *attrs); + MemberData::Index getValueOrSetter(String *name, PropertyAttributes *attrs); + ArrayData::Index getValueOrSetter(uint index, PropertyAttributes *attrs); bool hasProperty(String *name) const; bool hasProperty(uint index) const; @@ -296,7 +305,7 @@ public: void push_back(const Value &v); ArrayData::Type arrayType() const { - return arrayData() ? d()->arrayData->type : Heap::ArrayData::Simple; + return arrayData() ? static_cast<ArrayData::Type>(d()->arrayData->type) : Heap::ArrayData::Simple; } // ### remove me void setArrayType(ArrayData::Type t) { @@ -400,7 +409,6 @@ public: inline void call(Scope &scope, CallData *d) const { vtable()->call(this, scope, d); } protected: - static void markObjects(Heap::Base *that, ExecutionEngine *e); static void construct(const Managed *m, Scope &scope, CallData *); static void call(const Managed *m, Scope &scope, CallData *); static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); @@ -465,7 +473,7 @@ struct ArrayObject : Object { private: void commonInit() - { *propertyData(LengthPropertyIndex) = Primitive::fromInt32(0); } + { setProperty(internalClass->engine, LengthPropertyIndex, Primitive::fromInt32(0)); } }; } @@ -505,7 +513,7 @@ struct ArrayObject: Object { inline void Object::setArrayLengthUnchecked(uint l) { if (isArrayObject()) - *propertyData(Heap::ArrayObject::LengthPropertyIndex) = Primitive::fromUInt32(l); + setProperty(Heap::ArrayObject::LengthPropertyIndex, Primitive::fromUInt32(l)); } inline void Object::push_back(const Value &v) @@ -522,7 +530,7 @@ inline void Object::arraySet(uint index, const Property *p, PropertyAttributes a { // ### Clean up arrayCreate(); - if (attributes.isAccessor() || (index > 0x1000 && index > 2*d()->arrayData->alloc)) { + if (attributes.isAccessor() || (index > 0x1000 && index > 2*d()->arrayData->values.alloc)) { initSparseArray(); } else { arrayData()->vtable()->reallocate(this, index + 1, false); @@ -537,7 +545,7 @@ inline void Object::arraySet(uint index, const Property *p, PropertyAttributes a inline void Object::arraySet(uint index, const Value &value) { arrayCreate(); - if (index > 0x1000 && index > 2*d()->arrayData->alloc) { + if (index > 0x1000 && index > 2*d()->arrayData->values.alloc) { initSparseArray(); } ArrayData::insert(this, index, &value); diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 59115dfe21..3427ee89fe 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -177,10 +177,10 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString() DEFINE_OBJECT_VTABLE(ForEachIteratorObject); -void ForEachIteratorObject::markObjects(Heap::Base *that, ExecutionEngine *e) +void ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) { ForEachIteratorObject::Data *o = static_cast<ForEachIteratorObject::Data *>(that); - o->workArea[0].mark(e); - o->workArea[1].mark(e); - Object::markObjects(that, e); + o->workArea[0].mark(markStack); + o->workArea[1].mark(markStack); + Object::markObjects(that, markStack); } diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index 98e94a95ea..6168d59914 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -129,7 +129,7 @@ struct ForEachIteratorObject: Object { ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); } protected: - static void markObjects(Heap::Base *that, ExecutionEngine *e); + static void markObjects(Heap::Base *that, MarkStack *markStack); }; inline diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index f650ffc7b1..2e72c0f13f 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -295,7 +295,7 @@ void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallDat if (o->arrayData()) { ArrayData::ensureAttributes(o); - for (uint i = 0; i < o->d()->arrayData->alloc; ++i) { + for (uint i = 0; i < o->d()->arrayData->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) o->d()->arrayData->attrs[i].setConfigurable(false); } @@ -320,7 +320,7 @@ void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallD if (o->arrayData()) { ArrayData::ensureAttributes(o); - for (uint i = 0; i < o->arrayData()->alloc; ++i) { + for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) o->arrayData()->attrs[i].setConfigurable(false); if (o->arrayData()->attrs[i].isData()) @@ -371,7 +371,7 @@ void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, Cal return; } - for (uint i = 0; i < o->arrayData()->alloc; ++i) { + for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) if (o->arrayData()->attributes(i).isConfigurable()) { scope.result = Encode(false); @@ -411,7 +411,7 @@ void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, Cal return; } - for (uint i = 0; i < o->arrayData()->alloc; ++i) { + for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) { scope.result = Encode(false); diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index 987c322e47..0b31c971f9 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -215,26 +215,15 @@ void PersistentValueStorage::free(Value *v) freePage(p); } -static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) +void PersistentValueStorage::mark(MarkStack *markStack) { - while (engine->jsStackTop > markBase) { - Heap::Base *h = engine->popForGC(); - Q_ASSERT (h->vtable()->markObjects); - h->vtable()->markObjects(h, engine); - } -} - -void PersistentValueStorage::mark(ExecutionEngine *e) -{ - Value *markBase = e->jsStackTop; - Page *p = static_cast<Page *>(firstPage); while (p) { for (int i = 0; i < kEntriesPerPage; ++i) { if (Managed *m = p->values[i].as<Managed>()) - m->mark(e); + m->mark(markStack); } - drainMarkStack(e, markBase); + markStack->drain(); p = p->header.next; } @@ -393,11 +382,11 @@ void WeakValue::allocVal(ExecutionEngine *engine) val = engine->memoryManager->m_weakValues->allocate(); } -void WeakValue::markOnce(ExecutionEngine *e) +void WeakValue::markOnce(MarkStack *markStack) { if (!val) return; - val->mark(e); + val->mark(markStack); } void WeakValue::free() diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index c1cd1f34df..1f838f5531 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -65,7 +65,7 @@ struct Q_QML_EXPORT PersistentValueStorage Value *allocate(); static void free(Value *e); - void mark(ExecutionEngine *e); + void mark(MarkStack *markStack); struct Iterator { Iterator(void *p, int idx); @@ -203,7 +203,7 @@ public: bool isNullOrUndefined() const { return !val || val->isNullOrUndefined(); } void clear() { free(); } - void markOnce(ExecutionEngine *e); + void markOnce(MarkStack *markStack); private: Value *val; diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h index 5069d7690b..2a5b6f7f74 100644 --- a/src/qml/jsruntime/qv4property_p.h +++ b/src/qml/jsruntime/qv4property_p.h @@ -78,12 +78,6 @@ struct Property { attrs->resolve(); } - static Property genericDescriptor() { - Property pd; - pd.value = Primitive::emptyValue(); - return pd; - } - inline bool isSubset(const PropertyAttributes &attrs, const Property *other, PropertyAttributes otherAttrs) const; inline void merge(PropertyAttributes &attrs, const Property *other, PropertyAttributes otherAttrs); @@ -99,19 +93,12 @@ struct Property { } explicit Property() { value = Encode::undefined(); set = Value::fromHeapObject(0); } - explicit Property(Value v) : value(v) { set = Value::fromHeapObject(0); } - Property(FunctionObject *getter, FunctionObject *setter) { - value = reinterpret_cast<Managed *>(getter); - set = reinterpret_cast<Managed *>(setter); - } Property(Heap::FunctionObject *getter, Heap::FunctionObject *setter) { value.setM(reinterpret_cast<Heap::Base *>(getter)); set.setM(reinterpret_cast<Heap::Base *>(setter)); } - Property &operator=(Value v) { value = v; return *this; } private: - Property(const Property &); - Property &operator=(const Property &); + Q_DISABLE_COPY(Property) }; inline bool Property::isSubset(const PropertyAttributes &attrs, const Property *other, PropertyAttributes otherAttrs) const diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index cdc29c8b9c..56ecc9f682 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -298,14 +298,14 @@ bool QmlContextWrapper::put(Managed *m, String *name, const Value &value) void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml) { Heap::ExecutionContext::init(outerContext->engine(), Heap::ExecutionContext::Type_QmlContext); - outer = outerContext->d(); + outer.set(engine, outerContext->d()); strictMode = false; callData = outer->callData; lookups = outer->lookups; constantTable = outer->constantTable; compilationUnit = outer->compilationUnit; - this->qml = qml->d(); + this->qml.set(engine, qml->d()); } Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, const QUrl &source, Value *sendFunction) diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 6e5e743609..835c9236fe 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -77,10 +77,13 @@ struct QmlContextWrapper : Object { QQmlQPointer<QObject> scopeObject; }; -struct QmlContext : ExecutionContext { - void init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml); +#define QmlContextMembers(class, Member) \ + Member(class, Pointer, QmlContextWrapper *, qml) + +DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext) { + DECLARE_MARK_TABLE(QmlContext); - Pointer<QmlContextWrapper> qml; + void init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml); }; } diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index c9b4b433bd..2ac2b0b64d 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -539,7 +539,7 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob } } -void QObjectWrapper::markWrapper(QObject *object, ExecutionEngine *engine) +void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) { if (QQmlData::wasDeleted(object)) return; @@ -548,10 +548,10 @@ void QObjectWrapper::markWrapper(QObject *object, ExecutionEngine *engine) if (!ddata) return; - if (ddata->jsEngineId == engine->m_engineId) - ddata->jsWrapper.markOnce(engine); - else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) - engine->m_multiplyWrappedQObjects->mark(object, engine); + if (ddata->jsEngineId == markStack->engine->m_engineId) + ddata->jsWrapper.markOnce(markStack); + else if (markStack->engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) + markStack->engine->m_multiplyWrappedQObjects->mark(object, markStack); } ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired) @@ -938,36 +938,36 @@ void QObjectWrapper::method_disconnect(const BuiltinFunction *, Scope &scope, Ca RETURN_UNDEFINED(); } -static void markChildQObjectsRecursively(QObject *parent, QV4::ExecutionEngine *e) +static void markChildQObjectsRecursively(QObject *parent, QV4::MarkStack *markStack) { const QObjectList &children = parent->children(); for (int i = 0; i < children.count(); ++i) { QObject *child = children.at(i); if (!child) continue; - QObjectWrapper::markWrapper(child, e); - markChildQObjectsRecursively(child, e); + QObjectWrapper::markWrapper(child, markStack); + markChildQObjectsRecursively(child, markStack); } } -void QObjectWrapper::markObjects(Heap::Base *that, QV4::ExecutionEngine *e) +void QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack) { QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that); if (QObject *o = This->object()) { QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o); if (vme) - vme->mark(e); + vme->mark(markStack); // Children usually don't need to be marked, the gc keeps them alive. // But in the rare case of a "floating" QObject without a parent that // _gets_ marked (we've been called here!) then we also need to // propagate the marking down to the children recursively. if (!o->parent()) - markChildQObjectsRecursively(o, e); + markChildQObjectsRecursively(o, markStack); } - QV4::Object::markObjects(that, e); + QV4::Object::markObjects(that, markStack); } void QObjectWrapper::destroyObject(bool lastCall) @@ -1704,7 +1704,7 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueType Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope)); method->d()->setPropertyCache(valueType->d()->propertyCache()); method->d()->index = index; - method->d()->valueTypeWrapper = valueType->d(); + method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d()); return method.asReturnedValue(); } @@ -1841,15 +1841,6 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const } } -void QObjectMethod::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - QObjectMethod::Data *This = static_cast<QObjectMethod::Data*>(that); - if (This->valueTypeWrapper) - This->valueTypeWrapper->mark(e); - - FunctionObject::markObjects(that, e); -} - DEFINE_OBJECT_VTABLE(QObjectMethod); @@ -2075,12 +2066,12 @@ void MultiplyWrappedQObjectMap::remove(QObject *key) erase(it); } -void MultiplyWrappedQObjectMap::mark(QObject *key, ExecutionEngine *engine) +void MultiplyWrappedQObjectMap::mark(QObject *key, MarkStack *markStack) { Iterator it = find(key); if (it == end()) return; - it->markOnce(engine); + it->markOnce(markStack); } void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object) diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index d81ef2a680..55700d17c1 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -95,7 +95,15 @@ private: QQmlQPointer<QObject> qObj; }; -struct QObjectMethod : FunctionObject { +#define QObjectMethodMembers(class, Member) \ + Member(class, Pointer, QQmlValueTypeWrapper *, valueTypeWrapper) \ + Member(class, NoMark, QQmlQPointer<QObject>, qObj) \ + Member(class, NoMark, QQmlPropertyCache *, _propertyCache) \ + Member(class, NoMark, int, index) + +DECLARE_HEAP_OBJECT(QObjectMethod, FunctionObject) { + DECLARE_MARK_TABLE(QObjectMethod); + void init(QV4::ExecutionContext *scope); void destroy() { @@ -113,18 +121,10 @@ struct QObjectMethod : FunctionObject { _propertyCache = c; } - Pointer<QQmlValueTypeWrapper> valueTypeWrapper; - const QMetaObject *metaObject(); QObject *object() const { return qObj.data(); } void setObject(QObject *o) { qObj = o; } -private: - QQmlQPointer<QObject> qObj; - QQmlPropertyCache *_propertyCache; - -public: - int index; }; struct QMetaObjectWrapper : FunctionObject { @@ -171,7 +171,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); static ReturnedValue wrap(ExecutionEngine *engine, QObject *object); - static void markWrapper(QObject *object, ExecutionEngine *engine); + static void markWrapper(QObject *object, MarkStack *markStack); using Object::get; @@ -195,7 +195,7 @@ protected: static bool put(Managed *m, String *name, const Value &value); static PropertyAttributes query(const Managed *, String *name); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); + static void markObjects(Heap::Base *that, QV4::MarkStack *markStack); static void method_connect(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData); @@ -209,13 +209,10 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje if (Q_UNLIKELY(QQmlData::wasDeleted(object))) return QV4::Encode::null(); - QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); - if (Q_LIKELY(priv->declarativeData)) { - auto ddata = static_cast<QQmlData *>(priv->declarativeData); - if (Q_LIKELY(ddata->jsEngineId == engine->m_engineId && !ddata->jsWrapper.isUndefined())) { - // We own the JS object - return ddata->jsWrapper.value(); - } + auto ddata = QQmlData::get(object); + if (Q_LIKELY(ddata && ddata->jsEngineId == engine->m_engineId && !ddata->jsWrapper.isUndefined())) { + // We own the JS object + return ddata->jsWrapper.value(); } return wrap_slowPath(engine, object); @@ -243,8 +240,6 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject void callInternal(CallData *callData, Scope &scope) const; - static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); - static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function); }; @@ -297,7 +292,7 @@ public: ReturnedValue value(QObject *key) const { return QHash<QObject*, QV4::WeakValue>::value(key).value(); } Iterator erase(Iterator it); void remove(QObject *key); - void mark(QObject *key, ExecutionEngine *engine); + void mark(QObject *key, MarkStack *markStack); private Q_SLOTS: void removeDestroyedObject(QObject*); diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 9e94c58432..6778145ff1 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -126,9 +126,3 @@ void Heap::RegExp::destroy() delete pattern; Base::destroy(); } - -void RegExp::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - Q_UNUSED(that); - Q_UNUSED(e); -} diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index d3e63375a5..348af0fb14 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -119,8 +119,6 @@ struct RegExp : public Managed int captureCount() const { return subPatternCount() + 1; } - static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); - friend class RegExpCache; }; diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 0894d0c25b..85e37ebe82 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -74,17 +74,17 @@ void Heap::RegExpObject::init() Object::init(); Scope scope(internalClass->engine); Scoped<QV4::RegExpObject> o(scope, this); - o->d()->value = QV4::RegExp::create(scope.engine, QString(), false, false); - o->d()->global = false; + value.set(scope.engine, QV4::RegExp::create(scope.engine, QString(), false, false)); + global = false; o->initProperties(); } void Heap::RegExpObject::init(QV4::RegExp *value, bool global) { Object::init(); - this->global = global; - this->value = value->d(); Scope scope(internalClass->engine); + this->global = global; + this->value.set(scope.engine, value->d()); Scoped<QV4::RegExpObject> o(scope, this); o->initProperties(); } @@ -137,14 +137,15 @@ void Heap::RegExpObject::init(const QRegExp &re) Scope scope(internalClass->engine); Scoped<QV4::RegExpObject> o(scope, this); - o->d()->value = QV4::RegExp::create(scope.engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false); + o->d()->value.set(scope.engine, + QV4::RegExp::create(scope.engine, pattern, re.caseSensitivity() == Qt::CaseInsensitive, false)); o->initProperties(); } void RegExpObject::initProperties() { - *propertyData(Index_LastIndex) = Primitive::fromInt32(0); + setProperty(Index_LastIndex, Primitive::fromInt32(0)); Q_ASSERT(value()); @@ -156,25 +157,10 @@ void RegExpObject::initProperties() p.replace('/', QLatin1String("\\/")); } - *propertyData(Index_Source) = engine()->newString(p); - *propertyData(Index_Global) = Primitive::fromBoolean(global()); - *propertyData(Index_IgnoreCase) = Primitive::fromBoolean(value()->ignoreCase); - *propertyData(Index_Multiline) = Primitive::fromBoolean(value()->multiLine); -} - - -void RegExpObject::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - RegExpObject::Data *re = static_cast<RegExpObject::Data *>(that); - if (re->value) - re->value->mark(e); - Object::markObjects(that, e); -} - -Value *RegExpObject::lastIndexProperty() -{ - Q_ASSERT(0 == internalClass()->find(engine()->id_lastIndex())); - return propertyData(0); + setProperty(Index_Source, engine()->newString(p)); + setProperty(Index_Global, Primitive::fromBoolean(global())); + setProperty(Index_IgnoreCase, Primitive::fromBoolean(value()->ignoreCase)); + setProperty(Index_Multiline, Primitive::fromBoolean(value()->multiLine)); } // Converts a JS RegExp to a QRegExp. @@ -228,8 +214,8 @@ void Heap::RegExpCtor::init(QV4::ExecutionContext *scope) void Heap::RegExpCtor::clearLastMatch() { - lastMatch = Primitive::nullValue(); - lastInput = internalClass->engine->id_empty()->d(); + lastMatch.set(internalClass->engine, Primitive::nullValue()); + lastInput.set(internalClass->engine, internalClass->engine->id_empty()->d()); lastMatchStart = 0; lastMatchEnd = 0; } @@ -303,15 +289,6 @@ void RegExpCtor::call(const Managed *that, Scope &scope, CallData *callData) construct(that, scope, callData); } -void RegExpCtor::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - RegExpCtor::Data *This = static_cast<RegExpCtor::Data *>(that); - This->lastMatch.mark(e); - if (This->lastInput) - This->lastInput->mark(e); - FunctionObject::markObjects(that, e); -} - void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) { Scope scope(engine); @@ -361,9 +338,9 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat RETURN_UNDEFINED(); QString s = str->toQString(); - int offset = r->global() ? r->lastIndexProperty()->toInt32() : 0; + int offset = r->global() ? r->lastIndex() : 0; if (offset < 0 || offset > s.length()) { - *r->lastIndexProperty() = Primitive::fromInt32(0); + r->setLastIndex(0); RETURN_RESULT(Encode::null()); } @@ -374,7 +351,7 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat regExpCtor->d()->clearLastMatch(); if (result == -1) { - *r->lastIndexProperty() = Primitive::fromInt32(0); + r->setLastIndex(0); RETURN_RESULT(Encode::null()); } @@ -390,17 +367,17 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat array->arrayPut(i, v); } array->setArrayLengthUnchecked(len); - *array->propertyData(Index_ArrayIndex) = Primitive::fromInt32(result); - *array->propertyData(Index_ArrayInput) = str; + array->setProperty(Index_ArrayIndex, Primitive::fromInt32(result)); + array->setProperty(Index_ArrayInput, str); RegExpCtor::Data *dd = regExpCtor->d(); - dd->lastMatch = array; - dd->lastInput = str->d(); + dd->lastMatch.set(scope.engine, array); + dd->lastInput.set(scope.engine, str->d()); dd->lastMatchStart = matchOffsets[0]; dd->lastMatchEnd = matchOffsets[1]; if (r->global()) - *r->lastIndexProperty() = Primitive::fromInt32(matchOffsets[1]); + r->setLastIndex(matchOffsets[1]); scope.result = array; } @@ -432,7 +409,7 @@ void RegExpPrototype::method_compile(const BuiltinFunction *, Scope &scope, Call scope.engine->regExpCtor()->as<FunctionObject>()->construct(scope, cData); Scoped<RegExpObject> re(scope, scope.result.asReturnedValue()); - r->d()->value = re->value(); + r->d()->value.set(scope.engine, re->value()); r->d()->global = re->global(); RETURN_UNDEFINED(); } diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index c0c7dfa78a..0fcfe93135 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -73,21 +73,28 @@ namespace QV4 { namespace Heap { -struct RegExpObject : Object { +#define RegExpObjectMembers(class, Member) \ + Member(class, Pointer, RegExp *, value) \ + Member(class, NoMark, bool, global) + +DECLARE_HEAP_OBJECT(RegExpObject, Object) { + DECLARE_MARK_TABLE(RegExpObject); + void init(); void init(QV4::RegExp *value, bool global); void init(const QRegExp &re); - - Pointer<RegExp> value; - bool global; }; -struct RegExpCtor : FunctionObject { +#define RegExpCtorMembers(class, Member) \ + Member(class, HeapValue, HeapValue, lastMatch) \ + Member(class, Pointer, String *, lastInput) \ + Member(class, NoMark, int, lastMatchStart) \ + Member(class, NoMark, int, lastMatchEnd) + +DECLARE_HEAP_OBJECT(RegExpCtor, FunctionObject) { + DECLARE_MARK_TABLE(RegExpCtor); + void init(QV4::ExecutionContext *scope); - Value lastMatch; - Pointer<String> lastInput; - int lastMatchStart; - int lastMatchEnd; void clearLastMatch(); }; @@ -121,14 +128,19 @@ struct RegExpObject: Object { void initProperties(); - Value *lastIndexProperty(); + int lastIndex() const { + Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex())); + return propertyData(Index_LastIndex)->toInt32(); + } + void setLastIndex(int index) { + Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex())); + return setProperty(Index_LastIndex, Primitive::fromInt32(index)); + } + QRegExp toQRegExp() const; QString toString() const; QString source() const; uint flags() const; - -protected: - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; struct RegExpCtor: FunctionObject @@ -142,7 +154,6 @@ struct RegExpCtor: FunctionObject static void construct(const Managed *m, Scope &scope, CallData *callData); static void call(const Managed *that, Scope &scope, CallData *callData); - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; struct RegExpPrototype: RegExpObject diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 6590054bf3..b28a5f9000 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -219,6 +219,14 @@ void RuntimeCounters::count(const char *func, uint tag1, uint tag2) #endif // QV4_COUNT_RUNTIME_FUNCTIONS #ifndef V4_BOOTSTRAP + +Runtime::Runtime() +{ +#define INIT_METHOD(returnvalue, name, args) runtimeMethods[name] = reinterpret_cast<void*>(&method_##name); +FOR_EACH_RUNTIME_METHOD(INIT_METHOD) +#undef INIT_METHOD +} + void RuntimeHelpers::numberToString(QString *result, double num, int radix) { Q_ASSERT(result); @@ -300,7 +308,7 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) { - QV4::Function *clos = engine->current->compilationUnit->runtimeFunctions[functionId]; + QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeFunctions[functionId]; Q_ASSERT(clos); return FunctionObject::createScriptFunction(engine->currentContext, clos)->asReturnedValue(); } @@ -643,8 +651,8 @@ void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, co if (idx < UINT_MAX) { if (o->arrayType() == Heap::ArrayData::Simple) { Heap::SimpleArrayData *s = static_cast<Heap::SimpleArrayData *>(o->arrayData()); - if (s && idx < s->len && !s->data(idx).isEmpty()) { - s->data(idx) = value; + if (s && idx < s->values.size && !s->data(idx).isEmpty()) { + s->setData(engine, idx, value); return; } } @@ -1301,7 +1309,7 @@ ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *value ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags) { Scope scope(engine); - QV4::InternalClass *klass = engine->current->compilationUnit->runtimeClasses[classId]; + QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeClasses[classId]; ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype())); { @@ -1311,7 +1319,7 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4:: } for (uint i = 0; i < klass->size; ++i) - *o->propertyData(i) = *args++; + o->setProperty(i, *args++); if (arrayValueCount > 0) { ScopedValue entry(scope); @@ -1413,7 +1421,7 @@ ReturnedValue Runtime::method_getQmlContext(NoThrowEngine *engine) ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id) { - return engine->current->compilationUnit->runtimeRegularExpressions[id].asReturnedValue(); + return static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeRegularExpressions[id].asReturnedValue(); } ReturnedValue Runtime::method_getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) @@ -1743,6 +1751,8 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) return !left.isNaN(); if (left.type() == right.type()) { + if (left.isDouble() && left.doubleValue() == 0 && right.doubleValue() == 0) + return true; // this takes care of -0 == +0 (which obviously have different raw values) if (!left.isManaged()) return false; if (left.isString() == right.isString()) diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 355b7890b6..302facba06 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { +typedef uint Bool; struct NoThrowEngine; namespace { @@ -90,256 +91,169 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { }; } // anonymous namespace -#define RUNTIME_METHOD(returnvalue, name, args) \ - typedef returnvalue (*Method_##name)args; \ - enum { Method_##name##_NeedsExceptionCheck = ExceptionCheck<Method_##name>::NeedsCheck }; \ - static returnvalue method_##name args; \ - const Method_##name name - -#define INIT_RUNTIME_METHOD(name) \ - name(method_##name) +#define FOR_EACH_RUNTIME_METHOD(F) \ + /* call */ \ + F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ + F(ReturnedValue, callActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ + F(ReturnedValue, callQmlScopeObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)) \ + F(ReturnedValue, callQmlContextObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)) \ + F(ReturnedValue, callProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ + F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ + F(ReturnedValue, callElement, (ExecutionEngine *engine, const Value &index, CallData *callData)) \ + F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, CallData *callData)) \ + \ + /* construct */ \ + F(ReturnedValue, constructGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ + F(ReturnedValue, constructActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ + F(ReturnedValue, constructProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ + F(ReturnedValue, constructPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ + F(ReturnedValue, constructValue, (ExecutionEngine *engine, const Value &func, CallData *callData)) \ + \ + /* set & get */ \ + F(void, setActivationProperty, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ + F(void, setProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ + F(void, setElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ + F(ReturnedValue, getProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ + F(ReturnedValue, getActivationProperty, (ExecutionEngine *engine, int nameIndex)) \ + F(ReturnedValue, getElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ + \ + /* typeof */ \ + F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \ + F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \ + F(ReturnedValue, typeofScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)) \ + F(ReturnedValue, typeofContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)) \ + F(ReturnedValue, typeofMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \ + F(ReturnedValue, typeofElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \ + \ + /* delete */ \ + F(ReturnedValue, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \ + F(ReturnedValue, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \ + F(ReturnedValue, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \ + F(ReturnedValue, deleteName, (ExecutionEngine *engine, int nameIndex)) \ + \ + /* exceptions & scopes */ \ + F(void, throwException, (ExecutionEngine *engine, const Value &value)) \ + F(ReturnedValue, unwindException, (ExecutionEngine *engine)) \ + F(void, pushWithScope, (const Value &o, NoThrowEngine *engine)) \ + F(void, pushCatchScope, (NoThrowEngine *engine, int exceptionVarNameIndex)) \ + F(void, popScope, (NoThrowEngine *engine)) \ + \ + /* closures */ \ + F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \ + \ + /* function header */ \ + F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \ + F(ReturnedValue, setupArgumentsObject, (ExecutionEngine *engine)) \ + F(void, convertThisToObject, (ExecutionEngine *engine)) \ + \ + /* literals */ \ + F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \ + F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)) \ + F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)) \ + \ + /* foreach */ \ + F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \ + F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \ + \ + /* unary operators */ \ + F(ReturnedValue, uPlus, (const Value &value)) \ + F(ReturnedValue, uMinus, (const Value &value)) \ + F(ReturnedValue, uNot, (const Value &value)) \ + F(ReturnedValue, complement, (const Value &value)) \ + F(ReturnedValue, increment, (const Value &value)) \ + F(ReturnedValue, decrement, (const Value &value)) \ + \ + /* binary operators */ \ + F(ReturnedValue, instanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ + F(ReturnedValue, in, (ExecutionEngine *engine, const Value &left, const Value &right)) \ + F(ReturnedValue, add, (ExecutionEngine *engine, const Value &left, const Value &right)) \ + F(ReturnedValue, addString, (ExecutionEngine *engine, const Value &left, const Value &right)) \ + F(ReturnedValue, bitOr, (const Value &left, const Value &right)) \ + F(ReturnedValue, bitXor, (const Value &left, const Value &right)) \ + F(ReturnedValue, bitAnd, (const Value &left, const Value &right)) \ + F(ReturnedValue, sub, (const Value &left, const Value &right)) \ + F(ReturnedValue, mul, (const Value &left, const Value &right)) \ + F(ReturnedValue, div, (const Value &left, const Value &right)) \ + F(ReturnedValue, mod, (const Value &left, const Value &right)) \ + F(ReturnedValue, shl, (const Value &left, const Value &right)) \ + F(ReturnedValue, shr, (const Value &left, const Value &right)) \ + F(ReturnedValue, ushr, (const Value &left, const Value &right)) \ + F(ReturnedValue, greaterThan, (const Value &left, const Value &right)) \ + F(ReturnedValue, lessThan, (const Value &left, const Value &right)) \ + F(ReturnedValue, greaterEqual, (const Value &left, const Value &right)) \ + F(ReturnedValue, lessEqual, (const Value &left, const Value &right)) \ + F(ReturnedValue, equal, (const Value &left, const Value &right)) \ + F(ReturnedValue, notEqual, (const Value &left, const Value &right)) \ + F(ReturnedValue, strictEqual, (const Value &left, const Value &right)) \ + F(ReturnedValue, strictNotEqual, (const Value &left, const Value &right)) \ + \ + /* comparisons */ \ + F(Bool, compareGreaterThan, (const Value &l, const Value &r)) \ + F(Bool, compareLessThan, (const Value &l, const Value &r)) \ + F(Bool, compareGreaterEqual, (const Value &l, const Value &r)) \ + F(Bool, compareLessEqual, (const Value &l, const Value &r)) \ + F(Bool, compareEqual, (const Value &left, const Value &right)) \ + F(Bool, compareNotEqual, (const Value &left, const Value &right)) \ + F(Bool, compareStrictEqual, (const Value &left, const Value &right)) \ + F(Bool, compareStrictNotEqual, (const Value &left, const Value &right)) \ + \ + F(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ + F(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right)) \ + \ + /* conversions */ \ + F(Bool, toBoolean, (const Value &value)) \ + F(ReturnedValue, toDouble, (const Value &value)) \ + F(int, toInt, (const Value &value)) \ + F(int, doubleToInt, (const double &d)) \ + F(unsigned, toUInt, (const Value &value)) \ + F(unsigned, doubleToUInt, (const double &d)) \ + \ + /* qml */ \ + F(ReturnedValue, getQmlContext, (NoThrowEngine *engine)) \ + F(ReturnedValue, getQmlImportedScripts, (NoThrowEngine *engine)) \ + F(ReturnedValue, getQmlSingleton, (NoThrowEngine *engine, int nameIndex)) \ + F(ReturnedValue, getQmlAttachedProperty, (ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex)) \ + F(ReturnedValue, getQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ + F(ReturnedValue, getQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ + F(ReturnedValue, getQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)) \ + F(ReturnedValue, getQmlSingletonQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)) \ + F(ReturnedValue, getQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)) \ + \ + F(void, setQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ + F(void, setQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ + F(void, setQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value)) struct Q_QML_PRIVATE_EXPORT Runtime { - Runtime() - : INIT_RUNTIME_METHOD(callGlobalLookup) - , INIT_RUNTIME_METHOD(callActivationProperty) - , INIT_RUNTIME_METHOD(callQmlScopeObjectProperty) - , INIT_RUNTIME_METHOD(callQmlContextObjectProperty) - , INIT_RUNTIME_METHOD(callProperty) - , INIT_RUNTIME_METHOD(callPropertyLookup) - , INIT_RUNTIME_METHOD(callElement) - , INIT_RUNTIME_METHOD(callValue) - , INIT_RUNTIME_METHOD(constructGlobalLookup) - , INIT_RUNTIME_METHOD(constructActivationProperty) - , INIT_RUNTIME_METHOD(constructProperty) - , INIT_RUNTIME_METHOD(constructPropertyLookup) - , INIT_RUNTIME_METHOD(constructValue) - , INIT_RUNTIME_METHOD(setActivationProperty) - , INIT_RUNTIME_METHOD(setProperty) - , INIT_RUNTIME_METHOD(setElement) - , INIT_RUNTIME_METHOD(getProperty) - , INIT_RUNTIME_METHOD(getActivationProperty) - , INIT_RUNTIME_METHOD(getElement) - , INIT_RUNTIME_METHOD(typeofValue) - , INIT_RUNTIME_METHOD(typeofName) - , INIT_RUNTIME_METHOD(typeofScopeObjectProperty) - , INIT_RUNTIME_METHOD(typeofContextObjectProperty) - , INIT_RUNTIME_METHOD(typeofMember) - , INIT_RUNTIME_METHOD(typeofElement) - , INIT_RUNTIME_METHOD(deleteElement) - , INIT_RUNTIME_METHOD(deleteMember) - , INIT_RUNTIME_METHOD(deleteMemberString) - , INIT_RUNTIME_METHOD(deleteName) - , INIT_RUNTIME_METHOD(throwException) - , INIT_RUNTIME_METHOD(unwindException) - , INIT_RUNTIME_METHOD(pushWithScope) - , INIT_RUNTIME_METHOD(pushCatchScope) - , INIT_RUNTIME_METHOD(popScope) - , INIT_RUNTIME_METHOD(closure) - , INIT_RUNTIME_METHOD(declareVar) - , INIT_RUNTIME_METHOD(setupArgumentsObject) - , INIT_RUNTIME_METHOD(convertThisToObject) - , INIT_RUNTIME_METHOD(arrayLiteral) - , INIT_RUNTIME_METHOD(objectLiteral) - , INIT_RUNTIME_METHOD(regexpLiteral) - , INIT_RUNTIME_METHOD(foreachIterator) - , INIT_RUNTIME_METHOD(foreachNextPropertyName) - , INIT_RUNTIME_METHOD(uPlus) - , INIT_RUNTIME_METHOD(uMinus) - , INIT_RUNTIME_METHOD(uNot) - , INIT_RUNTIME_METHOD(complement) - , INIT_RUNTIME_METHOD(increment) - , INIT_RUNTIME_METHOD(decrement) - , INIT_RUNTIME_METHOD(instanceof) - , INIT_RUNTIME_METHOD(in) - , INIT_RUNTIME_METHOD(add) - , INIT_RUNTIME_METHOD(addString) - , INIT_RUNTIME_METHOD(bitOr) - , INIT_RUNTIME_METHOD(bitXor) - , INIT_RUNTIME_METHOD(bitAnd) - , INIT_RUNTIME_METHOD(sub) - , INIT_RUNTIME_METHOD(mul) - , INIT_RUNTIME_METHOD(div) - , INIT_RUNTIME_METHOD(mod) - , INIT_RUNTIME_METHOD(shl) - , INIT_RUNTIME_METHOD(shr) - , INIT_RUNTIME_METHOD(ushr) - , INIT_RUNTIME_METHOD(greaterThan) - , INIT_RUNTIME_METHOD(lessThan) - , INIT_RUNTIME_METHOD(greaterEqual) - , INIT_RUNTIME_METHOD(lessEqual) - , INIT_RUNTIME_METHOD(equal) - , INIT_RUNTIME_METHOD(notEqual) - , INIT_RUNTIME_METHOD(strictEqual) - , INIT_RUNTIME_METHOD(strictNotEqual) - , INIT_RUNTIME_METHOD(compareGreaterThan) - , INIT_RUNTIME_METHOD(compareLessThan) - , INIT_RUNTIME_METHOD(compareGreaterEqual) - , INIT_RUNTIME_METHOD(compareLessEqual) - , INIT_RUNTIME_METHOD(compareEqual) - , INIT_RUNTIME_METHOD(compareNotEqual) - , INIT_RUNTIME_METHOD(compareStrictEqual) - , INIT_RUNTIME_METHOD(compareStrictNotEqual) - , INIT_RUNTIME_METHOD(compareInstanceof) - , INIT_RUNTIME_METHOD(compareIn) - , INIT_RUNTIME_METHOD(toBoolean) - , INIT_RUNTIME_METHOD(toDouble) - , INIT_RUNTIME_METHOD(toInt) - , INIT_RUNTIME_METHOD(doubleToInt) - , INIT_RUNTIME_METHOD(toUInt) - , INIT_RUNTIME_METHOD(doubleToUInt) - , INIT_RUNTIME_METHOD(getQmlContext) - , INIT_RUNTIME_METHOD(getQmlImportedScripts) - , INIT_RUNTIME_METHOD(getQmlSingleton) - , INIT_RUNTIME_METHOD(getQmlAttachedProperty) - , INIT_RUNTIME_METHOD(getQmlScopeObjectProperty) - , INIT_RUNTIME_METHOD(getQmlContextObjectProperty) - , INIT_RUNTIME_METHOD(getQmlQObjectProperty) - , INIT_RUNTIME_METHOD(getQmlSingletonQObjectProperty) - , INIT_RUNTIME_METHOD(getQmlIdObject) - , INIT_RUNTIME_METHOD(setQmlScopeObjectProperty) - , INIT_RUNTIME_METHOD(setQmlContextObjectProperty) - , INIT_RUNTIME_METHOD(setQmlQObjectProperty) - { } - - // call - RUNTIME_METHOD(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, callActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, callQmlScopeObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, callQmlContextObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, callProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, callElement, (ExecutionEngine *engine, const Value &index, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, CallData *callData)); - - // construct - RUNTIME_METHOD(ReturnedValue, constructGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, constructActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, constructProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, constructPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)); - RUNTIME_METHOD(ReturnedValue, constructValue, (ExecutionEngine *engine, const Value &func, CallData *callData)); - - // set & get - RUNTIME_METHOD(void, setActivationProperty, (ExecutionEngine *engine, int nameIndex, const Value &value)); - RUNTIME_METHOD(void, setProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)); - RUNTIME_METHOD(void, setElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)); - RUNTIME_METHOD(ReturnedValue, getProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)); - RUNTIME_METHOD(ReturnedValue, getActivationProperty, (ExecutionEngine *engine, int nameIndex)); - RUNTIME_METHOD(ReturnedValue, getElement, (ExecutionEngine *engine, const Value &object, const Value &index)); - - // typeof - RUNTIME_METHOD(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)); - RUNTIME_METHOD(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)); - RUNTIME_METHOD(ReturnedValue, typeofScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)); - RUNTIME_METHOD(ReturnedValue, typeofContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)); - RUNTIME_METHOD(ReturnedValue, typeofMember, (ExecutionEngine *engine, const Value &base, int nameIndex)); - RUNTIME_METHOD(ReturnedValue, typeofElement, (ExecutionEngine *engine, const Value &base, const Value &index)); - - // delete - RUNTIME_METHOD(ReturnedValue, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)); - RUNTIME_METHOD(ReturnedValue, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)); - RUNTIME_METHOD(ReturnedValue, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)); - RUNTIME_METHOD(ReturnedValue, deleteName, (ExecutionEngine *engine, int nameIndex)); - - // exceptions & scopes - RUNTIME_METHOD(void, throwException, (ExecutionEngine *engine, const Value &value)); - RUNTIME_METHOD(ReturnedValue, unwindException, (ExecutionEngine *engine)); - RUNTIME_METHOD(void, pushWithScope, (const Value &o, NoThrowEngine *engine)); - RUNTIME_METHOD(void, pushCatchScope, (NoThrowEngine *engine, int exceptionVarNameIndex)); - RUNTIME_METHOD(void, popScope, (NoThrowEngine *engine)); - - // closures - RUNTIME_METHOD(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)); + Runtime(); - // function header - RUNTIME_METHOD(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)); - RUNTIME_METHOD(ReturnedValue, setupArgumentsObject, (ExecutionEngine *engine)); - RUNTIME_METHOD(void, convertThisToObject, (ExecutionEngine *engine)); - - // literals - RUNTIME_METHOD(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)); - RUNTIME_METHOD(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)); - RUNTIME_METHOD(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)); - - // foreach - RUNTIME_METHOD(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)); - RUNTIME_METHOD(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)); - - // unary operators typedef ReturnedValue (*UnaryOperation)(const Value &value); - RUNTIME_METHOD(ReturnedValue, uPlus, (const Value &value)); - RUNTIME_METHOD(ReturnedValue, uMinus, (const Value &value)); - RUNTIME_METHOD(ReturnedValue, uNot, (const Value &value)); - RUNTIME_METHOD(ReturnedValue, complement, (const Value &value)); - RUNTIME_METHOD(ReturnedValue, increment, (const Value &value)); - RUNTIME_METHOD(ReturnedValue, decrement, (const Value &value)); - - // binary operators typedef ReturnedValue (*BinaryOperation)(const Value &left, const Value &right); typedef ReturnedValue (*BinaryOperationContext)(ExecutionEngine *engine, const Value &left, const Value &right); - RUNTIME_METHOD(ReturnedValue, instanceof, (ExecutionEngine *engine, const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, in, (ExecutionEngine *engine, const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, add, (ExecutionEngine *engine, const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, addString, (ExecutionEngine *engine, const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, bitOr, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, bitXor, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, bitAnd, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, sub, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, mul, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, div, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, mod, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, shl, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, shr, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, ushr, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, greaterThan, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, lessThan, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, greaterEqual, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, lessEqual, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, equal, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, notEqual, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, strictEqual, (const Value &left, const Value &right)); - RUNTIME_METHOD(ReturnedValue, strictNotEqual, (const Value &left, const Value &right)); - - // comparisons - RUNTIME_METHOD(Bool, compareGreaterThan, (const Value &l, const Value &r)); - RUNTIME_METHOD(Bool, compareLessThan, (const Value &l, const Value &r)); - RUNTIME_METHOD(Bool, compareGreaterEqual, (const Value &l, const Value &r)); - RUNTIME_METHOD(Bool, compareLessEqual, (const Value &l, const Value &r)); - RUNTIME_METHOD(Bool, compareEqual, (const Value &left, const Value &right)); - RUNTIME_METHOD(Bool, compareNotEqual, (const Value &left, const Value &right)); - RUNTIME_METHOD(Bool, compareStrictEqual, (const Value &left, const Value &right)); - RUNTIME_METHOD(Bool, compareStrictNotEqual, (const Value &left, const Value &right)); +#define DEFINE_RUNTIME_METHOD_ENUM(returnvalue, name, args) name, + enum RuntimeMethods { + FOR_EACH_RUNTIME_METHOD(DEFINE_RUNTIME_METHOD_ENUM) + RuntimeMethodCount, + InvalidRuntimeMethod = RuntimeMethodCount + }; +#undef DEFINE_RUNTIME_METHOD_ENUM - RUNTIME_METHOD(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right)); - RUNTIME_METHOD(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right)); + void *runtimeMethods[RuntimeMethodCount]; - // conversions - RUNTIME_METHOD(Bool, toBoolean, (const Value &value)); - RUNTIME_METHOD(ReturnedValue, toDouble, (const Value &value)); - RUNTIME_METHOD(int, toInt, (const Value &value)); - RUNTIME_METHOD(int, doubleToInt, (const double &d)); - RUNTIME_METHOD(unsigned, toUInt, (const Value &value)); - RUNTIME_METHOD(unsigned, doubleToUInt, (const double &d)); + static uint runtimeMethodOffset(RuntimeMethods method) { return method*QT_POINTER_SIZE; } - // qml - RUNTIME_METHOD(ReturnedValue, getQmlContext, (NoThrowEngine *engine)); - RUNTIME_METHOD(ReturnedValue, getQmlImportedScripts, (NoThrowEngine *engine)); - RUNTIME_METHOD(ReturnedValue, getQmlSingleton, (NoThrowEngine *engine, int nameIndex)); - RUNTIME_METHOD(ReturnedValue, getQmlAttachedProperty, (ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex)); - RUNTIME_METHOD(ReturnedValue, getQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)); - RUNTIME_METHOD(ReturnedValue, getQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)); - RUNTIME_METHOD(ReturnedValue, getQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)); - RUNTIME_METHOD(ReturnedValue, getQmlSingletonQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)); - RUNTIME_METHOD(ReturnedValue, getQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)); +#define RUNTIME_METHOD(returnvalue, name, args) \ + typedef returnvalue (*Method_##name)args; \ + enum { Method_##name##_NeedsExceptionCheck = ExceptionCheck<Method_##name>::NeedsCheck }; \ + static returnvalue method_##name args; + FOR_EACH_RUNTIME_METHOD(RUNTIME_METHOD) +#undef RUNTIME_METHOD - RUNTIME_METHOD(void, setQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)); - RUNTIME_METHOD(void, setQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)); - RUNTIME_METHOD(void, setQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value)); }; -#undef RUNTIME_METHOD -#undef INIT_RUNTIME_METHOD +static_assert(std::is_standard_layout<Runtime>::value, "Runtime needs to be standard layout in order for us to be able to use offsetof"); +static_assert(offsetof(Runtime, runtimeMethods) == 0, "JIT expects this to be the first member"); +static_assert(sizeof(Runtime::BinaryOperation) == sizeof(void*), "JIT expects a function pointer to fit into a regular pointer, for cross-compilation offset translation"); } // namespace QV4 diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 6775028272..e9dcc9172f 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -366,9 +366,9 @@ struct Scoped struct ScopedCallData { ScopedCallData(const Scope &scope, int argc = 0) { - int size = qMax(argc, (int)QV4::Global::ReservedArgumentCount) + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value); + int size = qMax(argc, QV4::Global::ReservedArgumentCount + int(offsetof(QV4::CallData, args)/sizeof(QV4::Value))); ptr = reinterpret_cast<CallData *>(scope.alloc(size)); - ptr->tag = QV4::Value::Integer_Type_Internal; + ptr->tag = quint32(QV4::Value::ValueTypeInternal::Integer); ptr->argc = argc; } diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index f96f0254a5..4ebe2dd609 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -72,7 +72,7 @@ struct ContextStateSaver { bool strictMode; Lookup *lookups; const QV4::Value *constantTable; - CompiledData::CompilationUnit *compilationUnit; + CompiledData::CompilationUnitBase *compilationUnit; int lineNumber; ContextStateSaver(const Scope &scope, ExecutionContext *context) diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index cde2131aab..71f85c2d71 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -54,12 +54,12 @@ using namespace QV4; DEFINE_MANAGED_VTABLE(String); -void String::markObjects(Heap::Base *that, ExecutionEngine *e) +void String::markObjects(Heap::Base *that, MarkStack *markStack) { String::Data *s = static_cast<String::Data *>(that); if (s->largestSubLength) { - s->left->mark(e); - s->right->mark(e); + s->left->mark(markStack); + s->right->mark(markStack); } } diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 5b0fd292d6..71e55cbcd4 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -204,7 +204,7 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { Identifier *identifier() const { return d()->identifier; } protected: - static void markObjects(Heap::Base *that, ExecutionEngine *e); + static void markObjects(Heap::Base *that, MarkStack *markStack); static bool isEqualTo(Managed *that, Managed *o); static uint getLength(const Managed *m); #endif diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 72be11eca0..81f5c3566c 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -77,15 +77,15 @@ void Heap::StringObject::init() { Object::init(); Q_ASSERT(vtable() == QV4::StringObject::staticVTable()); - string = internalClass->engine->id_empty()->d(); - *propertyData(LengthPropertyIndex) = Primitive::fromInt32(0); + string.set(internalClass->engine, internalClass->engine->id_empty()->d()); + setProperty(internalClass->engine, LengthPropertyIndex, Primitive::fromInt32(0)); } void Heap::StringObject::init(const QV4::String *str) { Object::init(); - string = str->d(); - *propertyData(LengthPropertyIndex) = Primitive::fromInt32(length()); + string.set(internalClass->engine, str->d()); + setProperty(internalClass->engine, LengthPropertyIndex, Primitive::fromInt32(length())); } Heap::String *Heap::StringObject::getIndex(uint index) const @@ -145,13 +145,6 @@ void StringObject::advanceIterator(Managed *m, ObjectIterator *it, Value *name, return Object::advanceIterator(m, it, name, index, p, attrs); } -void StringObject::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - StringObject::Data *o = static_cast<StringObject::Data *>(that); - o->string->mark(e); - Object::markObjects(that, e); -} - DEFINE_OBJECT_VTABLE(StringCtor); void Heap::StringCtor::init(QV4::ExecutionContext *scope) @@ -563,7 +556,7 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call offset = qMax(offset + 1, matchOffsets[oldSize + 1]); } if (regExp->global()) - *regExp->lastIndexProperty() = Primitive::fromUInt32(0); + regExp->setLastIndex(0); numStringMatches = nMatchOffsets / (regExp->value()->captureCount() * 2); numCaptures = regExp->value()->captureCount(); } else { diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index aed3bc1e28..5ccee3335e 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -60,14 +60,18 @@ namespace QV4 { namespace Heap { -struct StringObject : Object { +#define StringObjectMembers(class, Member) \ + Member(class, Pointer, String *, string) + +DECLARE_HEAP_OBJECT(StringObject, Object) { + DECLARE_MARK_TABLE(StringObject); + enum { LengthPropertyIndex = 0 }; void init(); void init(const QV4::String *string); - String *string; Heap::String *getIndex(uint index) const; uint length() const; @@ -96,7 +100,6 @@ struct StringObject: Object { protected: static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs); - static void markObjects(Heap::Base *that, ExecutionEngine *e); }; struct StringCtor: FunctionObject diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index 5573a2e57f..a34a8922e1 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -229,8 +229,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat return; } - Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); - array->d()->buffer = buffer->d(); + Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); + array->d()->buffer.set(scope.engine, buffer->d()); array->d()->byteLength = byteLength; array->d()->byteOffset = 0; @@ -252,8 +252,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat return; } - Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); - array->d()->buffer = newBuffer->d(); + Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); + array->d()->buffer.set(scope.engine, newBuffer->d()); array->d()->byteLength = destByteLength; array->d()->byteOffset = 0; @@ -311,8 +311,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat byteLength = (uint)l; } - Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); - array->d()->buffer = buffer->d(); + Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); + array->d()->buffer.set(scope.engine, buffer->d()); array->d()->byteLength = byteLength; array->d()->byteOffset = byteOffset; scope.result = array.asReturnedValue(); @@ -335,8 +335,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat return; } - Scoped<TypedArray > array(scope, TypedArray::create(scope.engine, that->d()->type)); - array->d()->buffer = newBuffer->d(); + Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); + array->d()->buffer.set(scope.engine, newBuffer->d()); array->d()->byteLength = l * elementSize; array->d()->byteOffset = 0; @@ -375,12 +375,6 @@ Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type return e->memoryManager->allocObject<TypedArray>(e->emptyClass, e->typedArrayPrototype + t, t); } -void TypedArray::markObjects(Heap::Base *that, ExecutionEngine *e) -{ - static_cast<TypedArray::Data *>(that)->buffer->mark(e); - Object::markObjects(that, e); -} - ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty) { Scope scope(static_cast<const Object *>(m)->engine()); diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index fbf13c9815..96786c8231 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -72,7 +72,15 @@ struct TypedArrayOperations { namespace Heap { -struct TypedArray : Object { +#define TypedArrayMembers(class, Member) \ + Member(class, Pointer, ArrayBuffer *, buffer) \ + Member(class, NoMark, const TypedArrayOperations *, type) \ + Member(class, NoMark, uint, byteLength) \ + Member(class, NoMark, uint, byteOffset) \ + Member(class, NoMark, uint, arrayType) + +DECLARE_HEAP_OBJECT(TypedArray, Object) { + DECLARE_MARK_TABLE(TypedArray); enum Type { Int8Array, UInt8Array, @@ -87,12 +95,6 @@ struct TypedArray : Object { }; void init(Type t); - - const TypedArrayOperations *type; - Pointer<ArrayBuffer> buffer; - uint byteLength; - uint byteOffset; - Type arrayType; }; struct TypedArrayCtor : FunctionObject { @@ -128,10 +130,9 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object } Heap::TypedArray::Type arrayType() const { - return d()->arrayType; + return static_cast<Heap::TypedArray::Type>(d()->arrayType); } - static void markObjects(Heap::Base *that, ExecutionEngine *e); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); static bool putIndexed(Managed *m, uint index, const Value &value); }; diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 4ff0565f9b..50cecb6598 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -68,8 +68,6 @@ namespace Heap { struct Base; } -typedef uint Bool; - struct Q_QML_PRIVATE_EXPORT Value { private: @@ -209,23 +207,23 @@ public: } QML_NEARLY_ALWAYS_INLINE void setInt_32(int i) { - setTagValue(Integer_Type_Internal, quint32(i)); + setTagValue(quint32(ValueTypeInternal::Integer), quint32(i)); } QML_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } QML_NEARLY_ALWAYS_INLINE void setEmpty() { - setTagValue(Empty_Type_Internal, value()); + setTagValue(quint32(ValueTypeInternal::Empty), value()); } QML_NEARLY_ALWAYS_INLINE void setEmpty(int i) { - setTagValue(Empty_Type_Internal, quint32(i)); + setTagValue(quint32(ValueTypeInternal::Empty), quint32(i)); } QML_NEARLY_ALWAYS_INLINE void setEmpty(quint32 i) { - setTagValue(Empty_Type_Internal, i); + setTagValue(quint32(ValueTypeInternal::Empty), i); } QML_NEARLY_ALWAYS_INLINE quint32 emptyValue() @@ -268,8 +266,17 @@ public: IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, Managed_Type_Internal_64 = 0 }; + static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 + enum class ValueTypeInternal_64 { + Empty = Immediate_Mask_64| 0, + ConvertibleToInt = Immediate_Mask_64| 0x10000u, // bit 48 + Null = ConvertibleToInt | 0x08000u, + Boolean = ConvertibleToInt | 0x04000u, + Integer = ConvertibleToInt | 0x02000u + }; + // Used only by 32-bit encoding enum Masks { SilentNaNBit = 0x00040000, @@ -277,6 +284,14 @@ public: }; static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; + enum class ValueTypeInternal_32 { + Empty = Immediate_Mask_32| 0, + ConvertibleToInt = Immediate_Mask_32| 0x10000u, // bit 48 + Null = ConvertibleToInt | 0x08000u, + Boolean = ConvertibleToInt | 0x04000u, + Integer = ConvertibleToInt | 0x02000u + }; + enum { Managed_Type_Internal_32 = NotDouble_Mask }; @@ -286,28 +301,23 @@ public: Managed_Type_Internal = Managed_Type_Internal_64 }; static const quint64 Immediate_Mask = Immediate_Mask_64; + using ValueTypeInternal = ValueTypeInternal_64; #else enum { Managed_Type_Internal = Managed_Type_Internal_32 }; static const quint64 Immediate_Mask = Immediate_Mask_32; + using ValueTypeInternal = ValueTypeInternal_32; #endif enum { NaN_Mask = 0x7ff80000, }; - enum ValueTypeInternal { - Empty_Type_Internal = Immediate_Mask | 0, - ConvertibleToInt = Immediate_Mask | 0x10000u, // bit 48 - Null_Type_Internal = ConvertibleToInt | 0x08000u, - Boolean_Type_Internal = ConvertibleToInt | 0x04000u, - Integer_Type_Internal = ConvertibleToInt | 0x02000u - }; // used internally in property - inline bool isEmpty() const { return tag() == Empty_Type_Internal; } - inline bool isNull() const { return tag() == Null_Type_Internal; } - inline bool isBoolean() const { return tag() == Boolean_Type_Internal; } - inline bool isInteger() const { return tag() == Integer_Type_Internal; } + inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } + inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } + inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } + inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } inline bool isNumber() const { return isDouble() || isInteger(); } @@ -332,9 +342,9 @@ public: inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; } inline bool isManaged() const { return tag() == Managed_Type_Internal && !isUndefined(); } inline bool isManagedOrUndefined() const { return tag() == Managed_Type_Internal; } - inline bool integerCompatible() const { return (tag() & ConvertibleToInt) == ConvertibleToInt; } + inline bool integerCompatible() const { return (tag() & quint32(ValueTypeInternal::ConvertibleToInt)) == quint32(ValueTypeInternal::ConvertibleToInt); } static inline bool integerCompatible(Value a, Value b) { - return ((a.tag() & b.tag()) & ConvertibleToInt) == ConvertibleToInt; + return ((a.tag() & b.tag()) & quint32(ValueTypeInternal::ConvertibleToInt)) == quint32(ValueTypeInternal::ConvertibleToInt); } static inline bool bothDouble(Value a, Value b) { return ((a.tag() | b.tag()) & NotDouble_Mask) != NotDouble_Mask; @@ -361,7 +371,7 @@ public: inline bool isString() const; inline bool isObject() const; inline bool isInt32() { - if (tag() == Integer_Type_Internal) + if (tag() == quint32(ValueTypeInternal::Integer)) return true; if (isDouble()) { double d = doubleValue(); @@ -374,7 +384,7 @@ public: return false; } double asDouble() const { - if (tag() == Integer_Type_Internal) + if (tag() == quint32(ValueTypeInternal::Integer)) return int_32(); return doubleValue(); } @@ -429,7 +439,7 @@ public: inline bool tryIntegerConversion() { bool b = integerCompatible(); if (b) - setTagValue(Integer_Type_Internal, value()); + setTagValue(quint32(ValueTypeInternal::Integer), value()); return b; } @@ -477,7 +487,7 @@ public: // Section 9.12 bool sameValue(Value other) const; - inline void mark(ExecutionEngine *e); + inline void mark(MarkStack *markStack); Value &operator =(const ScopedValue &v); Value &operator=(ReturnedValue v) { _val = v; return *this; } @@ -612,14 +622,14 @@ inline Primitive Primitive::emptyValue(uint e) inline Primitive Primitive::nullValue() { Primitive v; - v.setTagValue(Null_Type_Internal, 0); + v.setTagValue(quint32(ValueTypeInternal::Null), 0); return v; } inline Primitive Primitive::fromBoolean(bool b) { Primitive v; - v.setTagValue(Boolean_Type_Internal, b); + v.setTagValue(quint32(ValueTypeInternal::Boolean), b); return v; } @@ -641,7 +651,7 @@ inline Primitive Primitive::fromUInt32(uint i) { Primitive v; if (i < INT_MAX) { - v.setTagValue(Integer_Type_Internal, i); + v.setTagValue(quint32(ValueTypeInternal::Integer), i); } else { v.setDouble(i); } @@ -708,7 +718,6 @@ inline unsigned int Value::toUInt32() const return (unsigned int)toInt32(); } - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index 5cab4c5386..f2ff5d307e 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -84,7 +84,7 @@ bool VariantObject::isEqualTo(Managed *m, Managed *other) return false; } -void VariantObject::addVmePropertyReference() +void VariantObject::addVmePropertyReference() const { if (d()->isScarce() && ++d()->vmePropertyReferenceCount == 1) { // remove from the ep->scarceResources list @@ -94,7 +94,7 @@ void VariantObject::addVmePropertyReference() } } -void VariantObject::removeVmePropertyReference() +void VariantObject::removeVmePropertyReference() const { if (d()->isScarce() && --d()->vmePropertyReferenceCount == 0) { // and add to the ep->scarceResources list diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index ef51b6632d..e281602bb5 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -96,8 +96,8 @@ struct VariantObject : Object V4_PROTOTYPE(variantPrototype) V4_NEEDS_DESTROY - void addVmePropertyReference(); - void removeVmePropertyReference(); + void addVmePropertyReference() const; + void removeVmePropertyReference() const; static bool isEqualTo(Managed *m, Managed *other); }; diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index be2772c23f..e16df8dc60 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -249,10 +249,11 @@ int qt_v4DebuggerHook(const char *json) return -NoSuchCommand; // Failure. } -static void qt_v4CheckForBreak(QV4::ExecutionContext *context, QV4::Value **scopes, int scopeDepth) +static void qt_v4CheckForBreak(QV4::ExecutionContext *context) { - Q_UNUSED(scopes); - Q_UNUSED(scopeDepth); + if (!qt_v4IsStepping && !qt_v4Breakpoints.size()) + return; + const int lineNumber = context->d()->lineNumber; QV4::Function *function = qt_v4ExtractFunction(context); QString engineName = function->sourceFile(); @@ -335,18 +336,24 @@ Param traceParam(const Param ¶m) return param; } # define VALUE(param) (*VALUEPTR(param)) -# define VALUEPTR(param) (scopes[traceParam(param).scope] + param.index) +# define VALUEPTR(param) (scopes[traceParam(param).scope].values + param.index) #else # define VALUE(param) (*VALUEPTR(param)) -# define VALUEPTR(param) (scopes[param.scope] + param.index) +# define VALUEPTR(param) (scopes[param.scope].values + param.index) #endif +// ### add write barrier here #define STOREVALUE(param, value) { \ QV4::ReturnedValue tmp = (value); \ if (engine->hasException) \ goto catchException; \ - VALUE(param) = tmp; \ - } + if (Q_LIKELY(!engine->writeBarrierActive || !scopes[param.scope].base)) { \ + VALUE(param) = tmp; \ + } else { \ + QV4::WriteBarrier::write(engine, scopes[param.scope].base, VALUEPTR(param), QV4::Value::fromReturnedValue(tmp)); \ + } \ +} + // qv4scopedvalue_p.h also defines a CHECK_EXCEPTION macro #ifdef CHECK_EXCEPTION #undef CHECK_EXCEPTION @@ -402,21 +409,29 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code } } - Q_ALLOCA_VAR(QV4::Value*, scopes, sizeof(QV4::Value *)*(2 + 2*scopeDepth)); + struct Scopes { + QV4::Value *values; + QV4::Heap::Base *base; // non 0 if a write barrier is required + }; + Q_ALLOCA_VAR(Scopes, scopes, sizeof(Scopes)*(2 + 2*scopeDepth)); { - scopes[0] = const_cast<QV4::Value *>(context->d()->compilationUnit->constants); + scopes[0] = { const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(context->d()->compilationUnit)->constants), 0 }; // stack gets setup in push instruction - scopes[1] = 0; + scopes[1] = { 0, 0 }; QV4::Heap::ExecutionContext *scope = context->d(); int i = 0; while (scope) { - if (scope->type >= QV4::Heap::ExecutionContext::Type_SimpleCallContext) { + if (scope->type == QV4::Heap::ExecutionContext::Type_SimpleCallContext) { + QV4::Heap::SimpleCallContext *cc = static_cast<QV4::Heap::SimpleCallContext *>(scope); + scopes[2*i + 2] = { cc->callData->args, 0 }; + scopes[2*i + 3] = { 0, 0 }; + } else if (scope->type == QV4::Heap::ExecutionContext::Type_CallContext) { QV4::Heap::CallContext *cc = static_cast<QV4::Heap::CallContext *>(scope); - scopes[2*i + 2] = cc->callData->args; - scopes[2*i + 3] = cc->locals; + scopes[2*i + 2] = { cc->callData->args, cc }; + scopes[2*i + 3] = { cc->locals.values, cc }; } else { - scopes[2*i + 2] = 0; - scopes[2*i + 3] = 0; + scopes[2*i + 2] = { 0, 0 }; + scopes[2*i + 3] = { 0, 0 }; } ++i; scope = scope->outer; @@ -451,16 +466,16 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(LoadRegExp) // TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - VALUE(instr.result) = context->d()->compilationUnit->runtimeRegularExpressions[instr.regExpId]; + VALUE(instr.result) = static_cast<CompiledData::CompilationUnit*>(context->d()->compilationUnit)->runtimeRegularExpressions[instr.regExpId]; MOTH_END_INSTR(LoadRegExp) MOTH_BEGIN_INSTR(LoadClosure) - STOREVALUE(instr.result, engine->runtime.closure(engine, instr.value)); + STOREVALUE(instr.result, Runtime::method_closure(engine, instr.value)); MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData()); - STOREVALUE(instr.result, engine->runtime.getActivationProperty(engine, instr.name)); + STOREVALUE(instr.result, Runtime::method_getActivationProperty(engine, instr.name)); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(GetGlobalLookup) @@ -470,32 +485,32 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(StoreName) TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData()); - engine->runtime.setActivationProperty(engine, instr.name, VALUE(instr.source)); + Runtime::method_setActivationProperty(engine, instr.name, VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreName) MOTH_BEGIN_INSTR(LoadElement) - STOREVALUE(instr.result, engine->runtime.getElement(engine, VALUE(instr.base), VALUE(instr.index))); + STOREVALUE(instr.result, Runtime::method_getElement(engine, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(LoadElementLookup) QV4::Lookup *l = context->d()->lookups + instr.lookup; - STOREVALUE(instr.result, l->indexedGetter(l, VALUE(instr.base), VALUE(instr.index))); + STOREVALUE(instr.result, l->indexedGetter(l, engine, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(LoadElementLookup) MOTH_BEGIN_INSTR(StoreElement) - engine->runtime.setElement(engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + Runtime::method_setElement(engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(StoreElementLookup) QV4::Lookup *l = context->d()->lookups + instr.lookup; - l->indexedSetter(l, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + l->indexedSetter(l, engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElementLookup) MOTH_BEGIN_INSTR(LoadProperty) - STOREVALUE(instr.result, engine->runtime.getProperty(engine, VALUE(instr.base), instr.name)); + STOREVALUE(instr.result, Runtime::method_getProperty(engine, VALUE(instr.base), instr.name)); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) @@ -504,7 +519,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) - engine->runtime.setProperty(engine, VALUE(instr.base), instr.name, VALUE(instr.source)); + Runtime::method_setProperty(engine, VALUE(instr.base), instr.name, VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreProperty) @@ -515,49 +530,49 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(SetLookup) MOTH_BEGIN_INSTR(StoreQObjectProperty) - engine->runtime.setQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + Runtime::method_setQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreQObjectProperty) MOTH_BEGIN_INSTR(LoadQObjectProperty) - STOREVALUE(instr.result, engine->runtime.getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); + STOREVALUE(instr.result, Runtime::method_getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); MOTH_END_INSTR(LoadQObjectProperty) MOTH_BEGIN_INSTR(StoreScopeObjectProperty) - engine->runtime.setQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + Runtime::method_setQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreScopeObjectProperty) MOTH_BEGIN_INSTR(LoadScopeObjectProperty) - STOREVALUE(instr.result, engine->runtime.getQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); + STOREVALUE(instr.result, Runtime::method_getQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); MOTH_END_INSTR(LoadScopeObjectProperty) MOTH_BEGIN_INSTR(StoreContextObjectProperty) - engine->runtime.setQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + Runtime::method_setQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); CHECK_EXCEPTION; MOTH_END_INSTR(StoreContextObjectProperty) MOTH_BEGIN_INSTR(LoadContextObjectProperty) - STOREVALUE(instr.result, engine->runtime.getQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); + STOREVALUE(instr.result, Runtime::method_getQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); MOTH_END_INSTR(LoadContextObjectProperty) MOTH_BEGIN_INSTR(LoadIdObject) - STOREVALUE(instr.result, engine->runtime.getQmlIdObject(engine, VALUE(instr.base), instr.index)); + STOREVALUE(instr.result, Runtime::method_getQmlIdObject(engine, VALUE(instr.base), instr.index)); MOTH_END_INSTR(LoadIdObject) MOTH_BEGIN_INSTR(LoadAttachedQObjectProperty) - STOREVALUE(instr.result, engine->runtime.getQmlAttachedProperty(engine, instr.attachedPropertiesId, instr.propertyIndex)); + STOREVALUE(instr.result, Runtime::method_getQmlAttachedProperty(engine, instr.attachedPropertiesId, instr.propertyIndex)); MOTH_END_INSTR(LoadAttachedQObjectProperty) MOTH_BEGIN_INSTR(LoadSingletonQObjectProperty) - STOREVALUE(instr.result, engine->runtime.getQmlSingletonQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); + STOREVALUE(instr.result, Runtime::method_getQmlSingletonQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); MOTH_END_INSTR(LoadSingletonQObjectProperty) MOTH_BEGIN_INSTR(Push) TRACE(inline, "stack size: %u", instr.value); stackSize = instr.value; stack = scope.alloc(stackSize); - scopes[1] = stack; + scopes[1].values = stack; MOTH_END_INSTR(Push) MOTH_BEGIN_INSTR(CallValue) @@ -571,75 +586,75 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code } } #endif // DO_TRACE_INSTR - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, engine->runtime.callValue(engine, VALUE(instr.dest), callData)); + STOREVALUE(instr.result, Runtime::method_callValue(engine, VALUE(instr.dest), callData)); 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()); - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, engine->runtime.callProperty(engine, instr.name, callData)); + STOREVALUE(instr.result, Runtime::method_callProperty(engine, instr.name, callData)); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, engine->runtime.callPropertyLookup(engine, instr.lookupIndex, callData)); + STOREVALUE(instr.result, Runtime::method_callPropertyLookup(engine, instr.lookupIndex, callData)); 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()); - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, engine->runtime.callQmlScopeObjectProperty(engine, instr.index, callData)); + STOREVALUE(instr.result, Runtime::method_callQmlScopeObjectProperty(engine, instr.index, callData)); 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()); - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, engine->runtime.callQmlContextObjectProperty(engine, instr.index, callData)); + STOREVALUE(instr.result, Runtime::method_callQmlContextObjectProperty(engine, instr.index, callData)); MOTH_END_INSTR(CallContextObjectProperty) MOTH_BEGIN_INSTR(CallElement) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, engine->runtime.callElement(engine, VALUE(instr.index), callData)); + STOREVALUE(instr.result, Runtime::method_callElement(engine, VALUE(instr.index), callData)); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallActivationProperty) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, engine->runtime.callActivationProperty(engine, instr.name, callData)); + STOREVALUE(instr.result, Runtime::method_callActivationProperty(engine, instr.name, callData)); MOTH_END_INSTR(CallActivationProperty) MOTH_BEGIN_INSTR(CallGlobalLookup) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::method_callGlobalLookup(engine, instr.index, callData)); @@ -650,141 +665,141 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(SetExceptionHandler) MOTH_BEGIN_INSTR(CallBuiltinThrow) - engine->runtime.throwException(engine, VALUE(instr.arg)); + Runtime::method_throwException(engine, VALUE(instr.arg)); CHECK_EXCEPTION; MOTH_END_INSTR(CallBuiltinThrow) MOTH_BEGIN_INSTR(CallBuiltinUnwindException) - STOREVALUE(instr.result, engine->runtime.unwindException(engine)); + STOREVALUE(instr.result, Runtime::method_unwindException(engine)); MOTH_END_INSTR(CallBuiltinUnwindException) MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope) - engine->runtime.pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name); + Runtime::method_pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name); context = engine->currentContext; MOTH_END_INSTR(CallBuiltinPushCatchScope) MOTH_BEGIN_INSTR(CallBuiltinPushScope) - engine->runtime.pushWithScope(VALUE(instr.arg), static_cast<QV4::NoThrowEngine*>(engine)); + Runtime::method_pushWithScope(VALUE(instr.arg), static_cast<QV4::NoThrowEngine*>(engine)); context = engine->currentContext; CHECK_EXCEPTION; MOTH_END_INSTR(CallBuiltinPushScope) MOTH_BEGIN_INSTR(CallBuiltinPopScope) - engine->runtime.popScope(static_cast<QV4::NoThrowEngine*>(engine)); + Runtime::method_popScope(static_cast<QV4::NoThrowEngine*>(engine)); context = engine->currentContext; MOTH_END_INSTR(CallBuiltinPopScope) MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) - STOREVALUE(instr.result, engine->runtime.foreachIterator(engine, VALUE(instr.arg))); + STOREVALUE(instr.result, Runtime::method_foreachIterator(engine, VALUE(instr.arg))); MOTH_END_INSTR(CallBuiltinForeachIteratorObject) MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) - STOREVALUE(instr.result, engine->runtime.foreachNextPropertyName(VALUE(instr.arg))); + STOREVALUE(instr.result, Runtime::method_foreachNextPropertyName(VALUE(instr.arg))); MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) - STOREVALUE(instr.result, engine->runtime.deleteMember(engine, VALUE(instr.base), instr.member)); + STOREVALUE(instr.result, Runtime::method_deleteMember(engine, VALUE(instr.base), instr.member)); MOTH_END_INSTR(CallBuiltinDeleteMember) MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) - STOREVALUE(instr.result, engine->runtime.deleteElement(engine, VALUE(instr.base), VALUE(instr.index))); + STOREVALUE(instr.result, Runtime::method_deleteElement(engine, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(CallBuiltinDeleteSubscript) MOTH_BEGIN_INSTR(CallBuiltinDeleteName) - STOREVALUE(instr.result, engine->runtime.deleteName(engine, instr.name)); + STOREVALUE(instr.result, Runtime::method_deleteName(engine, instr.name)); MOTH_END_INSTR(CallBuiltinDeleteName) MOTH_BEGIN_INSTR(CallBuiltinTypeofScopeObjectProperty) - STOREVALUE(instr.result, engine->runtime.typeofScopeObjectProperty(engine, VALUE(instr.base), instr.index)); + STOREVALUE(instr.result, Runtime::method_typeofScopeObjectProperty(engine, VALUE(instr.base), instr.index)); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinTypeofContextObjectProperty) - STOREVALUE(instr.result, engine->runtime.typeofContextObjectProperty(engine, VALUE(instr.base), instr.index)); + STOREVALUE(instr.result, Runtime::method_typeofContextObjectProperty(engine, VALUE(instr.base), instr.index)); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) - STOREVALUE(instr.result, engine->runtime.typeofMember(engine, VALUE(instr.base), instr.member)); + STOREVALUE(instr.result, Runtime::method_typeofMember(engine, VALUE(instr.base), instr.member)); MOTH_END_INSTR(CallBuiltinTypeofMember) MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) - STOREVALUE(instr.result, engine->runtime.typeofElement(engine, VALUE(instr.base), VALUE(instr.index))); + STOREVALUE(instr.result, Runtime::method_typeofElement(engine, VALUE(instr.base), VALUE(instr.index))); MOTH_END_INSTR(CallBuiltinTypeofSubscript) MOTH_BEGIN_INSTR(CallBuiltinTypeofName) - STOREVALUE(instr.result, engine->runtime.typeofName(engine, instr.name)); + STOREVALUE(instr.result, Runtime::method_typeofName(engine, instr.name)); MOTH_END_INSTR(CallBuiltinTypeofName) MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) - STOREVALUE(instr.result, engine->runtime.typeofValue(engine, VALUE(instr.value))); + STOREVALUE(instr.result, Runtime::method_typeofValue(engine, VALUE(instr.value))); MOTH_END_INSTR(CallBuiltinTypeofValue) MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) - engine->runtime.declareVar(engine, instr.isDeletable, instr.varName); + Runtime::method_declareVar(engine, instr.isDeletable, instr.varName); MOTH_END_INSTR(CallBuiltinDeclareVar) MOTH_BEGIN_INSTR(CallBuiltinDefineArray) Q_ASSERT(instr.args + instr.argc <= stackSize); QV4::Value *args = stack + instr.args; - STOREVALUE(instr.result, engine->runtime.arrayLiteral(engine, args, instr.argc)); + STOREVALUE(instr.result, Runtime::method_arrayLiteral(engine, args, instr.argc)); MOTH_END_INSTR(CallBuiltinDefineArray) MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral) QV4::Value *args = stack + instr.args; - STOREVALUE(instr.result, engine->runtime.objectLiteral(engine, args, instr.internalClassId, instr.arrayValueCount, instr.arrayGetterSetterCountAndFlags)); + STOREVALUE(instr.result, Runtime::method_objectLiteral(engine, args, instr.internalClassId, instr.arrayValueCount, instr.arrayGetterSetterCountAndFlags)); MOTH_END_INSTR(CallBuiltinDefineObjectLiteral) MOTH_BEGIN_INSTR(CallBuiltinSetupArgumentsObject) - STOREVALUE(instr.result, engine->runtime.setupArgumentsObject(engine)); + STOREVALUE(instr.result, Runtime::method_setupArgumentsObject(engine)); MOTH_END_INSTR(CallBuiltinSetupArgumentsObject) MOTH_BEGIN_INSTR(CallBuiltinConvertThisToObject) - engine->runtime.convertThisToObject(engine); + Runtime::method_convertThisToObject(engine); CHECK_EXCEPTION; MOTH_END_INSTR(CallBuiltinConvertThisToObject) MOTH_BEGIN_INSTR(CreateValue) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, engine->runtime.constructValue(engine, VALUE(instr.func), callData)); + STOREVALUE(instr.result, Runtime::method_constructValue(engine, VALUE(instr.func), callData)); MOTH_END_INSTR(CreateValue) MOTH_BEGIN_INSTR(CreateProperty) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, engine->runtime.constructProperty(engine, instr.name, callData)); + STOREVALUE(instr.result, Runtime::method_constructProperty(engine, instr.name, callData)); MOTH_END_INSTR(CreateProperty) MOTH_BEGIN_INSTR(ConstructPropertyLookup) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, engine->runtime.constructPropertyLookup(engine, instr.index, callData)); + STOREVALUE(instr.result, Runtime::method_constructPropertyLookup(engine, instr.index, callData)); MOTH_END_INSTR(ConstructPropertyLookup) MOTH_BEGIN_INSTR(CreateActivationProperty) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, engine->runtime.constructActivationProperty(engine, instr.name, callData)); + STOREVALUE(instr.result, Runtime::method_constructActivationProperty(engine, instr.name, callData)); MOTH_END_INSTR(CreateActivationProperty) MOTH_BEGIN_INSTR(ConstructGlobalLookup) - Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); + 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 = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, engine->runtime.constructGlobalLookup(engine, instr.index, callData)); + STOREVALUE(instr.result, Runtime::method_constructGlobalLookup(engine, instr.index, callData)); MOTH_END_INSTR(ConstructGlobalLookup) MOTH_BEGIN_INSTR(Jump) @@ -806,7 +821,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(JumpNe) MOTH_BEGIN_INSTR(UNot) - STOREVALUE(instr.result, engine->runtime.uNot(VALUE(instr.source))); + STOREVALUE(instr.result, Runtime::method_uNot(VALUE(instr.source))); MOTH_END_INSTR(UNot) MOTH_BEGIN_INSTR(UNotBool) @@ -815,15 +830,15 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(UNotBool) MOTH_BEGIN_INSTR(UPlus) - STOREVALUE(instr.result, engine->runtime.uPlus(VALUE(instr.source))); + STOREVALUE(instr.result, Runtime::method_uPlus(VALUE(instr.source))); MOTH_END_INSTR(UPlus) MOTH_BEGIN_INSTR(UMinus) - STOREVALUE(instr.result, engine->runtime.uMinus(VALUE(instr.source))); + STOREVALUE(instr.result, Runtime::method_uMinus(VALUE(instr.source))); MOTH_END_INSTR(UMinus) MOTH_BEGIN_INSTR(UCompl) - STOREVALUE(instr.result, engine->runtime.complement(VALUE(instr.source))); + STOREVALUE(instr.result, Runtime::method_complement(VALUE(instr.source))); MOTH_END_INSTR(UCompl) MOTH_BEGIN_INSTR(UComplInt) @@ -831,32 +846,32 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(UComplInt) MOTH_BEGIN_INSTR(Increment) - STOREVALUE(instr.result, engine->runtime.increment(VALUE(instr.source))); + STOREVALUE(instr.result, Runtime::method_increment(VALUE(instr.source))); MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) - STOREVALUE(instr.result, engine->runtime.decrement(VALUE(instr.source))); + STOREVALUE(instr.result, Runtime::method_decrement(VALUE(instr.source))); MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Binop) - QV4::Runtime::BinaryOperation op = *reinterpret_cast<QV4::Runtime::BinaryOperation *>(reinterpret_cast<char *>(&engine->runtime) + instr.alu); + QV4::Runtime::BinaryOperation op = *reinterpret_cast<QV4::Runtime::BinaryOperation *>(reinterpret_cast<char *>(&engine->runtime.runtimeMethods[instr.alu])); STOREVALUE(instr.result, op(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(Binop) MOTH_BEGIN_INSTR(Add) - STOREVALUE(instr.result, engine->runtime.add(engine, VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, Runtime::method_add(engine, VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(BitAnd) - STOREVALUE(instr.result, engine->runtime.bitAnd(VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, Runtime::method_bitAnd(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(BitAnd) MOTH_BEGIN_INSTR(BitOr) - STOREVALUE(instr.result, engine->runtime.bitOr(VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, Runtime::method_bitOr(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(BitOr) MOTH_BEGIN_INSTR(BitXor) - STOREVALUE(instr.result, engine->runtime.bitXor(VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, Runtime::method_bitXor(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(BitXor) MOTH_BEGIN_INSTR(Shr) @@ -891,15 +906,15 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(ShlConst) MOTH_BEGIN_INSTR(Mul) - STOREVALUE(instr.result, engine->runtime.mul(VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, Runtime::method_mul(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Sub) - STOREVALUE(instr.result, engine->runtime.sub(VALUE(instr.lhs), VALUE(instr.rhs))); + STOREVALUE(instr.result, Runtime::method_sub(VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(Sub) MOTH_BEGIN_INSTR(BinopContext) - QV4::Runtime::BinaryOperationContext op = *reinterpret_cast<QV4::Runtime::BinaryOperationContext *>(reinterpret_cast<char *>(&engine->runtime) + instr.alu); + QV4::Runtime::BinaryOperationContext op = *reinterpret_cast<QV4::Runtime::BinaryOperationContext *>(reinterpret_cast<char *>(&engine->runtime.runtimeMethods[instr.alu])); STOREVALUE(instr.result, op(engine, VALUE(instr.lhs), VALUE(instr.rhs))); MOTH_END_INSTR(BinopContext) @@ -915,13 +930,13 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code if (debugger && debugger->pauseAtNextOpportunity()) debugger->maybeBreakAtInstruction(); if (qt_v4IsDebugging) - qt_v4CheckForBreak(context, scopes, scopeDepth); + qt_v4CheckForBreak(context); MOTH_END_INSTR(Debug) MOTH_BEGIN_INSTR(Line) engine->current->lineNumber = instr.lineNumber; if (qt_v4IsDebugging) - qt_v4CheckForBreak(context, scopes, scopeDepth); + qt_v4CheckForBreak(context); MOTH_END_INSTR(Line) #endif // QT_NO_QML_DEBUGGER @@ -930,15 +945,15 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_END_INSTR(LoadThis) MOTH_BEGIN_INSTR(LoadQmlContext) - VALUE(instr.result) = engine->runtime.getQmlContext(static_cast<QV4::NoThrowEngine*>(engine)); + VALUE(instr.result) = Runtime::method_getQmlContext(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlContext) MOTH_BEGIN_INSTR(LoadQmlImportedScripts) - VALUE(instr.result) = engine->runtime.getQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); + VALUE(instr.result) = Runtime::method_getQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlImportedScripts) MOTH_BEGIN_INSTR(LoadQmlSingleton) - VALUE(instr.result) = engine->runtime.getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name); + VALUE(instr.result) = Runtime::method_getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name); MOTH_END_INSTR(LoadQmlSingleton) #ifdef MOTH_THREADED_INTERPRETER diff --git a/src/qml/memory/memory.pri b/src/qml/memory/memory.pri index 38fadbf23f..7956e4a9a1 100644 --- a/src/qml/memory/memory.pri +++ b/src/qml/memory/memory.pri @@ -7,7 +7,8 @@ SOURCES += \ HEADERS += \ $$PWD/qv4mm_p.h \ - $$PWD/qv4mmdefs_p.h + $$PWD/qv4mmdefs_p.h \ + $$PWD/qv4writebarrier_p.h } HEADERS += \ diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 8285ef4de7..a38a938588 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -72,6 +72,7 @@ namespace QV4 { struct VTable { const VTable * const parent; + const quint64 markTable; uint isExecutionContext : 1; uint isString : 1; uint isObject : 1; @@ -82,7 +83,7 @@ struct VTable uint type : 8; const char *className; void (*destroy)(Heap::Base *); - void (*markObjects)(Heap::Base *, ExecutionEngine *e); + void (*markObjects)(Heap::Base *, MarkStack *markStack); bool (*isEqualTo)(Managed *m, Managed *other); }; @@ -91,10 +92,12 @@ namespace Heap { struct Q_QML_EXPORT Base { void *operator new(size_t) = delete; + static Q_CONSTEXPR quint64 markTable = 0; + const VTable *vt; inline ReturnedValue asReturnedValue() const; - inline void mark(QV4::ExecutionEngine *engine); + inline void mark(QV4::MarkStack *markStack); void setVtable(const VTable *v) { vt = v; } const VTable *vtable() const { return vt; } @@ -110,6 +113,12 @@ struct Q_QML_EXPORT Base { Q_ASSERT(!Chunk::testBit(c->extendsBitmap, h - c->realBase())); return Chunk::setBit(c->blackBitmap, h - c->realBase()); } + inline void setGrayBit() { + const HeapItem *h = reinterpret_cast<const HeapItem *>(this); + Chunk *c = h->chunk(); + Q_ASSERT(!Chunk::testBit(c->extendsBitmap, h - c->realBase())); + return Chunk::setBit(c->grayBitmap, h - c->realBase()); + } inline bool inUse() const { const HeapItem *h = reinterpret_cast<const HeapItem *>(this); @@ -118,6 +127,8 @@ struct Q_QML_EXPORT Base { return Chunk::testBit(c->objectBitmap, h - c->realBase()); } + inline void markChildren(MarkStack *markStack); + void *operator new(size_t, Managed *m) { return m; } void *operator new(size_t, Heap::Base *m) { return m; } void operator delete(void *, Heap::Base *) {} @@ -133,7 +144,7 @@ struct Q_QML_EXPORT Base { else if (_livenessStatus == Destroyed) fprintf(stderr, "ERROR: use of object '%s' after call to destroy() !!\n", vtable()->className); - Q_ASSERT(_livenessStatus = Initialized); + Q_ASSERT(_livenessStatus == Initialized); } void _checkIsDestroyed() { if (_livenessStatus == Initialized) @@ -160,20 +171,12 @@ struct Q_QML_EXPORT Base { #endif }; V4_ASSERT_IS_TRIVIAL(Base) - -template <typename T> -struct Pointer { - T *operator->() const { return ptr; } - operator T *() const { return ptr; } - - Pointer &operator =(T *t) { ptr = t; return *this; } - - template <typename Type> - Type *cast() { return static_cast<Type *>(ptr); } - - T *ptr; -}; -V4_ASSERT_IS_TRIVIAL(Pointer<void>) +// This class needs to consist only of pointer sized members to allow +// 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(sizeof(Base) == QT_POINTER_SIZE); } diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index a829e902fb..7c57b93d9b 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -60,7 +60,11 @@ #include "qv4alloca_p.h" #include "qv4profiling_p.h" -#define MM_DEBUG 0 +//#define MM_STATS + +#if !defined(MM_STATS) && !defined(QT_NO_DEBUG) +#define MM_STATS +#endif #if MM_DEBUG #define DEBUG qDebug() << "MM:" @@ -113,14 +117,16 @@ struct MemorySegment { pageReservation = PageReservation::reserve(size, OSAllocator::JSGCHeapPages); base = reinterpret_cast<Chunk *>((reinterpret_cast<quintptr>(pageReservation.base()) + Chunk::ChunkSize - 1) & ~(Chunk::ChunkSize - 1)); nChunks = NumChunks; - if (base != pageReservation.base()) + availableBytes = size - (reinterpret_cast<quintptr>(base) - reinterpret_cast<quintptr>(pageReservation.base())); + if (availableBytes < SegmentSize) --nChunks; } MemorySegment(MemorySegment &&other) { qSwap(pageReservation, other.pageReservation); qSwap(base, other.base); - qSwap(nChunks, other.nChunks); qSwap(allocatedMap, other.allocatedMap); + qSwap(availableBytes, other.availableBytes); + qSwap(nChunks, other.nChunks); } ~MemorySegment() { @@ -150,7 +156,7 @@ struct MemorySegment { void free(Chunk *chunk, size_t size) { DEBUG << "freeing chunk" << chunk; size_t index = static_cast<size_t>(chunk - base); - size_t end = index + (size - 1)/Chunk::ChunkSize + 1; + size_t end = qMin(static_cast<size_t>(NumChunks), index + (size - 1)/Chunk::ChunkSize + 1); while (index < end) { Q_ASSERT(testBit(index)); clearBit(index); @@ -169,11 +175,19 @@ struct MemorySegment { PageReservation pageReservation; Chunk *base = 0; quint64 allocatedMap = 0; + size_t availableBytes = 0; uint nChunks = 0; }; Chunk *MemorySegment::allocate(size_t size) { + if (!allocatedMap && size >= SegmentSize) { + // chunk allocated for one huge allocation + Q_ASSERT(availableBytes >= size); + pageReservation.commit(base, size); + allocatedMap = ~static_cast<quintptr>(0); + return base; + } size_t requiredChunks = (size + sizeof(Chunk) - 1)/sizeof(Chunk); uint sequence = 0; Chunk *candidate = 0; @@ -246,12 +260,73 @@ void ChunkAllocator::free(Chunk *chunk, size_t size) } +void Heap::Base::markChildren(MarkStack *markStack) +{ + if (vtable()->markObjects) + vtable()->markObjects(this, markStack); + if (quint64 m = vtable()->markTable) { +// qDebug() << "using mark table:" << hex << m << "for" << h; + void **mem = reinterpret_cast<void **>(this); + while (m) { + MarkFlags mark = static_cast<MarkFlags>(m & 3); + switch (mark) { + case Mark_NoMark: + break; + case Mark_Value: +// qDebug() << "marking value at " << mem; + reinterpret_cast<Value *>(mem)->mark(markStack); + break; + case Mark_Pointer: { +// qDebug() << "marking pointer at " << mem; + Heap::Base *p = *reinterpret_cast<Heap::Base **>(mem); + if (p) + p->mark(markStack); + break; + } + case Mark_ValueArray: { + Q_ASSERT(m == Mark_ValueArray); +// qDebug() << "marking Value Array at offset" << hex << (mem - reinterpret_cast<void **>(h)); + ValueArray<0> *a = reinterpret_cast<ValueArray<0> *>(mem); + Value *v = a->values; + const Value *end = v + a->alloc; + if (a->alloc > 32*1024) { + // drain from time to time to avoid overflows in the js stack + Heap::Base **currentBase = markStack->top; + while (v < end) { + v->mark(markStack); + ++v; + if (markStack->top >= currentBase + 32*1024) { + Heap::Base **oldBase = markStack->base; + markStack->base = currentBase; + markStack->drain(); + markStack->base = oldBase; + } + } + } else { + while (v < end) { + v->mark(markStack); + ++v; + } + } + break; + } + } + + m >>= 2; + ++mem; + } + } +} + + void Chunk::sweep() { // DEBUG << "sweeping chunk" << this << (*freeList); HeapItem *o = realBase(); for (uint i = 0; i < Chunk::EntriesInBitmap; ++i) { +#if WRITEBARRIER(none) Q_ASSERT((grayBitmap[i] | blackBitmap[i]) == blackBitmap[i]); // check that we don't have gray only objects +#endif quintptr toFree = objectBitmap[i] ^ blackBitmap[i]; Q_ASSERT((toFree & objectBitmap[i]) == toFree); // check all black objects are marked as being used quintptr e = extendsBitmap[i]; @@ -280,7 +355,7 @@ void Chunk::sweep() } } objectBitmap[i] = blackBitmap[i]; - blackBitmap[i] = 0; + grayBitmap[i] = 0; extendsBitmap[i] = e; o += Chunk::Bits; } @@ -319,13 +394,56 @@ void Chunk::freeAll() } } objectBitmap[i] = 0; - blackBitmap[i] = 0; + grayBitmap[i] = 0; extendsBitmap[i] = e; o += Chunk::Bits; } // DEBUG << "swept chunk" << this << "freed" << slotsFreed << "slots."; } +void Chunk::resetBlackBits() +{ + memset(blackBitmap, 0, sizeof(blackBitmap)); +} + +#ifdef MM_STATS +static uint nGrayItems = 0; +#endif + +void Chunk::collectGrayItems(MarkStack *markStack) +{ + // DEBUG << "sweeping chunk" << this << (*freeList); + HeapItem *o = realBase(); + for (uint i = 0; i < Chunk::EntriesInBitmap; ++i) { +#if WRITEBARRIER(none) + Q_ASSERT((grayBitmap[i] | blackBitmap[i]) == blackBitmap[i]); // check that we don't have gray only objects +#endif + quintptr toMark = blackBitmap[i] & grayBitmap[i]; // correct for a Steele type barrier + Q_ASSERT((toMark & objectBitmap[i]) == toMark); // check all black objects are marked as being used + // DEBUG << hex << " index=" << i << toFree; + while (toMark) { + uint index = qCountTrailingZeroBits(toMark); + quintptr bit = (static_cast<quintptr>(1) << index); + + toMark ^= bit; // mask out marked slot + // DEBUG << " index" << hex << index << toFree; + + HeapItem *itemToFree = o + index; + Heap::Base *b = *itemToFree; + Q_ASSERT(b->inUse()); + markStack->push(b); +#ifdef MM_STATS + ++nGrayItems; +// qDebug() << "adding gray item" << b << "to mark stack"; +#endif + } + grayBitmap[i] = 0; + o += Chunk::Bits; + } + // DEBUG << "swept chunk" << this << "freed" << slotsFreed << "slots."; + +} + void Chunk::sortIntoBins(HeapItem **bins, uint nBins) { // qDebug() << "sortIntoBins:"; @@ -335,7 +453,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) #else const int start = 1; #endif -#ifndef QT_NO_DEBUG +#ifdef MM_STATS uint freeSlots = 0; uint allocatedSlots = 0; #endif @@ -345,7 +463,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) if (!i) usedSlots |= (static_cast<quintptr>(1) << (HeaderSize/SlotSize)) - 1; #endif -#ifndef QT_NO_DEBUG +#ifdef MM_STATS allocatedSlots += qPopulationCount(usedSlots); // qDebug() << hex << " i=" << i << "used=" << usedSlots; #endif @@ -362,7 +480,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) break; } usedSlots = (objectBitmap[i]|extendsBitmap[i]); -#ifndef QT_NO_DEBUG +#ifdef MM_STATS allocatedSlots += qPopulationCount(usedSlots); // qDebug() << hex << " i=" << i << "used=" << usedSlots; #endif @@ -373,7 +491,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) usedSlots |= (quintptr(1) << index) - 1; uint freeEnd = i*Bits + index; uint nSlots = freeEnd - freeStart; -#ifndef QT_NO_DEBUG +#ifdef MM_STATS // qDebug() << hex << " got free slots from" << freeStart << "to" << freeEnd << "n=" << nSlots << "usedSlots=" << usedSlots; freeSlots += nSlots; #endif @@ -384,7 +502,7 @@ void Chunk::sortIntoBins(HeapItem **bins, uint nBins) bins[bin] = freeItem; } } -#ifndef QT_NO_DEBUG +#ifdef MM_STATS Q_ASSERT(freeSlots + allocatedSlots == (EntriesInBitmap - start) * 8 * sizeof(quintptr)); #endif } @@ -555,6 +673,19 @@ void BlockAllocator::freeAll() } } +void BlockAllocator::resetBlackBits() +{ + for (auto c : chunks) + c->resetBlackBits(); +} + +void BlockAllocator::collectGrayItems(MarkStack *markStack) +{ + for (auto c : chunks) + c->collectGrayItems(markStack); + +} + #if MM_DEBUG void BlockAllocator::stats() { DEBUG << "MM stats:"; @@ -607,7 +738,6 @@ static void freeHugeChunk(ChunkAllocator *chunkAllocator, const HugeItemAllocato void HugeItemAllocator::sweep() { auto isBlack = [this] (const HugeChunk &c) { bool b = c.chunk->first()->isBlack(); - Chunk::clearBit(c.chunk->blackBitmap, c.chunk->first() - c.chunk->realBase()); if (!b) freeHugeChunk(chunkAllocator, c); return !b; @@ -617,6 +747,24 @@ void HugeItemAllocator::sweep() { chunks.erase(newEnd, chunks.end()); } +void HugeItemAllocator::resetBlackBits() +{ + for (auto c : chunks) + Chunk::clearBit(c.chunk->blackBitmap, c.chunk->first() - c.chunk->realBase()); +} + +void HugeItemAllocator::collectGrayItems(MarkStack *markStack) +{ + for (auto c : chunks) + // Correct for a Steele type barrier + if (Chunk::testBit(c.chunk->blackBitmap, c.chunk->first() - c.chunk->realBase()) && + Chunk::testBit(c.chunk->grayBitmap, c.chunk->first() - c.chunk->realBase())) { + HeapItem *i = c.chunk->first(); + Heap::Base *b = *i; + b->mark(markStack); + } +} + void HugeItemAllocator::freeAll() { for (auto &c : chunks) { @@ -642,15 +790,17 @@ MemoryManager::MemoryManager(ExecutionEngine *engine) #endif } -#ifndef QT_NO_DEBUG +#ifdef MM_STATS +static int allocationCount = 0; static size_t lastAllocRequestedSlots = 0; #endif Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize) { const size_t stringSize = align(sizeof(Heap::String)); -#ifndef QT_NO_DEBUG +#ifdef MM_STATS lastAllocRequestedSlots = stringSize >> Chunk::SlotSizeShift; + ++allocationCount; #endif bool didGCRun = false; @@ -661,7 +811,8 @@ Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize) unmanagedHeapSize += unmanagedSize; if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) { - runGC(); + if (!didGCRun) + runGC(); if (3*unmanagedHeapSizeGCLimit <= 4*unmanagedHeapSize) // more than 75% full, raise limit @@ -679,14 +830,16 @@ Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize) m = blockAllocator.allocate(stringSize, true); } +// qDebug() << "allocated string" << m; memset(m, 0, stringSize); return *m; } Heap::Base *MemoryManager::allocData(std::size_t size) { -#ifndef QT_NO_DEBUG +#ifdef MM_STATS lastAllocRequestedSlots = size >> Chunk::SlotSizeShift; + ++allocationCount; #endif bool didRunGC = false; @@ -703,8 +856,11 @@ Heap::Base *MemoryManager::allocData(std::size_t size) // qDebug() << "unmanagedHeapSize:" << unmanagedHeapSize << "limit:" << unmanagedHeapSizeGCLimit << "unmanagedSize:" << unmanagedSize; - if (size > Chunk::DataSize) - return *hugeItemAllocator.allocate(size); + if (size > Chunk::DataSize) { + HeapItem *h = hugeItemAllocator.allocate(size); +// qDebug() << "allocating huge item" << h; + return *h; + } HeapItem *m = blockAllocator.allocate(size); if (!m) { @@ -714,51 +870,77 @@ Heap::Base *MemoryManager::allocData(std::size_t size) } memset(m, 0, size); +// qDebug() << "allocating data" << m; return *m; } Heap::Object *MemoryManager::allocObjectWithMemberData(std::size_t size, uint nMembers) { - Heap::Object *o = static_cast<Heap::Object *>(allocData(size)); - - // ### Could optimize this and allocate both in one go through the block allocator - if (nMembers) { + Heap::Object *o; + if (!nMembers) { + o = static_cast<Heap::Object *>(allocData(size)); + } else { + // Allocate both in one go through the block allocator std::size_t memberSize = align(sizeof(Heap::MemberData) + (nMembers - 1)*sizeof(Value)); -// qDebug() << "allocating member data for" << o << nMembers << memberSize; - Heap::Base *m; - if (memberSize > Chunk::DataSize) - m = *hugeItemAllocator.allocate(memberSize); - else - m = *blockAllocator.allocate(memberSize, true); - memset(m, 0, memberSize); - o->memberData = static_cast<Heap::MemberData *>(m); - o->memberData->setVtable(MemberData::staticVTable()); - o->memberData->size = static_cast<uint>((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); - o->memberData->init(); + size_t totalSize = size + memberSize; + Heap::MemberData *m; + if (totalSize > Chunk::DataSize) { + o = static_cast<Heap::Object *>(allocData(size)); + m = hugeItemAllocator.allocate(memberSize)->as<Heap::MemberData>(); + } else { + HeapItem *mh = reinterpret_cast<HeapItem *>(allocData(totalSize)); + Heap::Base *b = *mh; + o = static_cast<Heap::Object *>(b); + mh += (size >> Chunk::SlotSizeShift); + m = mh->as<Heap::MemberData>(); + Chunk *c = mh->chunk(); + size_t index = mh - c->realBase(); + Chunk::setBit(c->objectBitmap, index); + Chunk::clearBit(c->extendsBitmap, index); + } + o->memberData.set(engine, m); + m->setVtable(MemberData::staticVTable()); + m->values.alloc = static_cast<uint>((memberSize - sizeof(Heap::MemberData) + sizeof(Value))/sizeof(Value)); + m->values.size = o->memberData->values.alloc; + m->init(); // qDebug() << " got" << o->memberData << o->memberData->size; } +// qDebug() << "allocating object with memberData" << o << o->memberData.operator->(); return o; } -static void drainMarkStack(QV4::ExecutionEngine *engine, Value *markBase) +static uint markStackSize = 0; + +MarkStack::MarkStack(ExecutionEngine *engine) + : engine(engine) +{ + base = (Heap::Base **)engine->gcStack->base(); + top = base; + limit = base + ExecutionEngine::GCStackLimit/sizeof(Heap::Base)*3/4; +} + +void MarkStack::drain() { - while (engine->jsStackTop > markBase) { - Heap::Base *h = engine->popForGC(); + while (top > base) { + Heap::Base *h = pop(); + ++markStackSize; Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen. - Q_ASSERT (h->vtable()->markObjects); - h->vtable()->markObjects(h, engine); + h->markChildren(this); } } -void MemoryManager::mark() +void MemoryManager::collectRoots(MarkStack *markStack) { - Value *markBase = engine->jsStackTop; + engine->markObjects(markStack); + +// qDebug() << " mark stack after engine->mark" << (engine->jsStackTop - markBase); - engine->markObjects(); + collectFromJSStack(markStack); - collectFromJSStack(); +// qDebug() << " mark stack after js stack collect" << (engine->jsStackTop - markBase); + m_persistentValues->mark(markStack); - m_persistentValues->mark(engine); +// qDebug() << " mark stack after persistants" << (engine->jsStackTop - markBase); // Preserve QObject ownership rules within JavaScript: A parent with c++ ownership // keeps all of its children alive in JavaScript. @@ -785,13 +967,21 @@ void MemoryManager::mark() } if (keepAlive) - qobjectWrapper->mark(engine); + qobjectWrapper->mark(markStack); - if (engine->jsStackTop >= engine->jsStackLimit) - drainMarkStack(engine, markBase); + if (markStack->top >= markStack->limit) + markStack->drain(); } +} + +void MemoryManager::mark() +{ + markStackSize = 0; - drainMarkStack(engine, markBase); + MarkStack markStack(engine); + collectRoots(&markStack); + + markStack.drain(); } void MemoryManager::sweep(bool lastSweep) @@ -848,31 +1038,30 @@ void MemoryManager::sweep(bool lastSweep) bool MemoryManager::shouldRunGC() const { size_t total = blockAllocator.totalSlots(); - size_t usedSlots = blockAllocator.usedSlotsAfterLastSweep; - if (total > MinSlotsGCLimit && usedSlots * GCOverallocation < total * 100) + if (total > MinSlotsGCLimit && usedSlotsAfterLastFullSweep * GCOverallocation < total * 100) return true; return false; } size_t dumpBins(BlockAllocator *b, bool printOutput = true) { - size_t totalFragmentedSlots = 0; + size_t totalSlotMem = 0; if (printOutput) - qDebug() << "Fragmentation map:"; + qDebug() << "Slot map:"; for (uint i = 0; i < BlockAllocator::NumBins; ++i) { uint nEntries = 0; HeapItem *h = b->freeBins[i]; while (h) { ++nEntries; - totalFragmentedSlots += h->freeData.availableSlots; + totalSlotMem += h->freeData.availableSlots; h = h->freeData.next; } if (printOutput) qDebug() << " number of entries in slot" << i << ":" << nEntries; } if (printOutput) - qDebug() << " total mem in bins" << totalFragmentedSlots*Chunk::SlotSize; - return totalFragmentedSlots*Chunk::SlotSize; + qDebug() << " total mem in bins" << totalSlotMem*Chunk::SlotSize; + return totalSlotMem*Chunk::SlotSize; } void MemoryManager::runGC() @@ -883,6 +1072,7 @@ void MemoryManager::runGC() } QScopedValueRollback<bool> gcBlocker(gcBlocked, true); +// qDebug() << "runGC"; if (!gcStats) { // uint oldUsed = allocator.usedMem(); @@ -897,21 +1087,28 @@ void MemoryManager::runGC() const size_t largeItemsBefore = getLargeItemsMem(); qDebug() << "========== GC =========="; -#ifndef QT_NO_DEBUG +#ifdef MM_STATS qDebug() << " Triggered by alloc request of" << lastAllocRequestedSlots << "slots."; + qDebug() << " Allocations since last GC" << allocationCount; + allocationCount = 0; #endif qDebug() << "Allocated" << totalMem << "bytes in" << blockAllocator.chunks.size() << "chunks"; qDebug() << "Fragmented memory before GC" << (totalMem - usedBefore); dumpBins(&blockAllocator); +#ifdef MM_STATS + nGrayItems = 0; +#endif + QElapsedTimer t; t.start(); mark(); - qint64 markTime = t.restart(); + qint64 markTime = t.nsecsElapsed()/1000; + t.restart(); sweep(); const size_t usedAfter = getUsedMem(); const size_t largeItemsAfter = getLargeItemsMem(); - qint64 sweepTime = t.elapsed(); + qint64 sweepTime = t.nsecsElapsed()/1000; if (triggeredByUnmanagedHeap) { qDebug() << "triggered by unmanaged heap:"; @@ -920,11 +1117,12 @@ void MemoryManager::runGC() qDebug() << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit; } size_t memInBins = dumpBins(&blockAllocator); - qDebug() << "Marked object in" << markTime << "ms."; - qDebug() << "Sweeped object in" << sweepTime << "ms."; + qDebug() << "Marked object in" << markTime << "us."; + qDebug() << " " << markStackSize << "objects marked"; + qDebug() << "Sweeped object in" << sweepTime << "us."; 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); size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter; if (lost) qDebug() << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; @@ -940,6 +1138,12 @@ void MemoryManager::runGC() // ensure we don't 'loose' any memory Q_ASSERT(blockAllocator.allocatedMem() == getUsedMem() + dumpBins(&blockAllocator, false)); } + + usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep; + + // reset all black bits + blockAllocator.resetBlackBits(); + hugeItemAllocator.resetBlackBits(); } size_t MemoryManager::getUsedMem() const @@ -1000,7 +1204,7 @@ void MemoryManager::willAllocate(std::size_t size) #endif // DETAILED_MM_STATS -void MemoryManager::collectFromJSStack() const +void MemoryManager::collectFromJSStack(MarkStack *markStack) const { Value *v = engine->jsStackBase; Value *top = engine->jsStackTop; @@ -1008,7 +1212,7 @@ void MemoryManager::collectFromJSStack() const Managed *m = v->managed(); if (m && m->inUse()) // Skip pointers to already freed objects, they are bogus as well - m->mark(engine); + m->mark(markStack); ++v; } } diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 00daf8a622..76ffec42de 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -80,27 +80,28 @@ struct StackAllocator { StackAllocator(ChunkAllocator *chunkAlloc); T *allocate() { - T *m = nextFree->as<T>(); + HeapItem *m = nextFree; if (Q_UNLIKELY(nextFree == lastInChunk)) { nextChunk(); } else { nextFree += requiredSlots; } -#if MM_DEBUG +#if MM_DEBUG || !defined(QT_NO_DEBUG) Chunk *c = m->chunk(); Chunk::setBit(c->objectBitmap, m - c->realBase()); #endif - return m; + return m->as<T>(); } void free() { -#if MM_DEBUG - Chunk::clearBit(item->chunk()->objectBitmap, item - item->chunk()->realBase()); -#endif if (Q_UNLIKELY(nextFree == firstInChunk)) { prevChunk(); } else { nextFree -= requiredSlots; } +#if MM_DEBUG || !defined(QT_NO_DEBUG) + Chunk *c = nextFree->chunk(); + Chunk::clearBit(c->objectBitmap, nextFree - c->realBase()); +#endif } void nextChunk(); @@ -154,6 +155,8 @@ struct BlockAllocator { void sweep(); void freeAll(); + void resetBlackBits(); + void collectGrayItems(MarkStack *markStack); // bump allocations HeapItem *nextFree = 0; @@ -175,6 +178,8 @@ struct HugeItemAllocator { HeapItem *allocate(size_t size); void sweep(); void freeAll(); + void resetBlackBits(); + void collectGrayItems(MarkStack *markStack); size_t usedMem() const { size_t used = 0; @@ -206,11 +211,11 @@ public: Q_DECL_CONSTEXPR static inline std::size_t align(std::size_t size) { return (size + Chunk::SlotSize - 1) & ~(Chunk::SlotSize - 1); } - QV4::Heap::CallContext *allocSimpleCallContext(QV4::ExecutionEngine *v4) + QV4::Heap::SimpleCallContext *allocSimpleCallContext(QV4::ExecutionEngine *v4) { Heap::CallContext *ctxt = stackAllocator.allocate(); - memset(ctxt, 0, sizeof(Heap::CallContext)); - ctxt->setVtable(QV4::CallContext::staticVTable()); + memset(ctxt, 0, sizeof(Heap::SimpleCallContext)); + ctxt->setVtable(QV4::SimpleCallContext::staticVTable()); ctxt->init(v4); return ctxt; @@ -245,7 +250,7 @@ public: o->setVtable(ObjectType::staticVTable()); Object *prototype = ObjectType::defaultPrototype(engine); o->internalClass = ic; - o->prototype = prototype->d(); + o->prototype.set(engine, prototype->d()); return static_cast<typename ObjectType::Data *>(o); } @@ -272,7 +277,7 @@ public: { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->prototype = prototype->d(); + t->d_unchecked()->prototype.set(engine, prototype->d()); t->d_unchecked()->init(); return t->d(); } @@ -282,7 +287,7 @@ public: { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->prototype = prototype->d(); + t->d_unchecked()->prototype.set(engine, prototype->d()); t->d_unchecked()->init(arg1); return t->d(); } @@ -292,7 +297,7 @@ public: { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->prototype = prototype->d(); + t->d_unchecked()->prototype.set(engine, prototype->d()); t->d_unchecked()->init(arg1, arg2); return t->d(); } @@ -302,7 +307,7 @@ public: { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->prototype = prototype->d(); + t->d_unchecked()->prototype.set(engine, prototype->d()); t->d_unchecked()->init(arg1, arg2, arg3); return t->d(); } @@ -312,7 +317,7 @@ public: { Scope scope(engine); Scoped<ObjectType> t(scope, allocateObject<ObjectType>(ic)); - t->d_unchecked()->prototype = prototype->d(); + t->d_unchecked()->prototype.set(engine, prototype->d()); t->d_unchecked()->init(arg1, arg2, arg3, arg4); return t->d(); } @@ -428,7 +433,6 @@ public: // called when a JS object grows itself. Specifically: Heap::String::append void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; } - protected: /// expects size to be aligned Heap::Base *allocString(std::size_t unmanagedSize); @@ -440,10 +444,11 @@ protected: #endif // DETAILED_MM_STATS private: - void collectFromJSStack() const; + void collectFromJSStack(MarkStack *markStack) const; void mark(); void sweep(bool lastSweep = false); bool shouldRunGC() const; + void collectRoots(MarkStack *markStack); public: QV4::ExecutionEngine *engine; @@ -457,6 +462,7 @@ public: std::size_t unmanagedHeapSize = 0; // the amount of bytes of heap that is not managed by the memory manager, but which is held onto by managed items. std::size_t unmanagedHeapSizeGCLimit; + std::size_t usedSlotsAfterLastFullSweep = 0; bool gcBlocked = false; bool aggressiveGC = false; diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h index 588ae21ee0..9512722782 100644 --- a/src/qml/memory/qv4mmdefs_p.h +++ b/src/qml/memory/qv4mmdefs_p.h @@ -51,6 +51,7 @@ // #include <private/qv4global_p.h> +#include <private/qv4runtimeapi_p.h> #include <QtCore/qalgorithms.h> #include <qdebug.h> @@ -58,6 +59,8 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct MarkStack; + /* * Chunks are the basic structure containing GC managed objects. * @@ -111,22 +114,29 @@ struct Chunk { HeapItem *realBase(); HeapItem *first(); + static Q_ALWAYS_INLINE size_t bitmapIndex(size_t index) { + return index >> BitShift; + } + static Q_ALWAYS_INLINE quintptr bitForIndex(size_t index) { + return static_cast<quintptr>(1) << (index & (Bits - 1)); + } + static void setBit(quintptr *bitmap, size_t index) { // Q_ASSERT(index >= HeaderSize/SlotSize && index < ChunkSize/SlotSize); - bitmap += index >> BitShift; - quintptr bit = static_cast<quintptr>(1) << (index & (Bits - 1)); + bitmap += bitmapIndex(index); + quintptr bit = bitForIndex(index); *bitmap |= bit; } static void clearBit(quintptr *bitmap, size_t index) { // Q_ASSERT(index >= HeaderSize/SlotSize && index < ChunkSize/SlotSize); - bitmap += index >> BitShift; - quintptr bit = static_cast<quintptr>(1) << (index & (Bits - 1)); + bitmap += bitmapIndex(index); + quintptr bit = bitForIndex(index); *bitmap &= ~bit; } static bool testBit(quintptr *bitmap, size_t index) { // Q_ASSERT(index >= HeaderSize/SlotSize && index < ChunkSize/SlotSize); - bitmap += index >> BitShift; - quintptr bit = static_cast<quintptr>(1) << (index & (Bits - 1)); + bitmap += bitmapIndex(index); + quintptr bit = bitForIndex(index); return (*bitmap & bit); } static void setBits(quintptr *bitmap, size_t index, size_t nBits) { @@ -176,6 +186,8 @@ struct Chunk { void sweep(); void freeAll(); + void resetBlackBits(); + void collectGrayItems(QV4::MarkStack *markStack); void sortIntoBins(HeapItem **bins, uint nBins); }; @@ -255,6 +267,120 @@ 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 {\ + MarkStack(ExecutionEngine *engine); + Heap::Base **top = 0; + Heap::Base **base = 0; + Heap::Base **limit = 0; + ExecutionEngine *engine; + void push(Heap::Base *m) { + *top = m; + ++top; + } + Heap::Base *pop() { + --top; + return *top; + } + void drain(); + +}; + +// 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 + +enum MarkFlags { + Mark_NoMark = 0, + Mark_Value = 1, + Mark_Pointer = 2, + Mark_ValueArray = 3 +}; + +template <typename T> +struct MarkFlagEvaluator { + static Q_CONSTEXPR quint64 value = 0; +}; +template <typename T, size_t o> +struct MarkFlagEvaluator<Heap::Pointer<T, o>> { + static Q_CONSTEXPR quint64 value = static_cast<quint64>(Mark_Pointer) << (2*o / sizeof(quintptr)); +}; +template <size_t o> +struct MarkFlagEvaluator<ValueArray<o>> { + static Q_CONSTEXPR quint64 value = static_cast<quint64>(Mark_ValueArray) << (2*o / sizeof(quintptr)); +}; +template <size_t o> +struct MarkFlagEvaluator<HeapValue<o>> { + static Q_CONSTEXPR quint64 value = static_cast<quint64>(Mark_Value) << (2 *o / sizeof(quintptr)); +}; + +#define HEAP_OBJECT_OFFSET_MEMBER_EXPANSION(c, gcType, type, name) \ + HEAP_OBJECT_OFFSET_MEMBER_EXPANSION_##gcType(c, type, name) + +#define HEAP_OBJECT_OFFSET_MEMBER_EXPANSION_Pointer(c, type, name) Pointer<type, 0> name; +#define HEAP_OBJECT_OFFSET_MEMBER_EXPANSION_NoMark(c, type, name) type name; +#define HEAP_OBJECT_OFFSET_MEMBER_EXPANSION_HeapValue(c, type, name) HeapValue<0> name; +#define HEAP_OBJECT_OFFSET_MEMBER_EXPANSION_ValueArray(c, type, name) type<0> name; + +#define HEAP_OBJECT_MEMBER_EXPANSION(c, gcType, type, name) \ + HEAP_OBJECT_MEMBER_EXPANSION_##gcType(c, type, name) + +#define HEAP_OBJECT_MEMBER_EXPANSION_Pointer(c, type, name) \ + Pointer<type, offsetof(c##OffsetStruct, name) + baseOffset> name; +#define HEAP_OBJECT_MEMBER_EXPANSION_NoMark(c, type, name) \ + type name; +#define HEAP_OBJECT_MEMBER_EXPANSION_HeapValue(c, type, name) \ + HeapValue<offsetof(c##OffsetStruct, name) + baseOffset> name; +#define HEAP_OBJECT_MEMBER_EXPANSION_ValueArray(c, type, name) \ + type<offsetof(c##OffsetStruct, name) + baseOffset> name; + +#define HEAP_OBJECT_MARK_EXPANSION(class, gcType, type, name) \ + MarkFlagEvaluator<decltype(class::name)>::value | + +#define DECLARE_HEAP_OBJECT(name, base) \ +struct name##OffsetStruct { \ + name##Members(name, HEAP_OBJECT_OFFSET_MEMBER_EXPANSION) \ +}; \ +struct name##SizeStruct : base, name##OffsetStruct {}; \ +struct name##Data { \ + static Q_CONSTEXPR size_t baseOffset = sizeof(name##SizeStruct) - sizeof(name##OffsetStruct); \ + name##Members(name, HEAP_OBJECT_MEMBER_EXPANSION) \ +}; \ +Q_STATIC_ASSERT(sizeof(name##SizeStruct) == sizeof(name##Data) + name##Data::baseOffset); \ +static Q_CONSTEXPR quint64 name##_markTable = \ + (name##Members(name##Data, HEAP_OBJECT_MARK_EXPANSION) 0) | QV4::Heap::base::markTable; \ + \ +struct name : base, name##Data + +#define DECLARE_MARK_TABLE(class) static Q_CONSTEXPR quint64 markTable = class##_markTable + } QT_END_NAMESPACE diff --git a/src/qml/memory/qv4writebarrier_p.h b/src/qml/memory/qv4writebarrier_p.h new file mode 100644 index 0000000000..e36ea0749a --- /dev/null +++ b/src/qml/memory/qv4writebarrier_p.h @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef QV4WRITEBARRIER_P_H +#define QV4WRITEBARRIER_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/qv4value_p.h> + +QT_BEGIN_NAMESPACE + +#define WRITEBARRIER_none 1 + +#define WRITEBARRIER(x) (1/WRITEBARRIER_##x == 1) + +namespace QV4 { + +namespace WriteBarrier { + +enum Type { + NoBarrier, + Barrier +}; + +enum NewValueType { + Primitive, + Object, + Unknown +}; + +// ### this needs to be filled with a real memory fence once marking is concurrent +Q_ALWAYS_INLINE void fence() {} + +#if WRITEBARRIER(none) + +template <NewValueType type> +static Q_CONSTEXPR inline bool isRequired() { + return false; +} + +inline void write(EngineBase *engine, Heap::Base *base, Value *slot, Value value) +{ + Q_UNUSED(engine); + Q_UNUSED(base); + *slot = value; +} + +inline void write(EngineBase *engine, Heap::Base *base, Value *slot, Heap::Base *value) +{ + Q_UNUSED(engine); + Q_UNUSED(base); + *slot = value; +} + +inline void write(EngineBase *engine, Heap::Base *base, Heap::Base **slot, Heap::Base *value) +{ + Q_UNUSED(engine); + Q_UNUSED(base); + *slot = value; +} + +#endif + +} + +namespace Heap { + +template <typename T, size_t o> +struct Pointer { + static Q_CONSTEXPR size_t offset = o; + T operator->() const { return ptr; } + operator T () const { return ptr; } + + Heap::Base *base() { + Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); + Q_ASSERT(base->inUse()); + return base; + } + + void set(ExecutionEngine *e, T newVal) { + WriteBarrier::write(e, base(), reinterpret_cast<Heap::Base **>(&ptr), reinterpret_cast<Heap::Base *>(newVal)); + } + + template <typename Type> + Type *cast() { return static_cast<Type *>(ptr); } + +private: + T ptr; +}; +typedef Pointer<char *, 0> V4PointerCheck; +V4_ASSERT_IS_TRIVIAL(V4PointerCheck) + +} + +template <size_t offset> +struct HeapValue : Value { + Heap::Base *base() { + Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); + Q_ASSERT(base->inUse()); + return base; + } + + void set(ExecutionEngine *e, const Value &newVal) { + WriteBarrier::write(e, base(), this, newVal); + } +}; + +template <size_t offset> +struct ValueArray { + uint size; + uint alloc; + Value values[1]; + + Heap::Base *base() { + Heap::Base *base = reinterpret_cast<Heap::Base *>(this) - (offset/sizeof(Heap::Base)); + Q_ASSERT(base->inUse()); + return base; + } + + void set(ExecutionEngine *e, uint index, Value v) { + WriteBarrier::write(e, base(), values + index, v); + } + void set(ExecutionEngine *e, uint index, Heap::Base *b) { + WriteBarrier::write(e, base(), values + index, b); + } + inline const Value &operator[] (uint index) const { + Q_ASSERT(index < alloc); + return values[index]; + } + inline const Value *data() const { + return values; + } + + void insertData(ExecutionEngine *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) { + Q_UNUSED(e); + for (uint i = index; i < size - n; ++i) { + values[i] = values[i + n]; + } + } +}; + +// It's really important that the offset of values in this structure is +// constant across all architecture, otherwise JIT cross-compiled code will +// have wrong offsets between host and target. +Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8); + +} + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/parser/qqmljsglobal_p.h b/src/qml/parser/qqmljsglobal_p.h index 933c8f5202..0e195994b4 100644 --- a/src/qml/parser/qqmljsglobal_p.h +++ b/src/qml/parser/qqmljsglobal_p.h @@ -67,13 +67,17 @@ #else // !QT_CREATOR # define QT_QML_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE # define QT_QML_END_NAMESPACE QT_END_NAMESPACE -# if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) - // QmlDevTools is a static library -# define QML_PARSER_EXPORT -# elif defined(QT_BUILD_QML_LIB) -# define QML_PARSER_EXPORT Q_DECL_EXPORT +# ifndef QT_STATIC +# if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) + // QmlDevTools is a static library +# define QML_PARSER_EXPORT +# elif defined(QT_BUILD_QML_LIB) +# define QML_PARSER_EXPORT Q_DECL_EXPORT +# else +# define QML_PARSER_EXPORT Q_DECL_IMPORT +# endif # else -# define QML_PARSER_EXPORT Q_DECL_IMPORT +# define QML_PARSER_EXPORT # endif #endif // QT_CREATOR diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 8f9e4b7f83..be3956bb61 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -48,7 +48,9 @@ include(jit/jit.pri) include(jsruntime/jsruntime.pri) include(qml/qml.pri) include(debugger/debugger.pri) -include(animations/animations.pri) +qtConfig(animation) { + include(animations/animations.pri) +} include(types/types.pri) MODULE_PLUGIN_TYPES = \ diff --git a/src/qml/qml/qqmlcomponent.cpp b/src/qml/qml/qqmlcomponent.cpp index a04f47e6a4..75968ffc43 100644 --- a/src/qml/qml/qqmlcomponent.cpp +++ b/src/qml/qml/qqmlcomponent.cpp @@ -1046,14 +1046,18 @@ namespace QV4 { namespace Heap { -struct QmlIncubatorObject : Object { +#define QmlIncubatorObjectMembers(class, Member) \ + Member(class, HeapValue, HeapValue, valuemap) \ + Member(class, HeapValue, HeapValue, statusChanged) \ + Member(class, Pointer, QmlContext *, qmlContext) \ + Member(class, NoMark, QQmlComponentIncubator *, incubator) \ + Member(class, NoMark, QQmlQPointer<QObject>, parent) + +DECLARE_HEAP_OBJECT(QmlIncubatorObject, Object) { + DECLARE_MARK_TABLE(QmlIncubatorObject); + void init(QQmlIncubator::IncubationMode = QQmlIncubator::Asynchronous); inline void destroy(); - QQmlComponentIncubator *incubator; - QQmlQPointer<QObject> parent; - QV4::Value valuemap; - QV4::Value statusChanged; - Pointer<Heap::QmlContext> qmlContext; }; } @@ -1069,8 +1073,6 @@ struct QmlIncubatorObject : public QV4::Object static void method_get_object(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_forceCompletion(const BuiltinFunction *, Scope &scope, CallData *callData); - static void markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *e); - void statusChanged(QQmlIncubator::Status); void setInitialState(QObject *); }; @@ -1374,8 +1376,8 @@ void QQmlComponent::incubateObject(QQmlV4Function *args) r->setPrototype(p); if (!valuemap->isUndefined()) - r->d()->valuemap = valuemap; - r->d()->qmlContext = v4->qmlContext(); + r->d()->valuemap.set(scope.engine, valuemap); + r->d()->qmlContext.set(scope.engine, v4->qmlContext()); r->d()->parent = parent; QQmlIncubator *incubator = r->d()->incubator; @@ -1459,7 +1461,7 @@ void QV4::QmlIncubatorObject::method_set_statusChanged(const BuiltinFunction *, if (!o || callData->argc < 1) THROW_TYPE_ERROR(); - o->d()->statusChanged = callData->args[0]; + o->d()->statusChanged.set(scope.engine, callData->args[0]); RETURN_UNDEFINED(); } @@ -1471,10 +1473,10 @@ QQmlComponentExtension::~QQmlComponentExtension() void QV4::Heap::QmlIncubatorObject::init(QQmlIncubator::IncubationMode m) { Object::init(); - valuemap = QV4::Primitive::undefinedValue(); - statusChanged = QV4::Primitive::undefinedValue(); + valuemap.set(internalClass->engine, QV4::Primitive::undefinedValue()); + statusChanged.set(internalClass->engine, QV4::Primitive::undefinedValue()); parent.init(); - qmlContext = nullptr; + qmlContext.set(internalClass->engine, nullptr); incubator = new QQmlComponentIncubator(this, m); } @@ -1497,16 +1499,6 @@ void QV4::QmlIncubatorObject::setInitialState(QObject *o) } } -void QV4::QmlIncubatorObject::markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *e) -{ - QmlIncubatorObject::Data *o = static_cast<QmlIncubatorObject::Data *>(that); - o->valuemap.mark(e); - o->statusChanged.mark(e); - if (o->qmlContext) - o->qmlContext->mark(e); - Object::markObjects(that, e); -} - void QV4::QmlIncubatorObject::statusChanged(QQmlIncubator::Status s) { QV4::Scope scope(engine()); diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index e271598c2d..2083326cd5 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -201,7 +201,9 @@ public: static QQmlData *get(const QObject *object, bool create = false) { QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); - if (priv->wasDeleted) { + // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has + // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use. + if (priv->isDeletingChildren || priv->wasDeleted) { Q_ASSERT(!create); return 0; } else if (priv->declarativeData) { @@ -269,8 +271,8 @@ bool QQmlData::wasDeleted(QObject *object) if (!priv || priv->wasDeleted) return true; - return priv->declarativeData && - static_cast<QQmlData *>(priv->declarativeData)->isQueuedForDeletion; + QQmlData *ddata = QQmlData::get(object); + return ddata && ddata->isQueuedForDeletion; } QQmlNotifierEndpoint *QQmlData::notify(int index) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index af94ece496..9ff76d7b24 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -82,7 +82,9 @@ #include <private/qqmllocale_p.h> #include <private/qqmlbind_p.h> #include <private/qqmlconnections_p.h> +#if QT_CONFIG(animation) #include <private/qqmltimer_p.h> +#endif #include <private/qqmllistmodel_p.h> #include <private/qqmlplatform_p.h> #include <private/qquickpackage_p.h> @@ -218,7 +220,9 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8 qmlRegisterType<QQmlConnections,1>(uri, versionMajor, (versionMinor < 3 ? 3 : versionMinor), "Connections"); //Only available in >=2.3 qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections"); +#if QT_CONFIG(animation) qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer"); +#endif qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1 qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser); qmlRegisterType<QQmlInstanceModel>(); @@ -709,9 +713,7 @@ QQmlEnginePrivate::~QQmlEnginePrivate() void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) { - QObjectPrivate *p = QObjectPrivate::get(o); - if (p->declarativeData) { - QQmlData *d = static_cast<QQmlData*>(p->declarativeData); + if (QQmlData *d = QQmlData::get(o)) { if (d->ownContext && d->context) { d->context->destroy(); d->context = 0; @@ -791,7 +793,7 @@ void QQmlData::signalEmitted(QAbstractDeclarativeData *, QObject *object, int in // marshalled back onto the QObject's thread and handled by QML from there. This is tested // by the qqmlecmascript::threadSignal() autotest. if (ddata->notifyList && - QThread::currentThreadId() != QObjectPrivate::get(object)->threadData->threadId) { + QThread::currentThreadId() != QObjectPrivate::get(object)->threadData->threadId.load()) { if (!QObjectPrivate::get(object)->threadData->thread) return; @@ -881,13 +883,10 @@ void QQmlData::markAsDeleted(QObject *o) void QQmlData::setQueuedForDeletion(QObject *object) { if (object) { - if (QObjectPrivate *priv = QObjectPrivate::get(object)) { - if (!priv->wasDeleted && priv->declarativeData) { - QQmlData *ddata = QQmlData::get(object, false); - if (ddata->ownContext && ddata->context) - ddata->context->emitDestruction(); - ddata->isQueuedForDeletion = true; - } + if (QQmlData *ddata = QQmlData::get(object)) { + if (ddata->ownContext && ddata->context) + ddata->context->emitDestruction(); + ddata->isQueuedForDeletion = true; } } } @@ -1336,17 +1335,11 @@ QQmlContext *QQmlEngine::contextForObject(const QObject *object) if(!object) return 0; - QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); - - QQmlData *data = - static_cast<QQmlData *>(priv->declarativeData); - - if (!data) - return 0; - else if (data->outerContext) + QQmlData *data = QQmlData::get(object); + if (data && data->outerContext) return data->outerContext->asQQmlContext(); - else - return 0; + + return 0; } /*! @@ -1881,6 +1874,7 @@ void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) QQmlData *QQmlData::createQQmlData(QObjectPrivate *priv) { Q_ASSERT(priv); + Q_ASSERT(!priv->isDeletingChildren); priv->declarativeData = new QQmlData; return static_cast<QQmlData *>(priv->declarativeData); } diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index c07d5c740a..ee5b38717b 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -210,7 +210,6 @@ QQmlType *fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringR } // namespace -#if QT_CONFIG(library) struct RegisteredPlugin { QString uri; QPluginLoader* loader; @@ -221,21 +220,23 @@ struct StringRegisteredPluginMap : public QMap<QString, RegisteredPlugin> { }; Q_GLOBAL_STATIC(StringRegisteredPluginMap, qmlEnginePluginsWithRegisteredTypes); // stores the uri and the PluginLoaders + void qmlClearEnginePlugins() { StringRegisteredPluginMap *plugins = qmlEnginePluginsWithRegisteredTypes(); QMutexLocker lock(&plugins->mutex); +#if QT_CONFIG(library) for (auto &plugin : qAsConst(*plugins)) { QPluginLoader* loader = plugin.loader; if (loader && !loader->unload()) qWarning("Unloading %s failed: %s", qPrintable(plugin.uri), qPrintable(loader->errorString())); delete loader; } +#endif plugins->clear(); } typedef QPair<QStaticPlugin, QJsonArray> StaticPluginPair; -#endif /*! \internal @@ -332,10 +333,9 @@ public: const QString &uri, const QString &url, int vmaj, int vmin, QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, bool lowPrecedence = false); -#if QT_CONFIG(library) - bool populatePluginPairVector(QVector<StaticPluginPair> &result, const QString &uri, const QStringList &versionUris, + + bool populatePluginPairVector(QVector<StaticPluginPair> &result, const QString &uri, const QStringList &versionUris, const QString &qmldirPath, QList<QQmlError> *errors); -#endif }; /*! @@ -959,7 +959,6 @@ static QStringList versionUriList(const QString &uri, int vmaj, int vmin) return result; } -#if QT_CONFIG(library) static QVector<QStaticPlugin> makePlugins() { QVector<QStaticPlugin> plugins; @@ -1009,7 +1008,6 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res } return true; } -#endif #if defined(QT_SHARED) || !QT_CONFIG(library) static inline QString msgCannotLoadPlugin(const QString &uri, const QString &why) @@ -1030,7 +1028,6 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors) { -#if QT_CONFIG(library) Q_ASSERT(qmldir); if (qmlImportTrace()) @@ -1143,22 +1140,6 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, database->qmlDirFilesForWhichPluginsHaveBeenLoaded.insert(qmldirFilePath); } - -#else - Q_UNUSED(vmaj); - Q_UNUSED(vmin); - Q_UNUSED(database); - Q_UNUSED(qmldir); - - if (errors) { - QQmlError error; - error.setDescription(msgCannotLoadPlugin(uri, QQmlImportDatabase::tr("library loading is disabled"))); - error.setUrl(QUrl::fromLocalFile(qmldirFilePath)); - errors->prepend(error); - } - - return false; -#endif // library return true; } @@ -2014,7 +1995,6 @@ bool QQmlImportDatabase::registerPluginTypes(QObject *instance, const QString &b bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &basePath, const QString &uri, const QString &typeNamespace, int vmaj, QList<QQmlError> *errors) { -#if QT_CONFIG(library) // Dynamic plugins are differentiated by their filepath. For static plugins we // don't have that information so we use their address as key instead. const QString uniquePluginID = QString::asprintf("%p", instance); @@ -2050,15 +2030,6 @@ bool QQmlImportDatabase::importStaticPlugin(QObject *instance, const QString &ba } return true; -#else - Q_UNUSED(instance); - Q_UNUSED(basePath); - Q_UNUSED(uri); - Q_UNUSED(typeNamespace); - Q_UNUSED(vmaj); - Q_UNUSED(errors); - return false; -#endif } /*! diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index bd6b9a1599..bb9b69c479 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1971,7 +1971,9 @@ QString QQmlMetaType::prettyTypeName(const QObject *object) const int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); if (lastSlash != -1) typeName = typeName.mid(lastSlash + 1); - } else { + } + + if (typeName.isEmpty()) { typeName = QString::fromUtf8(object->metaObject()->className()); int marker = typeName.indexOf(QLatin1String("_QMLTYPE_")); if (marker != -1) @@ -1982,10 +1984,12 @@ QString QQmlMetaType::prettyTypeName(const QObject *object) typeName = typeName.leftRef(marker) + QLatin1Char('*'); type = QQmlMetaType::qmlType(QMetaType::type(typeName.toLatin1())); if (type) { - typeName = type->qmlTypeName(); - const int lastSlash = typeName.lastIndexOf(QLatin1Char('/')); + QString qmlTypeName = type->qmlTypeName(); + const int lastSlash = qmlTypeName.lastIndexOf(QLatin1Char('/')); if (lastSlash != -1) - typeName = typeName.mid(lastSlash + 1); + qmlTypeName = qmlTypeName.mid(lastSlash + 1); + if (!qmlTypeName.isEmpty()) + typeName = qmlTypeName; } } } diff --git a/src/qml/qml/qqmlnotifier.cpp b/src/qml/qml/qqmlnotifier.cpp index 185f9687fb..538ca822ee 100644 --- a/src/qml/qml/qqmlnotifier.cpp +++ b/src/qml/qml/qqmlnotifier.cpp @@ -122,8 +122,8 @@ void QQmlNotifierEndpoint::connect(QObject *source, int sourceSignal, QQmlEngine disconnect(); Q_ASSERT(engine); - if (QObjectPrivate::get(source)->threadData->threadId != - QObjectPrivate::get(engine)->threadData->threadId) { + if (QObjectPrivate::get(source)->threadData->threadId.load() != + QObjectPrivate::get(engine)->threadData->threadId.load()) { QString sourceName; QDebug(&sourceName) << source; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 85fbd86dc4..2cbcfbbfb6 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1075,7 +1075,9 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo { QQmlData *ddata = new (ddataMemory) QQmlData; ddata->ownMemory = false; - QObjectPrivate::get(instance)->declarativeData = ddata; + QObjectPrivate* p = QObjectPrivate::get(instance); + Q_ASSERT(!p->isDeletingChildren); + p->declarativeData = ddata; } const int parserStatusCast = type->parserStatusCast(); diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index ca522c29af..9b5f7b0a06 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1627,7 +1627,7 @@ QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const Q */ static inline void flush_vme_signal(const QObject *object, int index, bool indexInSignalRange) { - QQmlData *data = static_cast<QQmlData *>(QObjectPrivate::get(const_cast<QObject *>(object))->declarativeData); + QQmlData *data = QQmlData::get(object); if (data && data->propertyCache) { QQmlPropertyData *property = indexInSignalRange ? data->propertyCache->signal(index) : data->propertyCache->method(index); diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index f4f04e12c0..40bd2e5020 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -64,6 +64,7 @@ #include <QtCore/qwaitcondition.h> #include <QtCore/qloggingcategory.h> #include <QtQml/qqmlextensioninterface.h> +#include <QtCore/qcryptographichash.h> #include <functional> @@ -80,10 +81,6 @@ # define NAME_MAX _POSIX_SYMLINK_MAX #endif -// LSB has a broken version of qOffsetOf that can't be used at compile time -// https://lsbbugs.linuxfoundation.org/show_bug.cgi?id=3462 -#undef qOffsetOf -#define qOffsetOf(TYPE, MEMBER) __builtin_qOffsetOf (TYPE, MEMBER) #endif // #define DATABLOB_DEBUG @@ -1249,20 +1246,20 @@ void QQmlTypeLoader::initializeEngine(QQmlExtensionInterface *iface, void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QByteArray &data) { QML_MEMORY_SCOPE_URL(blob->url()); - QQmlDataBlob::Data d; - d.d = &data; + QQmlDataBlob::SourceCodeData d; + d.inlineSourceCode = QString::fromUtf8(data); setData(blob, d); } void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QString &fileName) { QML_MEMORY_SCOPE_URL(blob->url()); - QQmlDataBlob::Data d; - d.d = &fileName; + QQmlDataBlob::SourceCodeData d; + d.fileInfo = QFileInfo(fileName); setData(blob, d); } -void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::Data &d) +void QQmlTypeLoader::setData(QQmlDataBlob *blob, const QQmlDataBlob::SourceCodeData &d) { QML_MEMORY_SCOPE_URL(blob->url()); QQmlCompilingProfiler prof(QQmlEnginePrivate::get(engine())->profiler, blob); @@ -1529,7 +1526,8 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE return false; } - *it = priority; + if (it != m_unresolvedImports.end()) + *it = priority; return true; } } @@ -2009,6 +2007,16 @@ QQmlTypeData::TypeDataCallback::~TypeDataCallback() { } +QString QQmlTypeData::TypeReference::qualifiedName() const +{ + QString result; + if (!prefix.isEmpty()) { + result = prefix + QLatin1Char('.'); + } + result.append(type->qmlTypeName()); + return result; +} + QQmlTypeData::QQmlTypeData(const QUrl &url, QQmlTypeLoader *manager) : QQmlTypeLoader::Blob(url, QmlFile, manager), m_typesResolved(false), m_implicitImportLoaded(false) @@ -2069,7 +2077,7 @@ bool QQmlTypeData::tryLoadFromDiskCache() QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); { QString error; - if (!unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { + if (!unit->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), v4->iselFactory.data(), &error)) { qCDebug(DBG_DISK_CACHE) << "Error loading" << url().toString() << "from disk cache:" << error; return false; } @@ -2154,6 +2162,23 @@ void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeName aliasCreator.appendAliasPropertiesToMetaObjects(); } +static bool addTypeReferenceChecksumsToHash(const QList<QQmlTypeData::TypeReference> &typeRefs, QCryptographicHash *hash, QQmlEngine *engine) +{ + for (const auto &typeRef: typeRefs) { + if (typeRef.typeData) { + const auto unit = typeRef.typeData->compilationUnit(); + hash->addData(unit->data->md5Checksum, sizeof(unit->data->md5Checksum)); + } else if (typeRef.type) { + const auto propertyCache = QQmlEnginePrivate::get(engine)->cache(typeRef.type->metaObject()); + bool ok = false; + hash->addData(propertyCache->checksum(&ok)); + if (!ok) + return false; + } + } + return true; +} + void QQmlTypeData::done() { QDeferredCleanup cleanup([this]{ @@ -2234,18 +2259,24 @@ void QQmlTypeData::done() QQmlEngine *const engine = typeLoader()->engine(); + const auto dependencyHasher = [engine, resolvedTypeCache, this](QCryptographicHash *hash) { + if (!resolvedTypeCache.addToHash(hash, engine)) + return false; + return ::addTypeReferenceChecksumsToHash(m_compositeSingletons, hash, engine); + }; + // verify if any dependencies changed if we're using a cache - if (m_document.isNull() && !m_compiledData->verifyChecksum(engine, resolvedTypeCache)) { + if (m_document.isNull() && !m_compiledData->verifyChecksum(dependencyHasher)) { qCDebug(DBG_DISK_CACHE) << "Checksum mismatch for cached version of" << m_compiledData->url().toString(); if (!loadFromSource()) return; - m_backupSourceCode.clear(); + m_backupSourceCode = SourceCodeData(); m_compiledData = nullptr; } if (!m_document.isNull()) { // Compile component - compile(typeNameCache, resolvedTypeCache); + compile(typeNameCache, resolvedTypeCache, dependencyHasher); } else { createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); } @@ -2346,13 +2377,9 @@ bool QQmlTypeData::loadImplicitImport() return true; } -void QQmlTypeData::dataReceived(const Data &data) +void QQmlTypeData::dataReceived(const SourceCodeData &data) { - QString error; - m_backupSourceCode = data.readAll(&error, &m_sourceTimeStamp); - // if we failed to read the source code, process it _after_ we've tried - // to use the disk cache, in order to support scenarios where the source - // was removed deliberately. + m_backupSourceCode = data; if (tryLoadFromDiskCache()) return; @@ -2360,8 +2387,8 @@ void QQmlTypeData::dataReceived(const Data &data) if (isError()) return; - if (!error.isEmpty()) { - setError(error); + if (!m_backupSourceCode.exists()) { + setError(QQmlTypeLoader::tr("No such file or directory")); return; } @@ -2380,12 +2407,19 @@ void QQmlTypeData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *un bool QQmlTypeData::loadFromSource() { - QString code = QString::fromUtf8(m_backupSourceCode); m_document.reset(new QmlIR::Document(isDebugging())); - m_document->jsModule.sourceTimeStamp = m_sourceTimeStamp; + m_document->jsModule.sourceTimeStamp = m_backupSourceCode.sourceTimeStamp(); QQmlEngine *qmlEngine = typeLoader()->engine(); QmlIR::IRBuilder compiler(QV8Engine::get(qmlEngine)->illegalNames()); - if (!compiler.generateFromQml(code, finalUrlString(), m_document.data())) { + + QString sourceError; + const QString source = m_backupSourceCode.readAll(&sourceError); + if (!sourceError.isEmpty()) { + setError(sourceError); + return false; + } + + if (!compiler.generateFromQml(source, finalUrlString(), m_document.data())) { QList<QQmlError> errors; errors.reserve(compiler.errors.count()); for (const QQmlJS::DiagnosticMessage &msg : qAsConst(compiler.errors)) { @@ -2407,6 +2441,7 @@ void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> m_document.reset(new QmlIR::Document(isDebugging())); QmlIR::IRLoader loader(unit->data, m_document.data()); loader.load(); + m_document->jsModule.setFileName(finalUrlString()); m_document->javaScriptCompilationUnit = unit; continueLoadFromIR(); } @@ -2503,24 +2538,27 @@ QString QQmlTypeData::stringAt(int index) const return m_document->jsGenerator.stringTable.stringForIndex(index); } -void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) +void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache, + const QV4::CompiledData::DependentTypesHasher &dependencyHasher) { Q_ASSERT(m_compiledData.isNull()); + const bool typeRecompilation = m_document && m_document->javaScriptCompilationUnit && m_document->javaScriptCompilationUnit->data->flags & QV4::CompiledData::Unit::PendingTypeCompilation; + QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine()); - QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache); + QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache, dependencyHasher); m_compiledData = compiler.compile(); if (!m_compiledData) { setError(compiler.compilationErrors()); return; } - const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode; + const bool trySaveToDisk = (!disableDiskCache() || forceDiskCache()) && !m_document->jsModule.debugMode && !typeRecompilation; if (trySaveToDisk) { QString errorString; if (m_compiledData->saveToDisk(url(), &errorString)) { QString error; - if (!m_compiledData->loadFromDisk(url(), enginePrivate->v4engine()->iselFactory.data(), &error)) { + if (!m_compiledData->loadFromDisk(url(), m_backupSourceCode.sourceTimeStamp(), enginePrivate->v4engine()->iselFactory.data(), &error)) { // ignore error, keep using the in-memory compilation unit. } } else { @@ -2580,6 +2618,10 @@ void QQmlTypeData::resolveTypes() } } + std::stable_sort(m_compositeSingletons.begin(), m_compositeSingletons.end(), [](const TypeReference &lhs, const TypeReference &rhs){ + return lhs.qualifiedName() < rhs.qualifiedName(); + }); + for (QV4::CompiledData::TypeReferenceMap::ConstIterator unresolvedRef = m_typeReferences.constBegin(), end = m_typeReferences.constEnd(); unresolvedRef != end; ++unresolvedRef) { @@ -2873,14 +2915,14 @@ struct EmptyCompilationUnit : public QV4::CompiledData::CompilationUnit void linkBackendToEngine(QV4::ExecutionEngine *) override {} }; -void QQmlScriptBlob::dataReceived(const Data &data) +void QQmlScriptBlob::dataReceived(const SourceCodeData &data) { QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); if (!disableDiskCache() || forceDiskCache()) { QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); QString error; - if (unit->loadFromDisk(url(), v4->iselFactory.data(), &error)) { + if (unit->loadFromDisk(url(), data.sourceTimeStamp(), v4->iselFactory.data(), &error)) { initializeFromCompilationUnit(unit); return; } else { @@ -2891,8 +2933,9 @@ void QQmlScriptBlob::dataReceived(const Data &data) QmlIR::Document irUnit(isDebugging()); + irUnit.jsModule.sourceTimeStamp = data.sourceTimeStamp(); QString error; - QString source = QString::fromUtf8(data.readAll(&error, &irUnit.jsModule.sourceTimeStamp)); + QString source = data.readAll(&error); if (!error.isEmpty()) { setError(error); return; @@ -2917,8 +2960,7 @@ void QQmlScriptBlob::dataReceived(const Data &data) irUnit.jsModule.unitFlags |= QV4::CompiledData::Unit::IsSharedLibrary; QmlIR::QmlUnitGenerator qmlGenerator; - QV4::CompiledData::ResolvedTypeReferenceMap emptyDependencies; - QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit, m_typeLoader->engine(), emptyDependencies); + QV4::CompiledData::Unit *unitData = qmlGenerator.generate(irUnit); Q_ASSERT(!unit->data); // The js unit owns the data and will free the qml unit. unit->data = unitData; @@ -3056,10 +3098,10 @@ void QQmlQmldirData::setPriority(int priority) m_priority = priority; } -void QQmlQmldirData::dataReceived(const Data &data) +void QQmlQmldirData::dataReceived(const SourceCodeData &data) { QString error; - m_content = QString::fromUtf8(data.readAll(&error)); + m_content = data.readAll(&error); if (!error.isEmpty()) { setError(error); return; @@ -3071,34 +3113,54 @@ void QQmlQmldirData::initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit * Q_UNIMPLEMENTED(); } -QByteArray QQmlDataBlob::Data::readAll(QString *error, qint64 *sourceTimeStamp) const +QString QQmlDataBlob::SourceCodeData::readAll(QString *error) const { - Q_ASSERT(!d.isNull()); error->clear(); - if (d.isT1()) { - if (sourceTimeStamp) - *sourceTimeStamp = 0; - return *d.asT1(); - } - QFile f(*d.asT2()); + if (!inlineSourceCode.isEmpty()) + return inlineSourceCode; + + QFile f(fileInfo.absoluteFilePath()); if (!f.open(QIODevice::ReadOnly)) { *error = f.errorString(); - return QByteArray(); + return QString(); } - if (sourceTimeStamp) { - QDateTime timeStamp = QFileInfo(f).lastModified(); - // Files from the resource system do not have any time stamps, so fall back to the application - // executable. - if (!timeStamp.isValid()) - timeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); - *sourceTimeStamp = timeStamp.toMSecsSinceEpoch(); + + const qint64 fileSize = fileInfo.size(); + + if (uchar *mappedData = f.map(0, fileSize)) { + QString source = QString::fromUtf8(reinterpret_cast<const char *>(mappedData), fileSize); + f.unmap(mappedData); + return source; } - QByteArray data(f.size(), Qt::Uninitialized); + + QByteArray data(fileSize, Qt::Uninitialized); if (f.read(data.data(), data.length()) != data.length()) { *error = f.errorString(); - return QByteArray(); + return QString(); } - return data; + return QString::fromUtf8(data); +} + +QDateTime QQmlDataBlob::SourceCodeData::sourceTimeStamp() const +{ + if (!inlineSourceCode.isEmpty()) + return QDateTime(); + + QDateTime timeStamp = fileInfo.lastModified(); + if (timeStamp.isValid()) + return timeStamp; + + static QDateTime appTimeStamp; + if (!appTimeStamp.isValid()) + appTimeStamp = QFileInfo(QCoreApplication::applicationFilePath()).lastModified(); + return appTimeStamp; +} + +bool QQmlDataBlob::SourceCodeData::exists() const +{ + if (!inlineSourceCode.isEmpty()) + return true; + return fileInfo.exists(); } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index 915b1bcc4c..48e7d5cba4 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -54,6 +54,7 @@ #include <QtQml/qtqmlglobal.h> #include <QtCore/qobject.h> #include <QtCore/qatomic.h> +#include <QtCore/qfileinfo.h> #if QT_CONFIG(qml_network) #include <QtNetwork/qnetworkreply.h> #endif @@ -130,16 +131,16 @@ public: QList<QQmlError> errors() const; - class Data { + class SourceCodeData { public: - QByteArray readAll(QString *error, qint64 *sourceTimeStamp = 0) const; + QString readAll(QString *error) const; + QDateTime sourceTimeStamp() const; + bool exists() const; private: friend class QQmlDataBlob; friend class QQmlTypeLoader; - inline Data(); - Data(const Data &); - Data &operator=(const Data &); - QBiPointer<const QByteArray, const QString> d; + QString inlineSourceCode; + QFileInfo fileInfo; }; protected: @@ -152,7 +153,7 @@ protected: void addDependency(QQmlDataBlob *); // Callbacks made in load thread - virtual void dataReceived(const Data &) = 0; + virtual void dataReceived(const SourceCodeData &) = 0; virtual void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit*) = 0; virtual void done(); #if QT_CONFIG(qml_network) @@ -339,7 +340,7 @@ private: void setData(QQmlDataBlob *, const QByteArray &); void setData(QQmlDataBlob *, const QString &fileName); - void setData(QQmlDataBlob *, const QQmlDataBlob::Data &); + void setData(QQmlDataBlob *, const QQmlDataBlob::SourceCodeData &); void setCachedUnit(QQmlDataBlob *blob, const QQmlPrivate::CachedQmlUnit *unit); template<typename T> @@ -400,6 +401,7 @@ public: int minorVersion; QQmlTypeData *typeData; QString prefix; // used by CompositeSingleton types + QString qualifiedName() const; bool needsCreation; }; @@ -436,7 +438,7 @@ public: protected: void done() override; void completed() override; - void dataReceived(const Data &) override; + void dataReceived(const SourceCodeData &) override; void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit) override; void allDependenciesDone() override; void downloadProgressChanged(qreal) override; @@ -454,7 +456,7 @@ private: QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache ) const; void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, - const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); + const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache, const QV4::CompiledData::DependentTypesHasher &dependencyHasher); void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref, int lineNumber = -1, int columnNumber = -1, bool reportErrors = true); @@ -462,8 +464,7 @@ private: void scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData::Location &location, const QString &qualifier, const QString &nameSpace) override; - qint64 m_sourceTimeStamp = 0; - QByteArray m_backupSourceCode; // used when cache verification fails. + SourceCodeData m_backupSourceCode; // used when cache verification fails. QScopedPointer<QmlIR::Document> m_document; QV4::CompiledData::TypeReferenceMap m_typeReferences; @@ -547,7 +548,7 @@ public: QQmlScriptData *scriptData() const; protected: - void dataReceived(const Data &) override; + void dataReceived(const SourceCodeData &) override; void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit *unit) override; void done() override; @@ -578,7 +579,7 @@ public: void setPriority(int); protected: - void dataReceived(const Data &) override; + void dataReceived(const SourceCodeData &) override; void initializeFromCachedUnit(const QQmlPrivate::CachedQmlUnit*) override; private: @@ -587,11 +588,6 @@ private: int m_priority; }; -QQmlDataBlob::Data::Data() -{ -} - - QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 49103ed653..7b98096a7f 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -342,4 +342,44 @@ bool QmlTypeWrapper::isEqualTo(Managed *a, Managed *b) return false; } +ReturnedValue QmlTypeWrapper::instanceOf(const Object *typeObject, const Value &var) +{ + Q_ASSERT(typeObject->as<QV4::QmlTypeWrapper>()); + const QV4::QmlTypeWrapper *typeWrapper = static_cast<const QV4::QmlTypeWrapper *>(typeObject); + QV4::ExecutionEngine *engine = typeObject->internalClass()->engine; + QQmlEnginePrivate *qenginepriv = QQmlEnginePrivate::get(engine->qmlEngine()); + + // can only compare a QObject* against a QML type + const QObjectWrapper *wrapper = var.as<QObjectWrapper>(); + if (!wrapper) + return engine->throwTypeError(); + + // in case the wrapper outlived the QObject* + const QObject *wrapperObject = wrapper->object(); + if (!wrapperObject) + return engine->throwTypeError(); + + const int myTypeId = typeWrapper->d()->type->typeId(); + QQmlMetaObject myQmlType; + if (myTypeId == 0) { + // we're a composite type; a composite type cannot be equal to a + // non-composite object instance (Rectangle{} is never an instance of + // CustomRectangle) + QQmlData *theirDData = QQmlData::get(wrapperObject, /*create=*/false); + Q_ASSERT(theirDData); // must exist, otherwise how do we have a QObjectWrapper for it?! + if (!theirDData->compilationUnit) + return Encode(false); + + QQmlTypeData *td = qenginepriv->typeLoader.getType(typeWrapper->d()->type->sourceUrl()); + CompiledData::CompilationUnit *cu = td->compilationUnit(); + myQmlType = qenginepriv->metaObjectForType(cu->metaTypeId); + } else { + myQmlType = qenginepriv->metaObjectForType(myTypeId); + } + + const QMetaObject *theirType = wrapperObject->metaObject(); + + return QV4::Encode(QQmlMetaObject::canConvert(theirType, myQmlType)); +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index cfb6cb0ec9..c584458ed4 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -103,7 +103,7 @@ struct Q_QML_EXPORT QmlTypeWrapper : Object static bool put(Managed *m, String *name, const Value &value); static PropertyAttributes query(const Managed *, String *name); static bool isEqualTo(Managed *that, Managed *o); - + static ReturnedValue instanceOf(const Object *typeObject, const Value &var); }; } diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 490a4e19ab..9f86d1cae9 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -104,8 +104,10 @@ void QQmlVMEVariantQObjectPtr::objectDestroyed(QObject *) if (v4) { QV4::Scope scope(v4); QV4::Scoped<QV4::MemberData> sp(scope, m_target->propertyAndMethodStorage.value()); - if (sp) - *(sp->data() + m_index) = QV4::Primitive::nullValue(); + if (sp) { + QV4::MemberData::Index index{ sp->d(), static_cast<uint>(m_index) }; + index.set(v4, QV4::Primitive::nullValue()); + } } m_target->activate(m_target->object, m_target->methodOffset() + m_index, 0); @@ -329,7 +331,7 @@ QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj, if (size) { QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, size); propertyAndMethodStorage.set(v4, data); - std::fill(data->data, data->data + data->size, QV4::Encode::undefined()); + std::fill(data->values.values, data->values.values + data->values.size, QV4::Encode::undefined()); } // Need JS wrapper to ensure properties/methods are marked. @@ -364,77 +366,77 @@ void QQmlVMEMetaObject::writeProperty(int id, int v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = QV4::Primitive::fromInt32(v); + md->set(cache->engine, id, QV4::Primitive::fromInt32(v)); } void QQmlVMEMetaObject::writeProperty(int id, bool v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = QV4::Primitive::fromBoolean(v); + md->set(cache->engine, id, QV4::Primitive::fromBoolean(v)); } void QQmlVMEMetaObject::writeProperty(int id, double v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = QV4::Primitive::fromDouble(v); + md->set(cache->engine, id, QV4::Primitive::fromDouble(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QString& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newString(v); + md->set(cache->engine, id, cache->engine->newString(v)); } void QQmlVMEMetaObject::writeProperty(int id, const QUrl& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + md->set(cache->engine, id, cache->engine->newVariantObject(QVariant::fromValue(v))); } void QQmlVMEMetaObject::writeProperty(int id, const QDate& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + md->set(cache->engine, id, cache->engine->newVariantObject(QVariant::fromValue(v))); } void QQmlVMEMetaObject::writeProperty(int id, const QDateTime& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + md->set(cache->engine, id, cache->engine->newVariantObject(QVariant::fromValue(v))); } void QQmlVMEMetaObject::writeProperty(int id, const QPointF& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + md->set(cache->engine, id, cache->engine->newVariantObject(QVariant::fromValue(v))); } void QQmlVMEMetaObject::writeProperty(int id, const QSizeF& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + md->set(cache->engine, id, cache->engine->newVariantObject(QVariant::fromValue(v))); } void QQmlVMEMetaObject::writeProperty(int id, const QRectF& v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = cache->engine->newVariantObject(QVariant::fromValue(v)); + md->set(cache->engine, id, cache->engine->newVariantObject(QVariant::fromValue(v))); } void QQmlVMEMetaObject::writeProperty(int id, QObject* v) { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) - *(md->data() + id) = QV4::QObjectWrapper::wrap(cache->engine, v); + md->set(cache->engine, id, QV4::Value::fromReturnedValue(QV4::QObjectWrapper::wrap(cache->engine, v))); QQmlVMEVariantQObjectPtr *guard = getQObjectGuardForProperty(id); if (v && !guard) { @@ -592,7 +594,7 @@ QList<QObject *> *QQmlVMEMetaObject::readPropertyAsList(int id) const if (!v || (int)v->d()->data().userType() != qMetaTypeId<QList<QObject *> >()) { QVariant variant(qVariantFromValue(QList<QObject*>())); v = cache->engine->newVariantObject(variant); - *(md->data() + id) = v; + md->set(cache->engine, id, v); } return static_cast<QList<QObject *> *>(v->d()->data().data()); } @@ -742,7 +744,7 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { QVariant propertyAsVariant; - if (QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>()) + if (const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>()) propertyAsVariant = v->d()->data(); QQml_valueTypeProvider()->readValueType(propertyAsVariant, a[0], fallbackMetaType); } @@ -815,9 +817,9 @@ int QQmlVMEMetaObject::metaCall(QObject *o, QMetaObject::Call c, int _id, void * case QV4::CompiledData::Property::Quaternion: Q_ASSERT(fallbackMetaType != QMetaType::UnknownType); if (QV4::MemberData *md = propertyAndMethodStorageAsMemberData()) { - QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); + const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); if (!v) { - *(md->data() + id) = cache->engine->newVariantObject(QVariant()); + md->set(cache->engine, id, cache->engine->newVariantObject(QVariant())); v = (md->data() + id)->as<QV4::VariantObject>(); QQml_valueTypeProvider()->initValueType(fallbackMetaType, v->d()->data()); } @@ -1028,7 +1030,7 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) // Importantly, if the current value is a scarce resource, we need to ensure that it // gets automatically released by the engine if no other references to it exist. - QV4::VariantObject *oldVariant = (md->data() + id)->as<QV4::VariantObject>(); + const QV4::VariantObject *oldVariant = (md->data() + id)->as<QV4::VariantObject>(); if (oldVariant) oldVariant->removeVmePropertyReference(); @@ -1054,7 +1056,7 @@ void QQmlVMEMetaObject::writeVarProperty(int id, const QV4::Value &value) guard->setGuardedValue(valueObject, this, id); // Write the value and emit change signal as appropriate. - *(md->data() + id) = value; + md->set(cache->engine, id, value); activate(object, methodOffset() + id, 0); } @@ -1067,7 +1069,7 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) // Importantly, if the current value is a scarce resource, we need to ensure that it // gets automatically released by the engine if no other references to it exist. - QV4::VariantObject *oldv = (md->data() + id)->as<QV4::VariantObject>(); + const QV4::VariantObject *oldv = (md->data() + id)->as<QV4::VariantObject>(); if (oldv) oldv->removeVmePropertyReference(); @@ -1081,7 +1083,7 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) // Write the value and emit change signal as appropriate. QVariant currentValue = readPropertyAsVariant(id); - *(md->data() + id) = newv; + md->set(cache->engine, id, newv); if ((currentValue.userType() != value.userType() || currentValue != value)) activate(object, methodOffset() + id, 0); } else { @@ -1093,14 +1095,14 @@ void QQmlVMEMetaObject::writeProperty(int id, const QVariant &value) } else { QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (md) { - QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); + const QV4::VariantObject *v = (md->data() + id)->as<QV4::VariantObject>(); needActivate = (!v || v->d()->data().userType() != value.userType() || v->d()->data() != value); if (v) v->removeVmePropertyReference(); - *(md->data() + id) = cache->engine->newVariantObject(value); - v = static_cast<QV4::VariantObject *>(md->data() + id); + md->set(cache->engine, id, cache->engine->newVariantObject(value)); + v = static_cast<const QV4::VariantObject *>(md->data() + id); v->addVmePropertyReference(); } } @@ -1139,7 +1141,7 @@ void QQmlVMEMetaObject::setVmeMethod(int index, const QV4::Value &function) QV4::MemberData *md = propertyAndMethodStorageAsMemberData(); if (!md) return; - *(md->data() + methodIndex + compiledObject->nProperties) = function; + md->set(cache->engine, methodIndex + compiledObject->nProperties, function); } QV4::ReturnedValue QQmlVMEMetaObject::vmeProperty(int index) const @@ -1168,16 +1170,16 @@ void QQmlVMEMetaObject::ensureQObjectWrapper() QV4::QObjectWrapper::wrap(v4, object); } -void QQmlVMEMetaObject::mark(QV4::ExecutionEngine *e) +void QQmlVMEMetaObject::mark(QV4::MarkStack *markStack) { QV4::ExecutionEngine *v4 = cache ? cache->engine : 0; - if (v4 != e) + if (v4 != markStack->engine) return; - propertyAndMethodStorage.markOnce(e); + propertyAndMethodStorage.markOnce(markStack); if (QQmlVMEMetaObject *parent = parentVMEMetaObject()) - parent->mark(e); + parent->mark(markStack); } bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index bb6fede7c8..031a9a9ddd 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -203,7 +203,7 @@ public: void ensureQObjectWrapper(); - void mark(QV4::ExecutionEngine *e); + void mark(QV4::MarkStack *markStack); void connectAlias(int aliasId); diff --git a/src/qml/qml/qqmlxmlhttprequest.cpp b/src/qml/qml/qqmlxmlhttprequest.cpp index d0d9f080da..b18904fc73 100644 --- a/src/qml/qml/qqmlxmlhttprequest.cpp +++ b/src/qml/qml/qqmlxmlhttprequest.cpp @@ -1597,10 +1597,12 @@ struct QQmlXMLHttpRequestWrapper : Object { QQmlXMLHttpRequest *request; }; -struct QQmlXMLHttpRequestCtor : FunctionObject { - void init(ExecutionEngine *engine); +#define QQmlXMLHttpRequestCtorMembers(class, Member) \ + Member(class, Pointer, Object *, proto) - Pointer<Object> proto; +DECLARE_HEAP_OBJECT(QQmlXMLHttpRequestCtor, FunctionObject) { + DECLARE_MARK_TABLE(QQmlXMLHttpRequestCtor); + void init(ExecutionEngine *engine); }; } @@ -1614,12 +1616,7 @@ struct QQmlXMLHttpRequestWrapper : public Object struct QQmlXMLHttpRequestCtor : public FunctionObject { V4_OBJECT2(QQmlXMLHttpRequestCtor, FunctionObject) - static void markObjects(Heap::Base *that, ExecutionEngine *e) { - QQmlXMLHttpRequestCtor::Data *c = static_cast<QQmlXMLHttpRequestCtor::Data *>(that); - if (c->proto) - c->proto->mark(e); - FunctionObject::markObjects(that, e); - } + static void construct(const Managed *that, Scope &scope, QV4::CallData *) { Scoped<QQmlXMLHttpRequestCtor> ctor(scope, that->as<QQmlXMLHttpRequestCtor>()); @@ -1686,7 +1683,7 @@ void QQmlXMLHttpRequestCtor::setupProto() ExecutionEngine *v4 = engine(); Scope scope(v4); ScopedObject p(scope, v4->newObject()); - d()->proto = p->d(); + d()->proto.set(scope.engine, p->d()); // Methods p->defineDefaultProperty(QStringLiteral("open"), method_open); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index d359a0f62f..8cc0b32168 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1885,7 +1885,7 @@ void GlobalExtensions::method_qsTr(const BuiltinFunction *, Scope &scope, CallDa ExecutionContext *parentCtx = scope.engine->currentContext; // The first non-empty source URL in the call stack determines the translation context. while (!!parentCtx && context.isEmpty()) { - if (QV4::CompiledData::CompilationUnit *unit = parentCtx->d()->compilationUnit) { + if (CompiledData::CompilationUnit *unit = static_cast<CompiledData::CompilationUnit*>(parentCtx->d()->compilationUnit)) { QString fileName = unit->fileName(); QUrl url(unit->fileName()); if (url.isValid() && url.isRelative()) { diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index a5878dcffd..f26e5f6cdb 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -837,10 +837,9 @@ void QQDMIncubationTask::statusChanged(Status status) } else if (isDoneIncubating(status)) { Q_ASSERT(incubating); // The model was deleted from under our feet, cleanup ourselves - if (incubating->object) { - delete incubating->object; - - incubating->object = 0; + delete incubating->object; + incubating->object = 0; + if (incubating->contextData) { incubating->contextData->destroy(); incubating->contextData = 0; } @@ -1965,9 +1964,8 @@ void QQmlDelegateModelItem::destroyObject() Q_ASSERT(object); Q_ASSERT(contextData); - QObjectPrivate *p = QObjectPrivate::get(object); - Q_ASSERT(p->declarativeData); - QQmlData *data = static_cast<QQmlData*>(p->declarativeData); + QQmlData *data = QQmlData::get(object); + Q_ASSERT(data); if (data->ownContext && data->context) data->context->clearContext(); object->deleteLater(); @@ -1984,10 +1982,8 @@ void QQmlDelegateModelItem::destroyObject() QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) { - QObjectPrivate *p = QObjectPrivate::get(object); - QQmlContextData *context = p->declarativeData - ? static_cast<QQmlData *>(p->declarativeData)->context - : 0; + QQmlData *d = QQmlData::get(object); + QQmlContextData *context = d ? d->context : 0; for (context = context ? context->parent : 0; context; context = context->parent) { if (QQmlDelegateModelItem *cacheItem = qobject_cast<QQmlDelegateModelItem *>( context->contextObject)) { diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index d2e5020738..e85ab5982b 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -7,7 +7,6 @@ SOURCES += \ $$PWD/qqmlmodelsmodule.cpp \ $$PWD/qqmlmodelindexvaluetype.cpp \ $$PWD/qqmlobjectmodel.cpp \ - $$PWD/qqmltimer.cpp \ $$PWD/qquickpackage.cpp \ $$PWD/qquickworkerscript.cpp \ $$PWD/qqmlinstantiator.cpp @@ -23,8 +22,15 @@ HEADERS += \ $$PWD/qqmlmodelsmodule_p.h \ $$PWD/qqmlmodelindexvaluetype_p.h \ $$PWD/qqmlobjectmodel_p.h \ - $$PWD/qqmltimer_p.h \ $$PWD/qquickpackage_p.h \ $$PWD/qquickworkerscript_p.h \ $$PWD/qqmlinstantiator_p.h \ $$PWD/qqmlinstantiator_p_p.h + +qtConfig(animation) { + SOURCES += \ + $$PWD/qqmltimer.cpp + + HEADERS += \ + $$PWD/qqmltimer_p.h +} diff --git a/src/quick/configure.json b/src/quick/configure.json index 4ed11e8318..047fa8c948 100644 --- a/src/quick/configure.json +++ b/src/quick/configure.json @@ -51,6 +51,7 @@ "quick-canvas": { "label": "Canvas item", "purpose": "Provides the Qt Quick Canvas Item", + "condition": "features.quick-path", "output": [ "privateFeature" ] @@ -97,6 +98,14 @@ "privateFeature" ] }, + "quick-particles": { + "label": "Particle support", + "purpose": "Provides a particle system for Qt Quick", + "condition": "features.quick-shadereffect && features.quick-sprite && features.opengl", + "output": [ + "privateFeature" + ] + }, "quick-path": { "label": "Path support", "purpose": "Provides Path elements in Qt Quick", diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index 1167f408f5..dab35f2a54 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -44,8 +44,10 @@ #include <private/qquickcontext2d_p.h> #include <private/qquickcontext2dtexture_p.h> #include <private/qsgadaptationlayer_p.h> +#include <qsgtextureprovider.h> #include <QtQuick/private/qquickpixmapcache_p.h> #include <QtGui/QGuiApplication> +#include <qsgtextureprovider.h> #include <qqmlinfo.h> #include <private/qqmlengine_p.h> @@ -1102,14 +1104,17 @@ bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const QImage QQuickCanvasItem::toImage(const QRectF& rect) const { Q_D(const QQuickCanvasItem); - if (d->context) { - if (rect.isEmpty()) - return d->context->toImage(canvasWindow()); - else - return d->context->toImage(rect); - } - return QImage(); + if (!d->context) + return QImage(); + + const QRectF &rectSource = rect.isEmpty() ? canvasWindow() : rect; + const qreal dpr = window() ? window()->effectiveDevicePixelRatio() : qreal(1); + const QRectF rectScaled(rectSource.topLeft() * dpr, rectSource.size() * dpr); + + QImage image = d->context->toImage(rectScaled); + image.setDevicePixelRatio(dpr); + return image; } static const char* mimeToType(const QString &mime) diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index b9b701313e..1a6f530bfa 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -42,13 +42,16 @@ #include "qquickcanvasitem_p.h" #include <private/qquickcontext2dtexture_p.h> #include <private/qquickitem_p.h> +#if QT_CONFIG(quick_shadereffect) #include <QtQuick/private/qquickshadereffectsource_p.h> +#endif #include <qsgrendererinterface.h> #include <QtQuick/private/qsgcontext_p.h> #include <private/qquicksvgparser_p.h> +#if QT_CONFIG(quick_path) #include <private/qquickpath_p.h> - +#endif #include <private/qquickimage_p_p.h> #include <qqmlinfo.h> @@ -126,8 +129,6 @@ QT_BEGIN_NAMESPACE Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok); -#define DEGREES(t) ((t) * 180.0 / M_PI) - #define CHECK_CONTEXT(r) if (!r || !r->d()->context || !r->d()->context->bufferValid()) \ THROW_GENERIC_ERROR("Not a Context2D object"); @@ -567,9 +568,10 @@ struct QQuickJSContext2D : public QV4::Object static void method_set_shadowOffsetY(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); // should these two be on the proto? +#if QT_CONFIG(quick_path) static void method_get_path(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); static void method_set_path(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - +#endif static void method_get_font(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); static void method_set_font(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); static void method_get_textAlign(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); @@ -925,9 +927,9 @@ struct QQuickJSContext2DImageData : public QV4::Object static void method_get_height(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); static void method_get_data(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); - static void markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *engine) { - static_cast<QQuickJSContext2DImageData::Data *>(that)->pixelData.mark(engine); - QV4::Object::markObjects(that, engine); + static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack) { + static_cast<QQuickJSContext2DImageData::Data *>(that)->pixelData.mark(markStack); + QV4::Object::markObjects(that, markStack); } }; @@ -958,7 +960,7 @@ static QV4::ReturnedValue qt_create_image_data(qreal w, qreal h, QV4::ExecutionE *pixelData->d()->image = QImage(w, h, QImage::Format_ARGB32); pixelData->d()->image->fill(0x00000000); } else { - Q_ASSERT(image.width() == qRound(w) && image.height() == qRound(h)); + Q_ASSERT(image.width()== qRound(w * image.devicePixelRatio()) && image.height() == qRound(h * image.devicePixelRatio())); *pixelData->d()->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32); } @@ -1639,7 +1641,7 @@ void QQuickJSContext2DPrototype::method_createConicalGradient(const QV4::Builtin if (callData->argc >= 3) { qreal x = callData->args[0].toNumber(); qreal y = callData->args[1].toNumber(); - qreal angle = DEGREES(callData->args[2].toNumber()); + qreal angle = qRadiansToDegrees(callData->args[2].toNumber()); if (!qt_is_finite(x) || !qt_is_finite(y)) { THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments"); } @@ -2032,6 +2034,7 @@ void QQuickJSContext2D::method_set_shadowOffsetY(const QV4::BuiltinFunction *, Q RETURN_UNDEFINED(); } +#if QT_CONFIG(quick_path) void QQuickJSContext2D::method_get_path(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) { QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject); @@ -2058,6 +2061,7 @@ void QQuickJSContext2D::method_set_path(const QV4::BuiltinFunction *, QV4::Scope r->d()->context->m_v4path.set(scope.engine, value); RETURN_UNDEFINED(); } +#endif // QT_CONFIG(quick_path) //rects /*! @@ -3364,7 +3368,7 @@ void QQuickContext2D::rotate(qreal angle) return; QTransform newTransform =state.matrix; - newTransform.rotate(DEGREES(angle)); + newTransform.rotate(qRadiansToDegrees(angle)); if (!newTransform.isInvertible()) { state.invertibleCTM = false; @@ -3373,7 +3377,7 @@ void QQuickContext2D::rotate(qreal angle) state.matrix = newTransform; buffer()->updateMatrix(state.matrix); - m_path = QTransform().rotate(-DEGREES(angle)).map(m_path); + m_path = QTransform().rotate(-qRadiansToDegrees(angle)).map(m_path); } void QQuickContext2D::shear(qreal h, qreal v) @@ -3770,8 +3774,8 @@ void QQuickContext2D::arc(qreal xc, qreal yc, qreal radius, qreal sar, qreal ear antiClockWise = !antiClockWise; //end hack - float sa = DEGREES(sar); - float ea = DEGREES(ear); + float sa = qRadiansToDegrees(sar); + float ea = qRadiansToDegrees(ear); double span = 0; @@ -4208,7 +4212,9 @@ QQuickContext2DEngineData::QQuickContext2DEngineData(QV4::ExecutionEngine *v4) proto->defineAccessorProperty(QStringLiteral("fillStyle"), QQuickJSContext2D::method_get_fillStyle, QQuickJSContext2D::method_set_fillStyle); proto->defineAccessorProperty(QStringLiteral("shadowColor"), QQuickJSContext2D::method_get_shadowColor, QQuickJSContext2D::method_set_shadowColor); proto->defineAccessorProperty(QStringLiteral("textBaseline"), QQuickJSContext2D::method_get_textBaseline, QQuickJSContext2D::method_set_textBaseline); +#if QT_CONFIG(quick_path) proto->defineAccessorProperty(QStringLiteral("path"), QQuickJSContext2D::method_get_path, QQuickJSContext2D::method_set_path); +#endif proto->defineAccessorProperty(QStringLiteral("lineJoin"), QQuickJSContext2D::method_get_lineJoin, QQuickJSContext2D::method_set_lineJoin); proto->defineAccessorProperty(QStringLiteral("lineWidth"), QQuickJSContext2D::method_get_lineWidth, QQuickJSContext2D::method_set_lineWidth); proto->defineAccessorProperty(QStringLiteral("textAlign"), QQuickJSContext2D::method_get_textAlign, QQuickJSContext2D::method_set_textAlign); diff --git a/src/quick/items/qquickdrag_p.h b/src/quick/items/qquickdrag_p.h index 357f72b3e7..17e9d8c690 100644 --- a/src/quick/items/qquickdrag_p.h +++ b/src/quick/items/qquickdrag_p.h @@ -248,7 +248,7 @@ class QQuickDragAttached : public QObject Q_PROPERTY(QObject *source READ source WRITE setSource NOTIFY sourceChanged RESET resetSource) Q_PROPERTY(QObject *target READ target NOTIFY targetChanged) Q_PROPERTY(QPointF hotSpot READ hotSpot WRITE setHotSpot NOTIFY hotSpotChanged) - Q_PROPERTY(QUrl imageSource READ imageSource WRITE setImageSource NOTIFY imageSourceChanged REVISION 8) + Q_PROPERTY(QUrl imageSource READ imageSource WRITE setImageSource NOTIFY imageSourceChanged) Q_PROPERTY(QStringList keys READ keys WRITE setKeys NOTIFY keysChanged) Q_PROPERTY(QVariantMap mimeData READ mimeData WRITE setMimeData NOTIFY mimeDataChanged) Q_PROPERTY(Qt::DropActions supportedActions READ supportedActions WRITE setSupportedActions NOTIFY supportedActionsChanged) diff --git a/src/quick/items/qquickevents.cpp b/src/quick/items/qquickevents.cpp index 2671b9c9a2..19dd81b6fc 100644 --- a/src/quick/items/qquickevents.cpp +++ b/src/quick/items/qquickevents.cpp @@ -195,8 +195,8 @@ Item { */ /*! - \qmlproperty int QtQuick::MouseEvent::x - \qmlproperty int QtQuick::MouseEvent::y + \qmlproperty real QtQuick::MouseEvent::x + \qmlproperty real QtQuick::MouseEvent::y These properties hold the coordinates of the position supplied by the mouse event. */ @@ -342,8 +342,8 @@ Item { */ /*! - \qmlproperty int QtQuick::WheelEvent::x - \qmlproperty int QtQuick::WheelEvent::y + \qmlproperty real QtQuick::WheelEvent::x + \qmlproperty real QtQuick::WheelEvent::y These properties hold the coordinates of the position supplied by the wheel event. */ @@ -1225,9 +1225,24 @@ QTouchEvent *QQuickPointerTouchEvent::touchEventForItem(QQuickItem *item, bool i auto p = m_touchPoints.at(i); if (p->isAccepted()) continue; + // include points where item is the grabber bool isGrabber = p->exclusiveGrabber() == item; + // include newly pressed points inside the bounds bool isPressInside = p->state() == QQuickEventPoint::Pressed && item->contains(item->mapFromScene(p->scenePos())); - if (!(isGrabber || isPressInside || isFiltering)) + + // filtering: (childMouseEventFilter) include points that are grabbed by children of the target item + bool grabberIsChild = false; + auto parent = p->grabberItem(); + while (isFiltering && parent) { + if (parent == item) { + grabberIsChild = true; + break; + } + parent = parent->parentItem(); + } + bool filterRelevant = isFiltering && grabberIsChild; + + if (!(isGrabber || isPressInside || filterRelevant)) continue; const QTouchEvent::TouchPoint *tp = touchPointById(p->pointId()); diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index 6306a48d48..40719c6324 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -255,6 +255,7 @@ QQuickFlickablePrivate::QQuickFlickablePrivate() , flickBoost(1.0), fixupMode(Normal), vTime(0), visibleArea(0) , flickableDirection(QQuickFlickable::AutoFlickDirection) , boundsBehavior(QQuickFlickable::DragAndOvershootBounds) + , boundsMovement(QQuickFlickable::FollowBoundsBehavior) , rebound(0) { } @@ -643,8 +644,10 @@ is finished. \section1 Limitations - \note Due to an implementation detail, items placed inside a Flickable cannot anchor to it by - \c id. Use \c parent instead. + \note Due to an implementation detail, items placed inside a Flickable + cannot anchor to the Flickable. Instead, use \l {Item::}{parent}, which + refers to the Flickable's \l contentItem. The size of the content item is + determined by \l contentWidth and \l contentHeight. */ /*! @@ -1575,16 +1578,18 @@ void QQuickFlickablePrivate::replayDelayedPress() void QQuickFlickablePrivate::setViewportX(qreal x) { Q_Q(QQuickFlickable); - if (pixelAligned) - x = -Round(-x); - - contentItem->setX(x); - if (contentItem->x() != x) - return; // reentered + qreal effectiveX = pixelAligned ? -Round(-x) : x; const qreal maxX = q->maxXExtent(); const qreal minX = q->minXExtent(); + if (boundsMovement == int(QQuickFlickable::StopAtBounds)) + effectiveX = qBound(maxX, effectiveX, minX); + + contentItem->setX(effectiveX); + if (contentItem->x() != effectiveX) + return; // reentered + qreal overshoot = 0.0; if (x <= maxX) overshoot = maxX - x; @@ -1600,16 +1605,18 @@ void QQuickFlickablePrivate::setViewportX(qreal x) void QQuickFlickablePrivate::setViewportY(qreal y) { Q_Q(QQuickFlickable); - if (pixelAligned) - y = -Round(-y); - - contentItem->setY(y); - if (contentItem->y() != y) - return; // reentered + qreal effectiveY = pixelAligned ? -Round(-y) : y; const qreal maxY = q->maxYExtent(); const qreal minY = q->minYExtent(); + if (boundsMovement == int(QQuickFlickable::StopAtBounds)) + effectiveY = qBound(maxY, effectiveY, minY); + + contentItem->setY(effectiveY); + if (contentItem->y() != effectiveY) + return; // reentered + qreal overshoot = 0.0; if (y <= maxY) overshoot = maxY - y; @@ -1869,8 +1876,9 @@ QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren() beyond the Flickable's boundaries, or overshoot the Flickable's boundaries when flicked. - This enables the feeling that the edges of the view are soft, - rather than a hard physical boundary. + When the \l boundsMovement is \c Flickable.FollowBoundsBehavior, a value + other than \c Flickable.StopAtBounds will give a feeling that the edges of + the view are soft, rather than a hard physical boundary. The \c boundsBehavior can be one of: @@ -1886,7 +1894,7 @@ QQmlListProperty<QQuickItem> QQuickFlickable::flickableChildren() boundary when flicked. \endlist - \sa horizontalOvershoot, verticalOvershoot + \sa horizontalOvershoot, verticalOvershoot, boundsMovement */ QQuickFlickable::BoundsBehavior QQuickFlickable::boundsBehavior() const { @@ -2303,7 +2311,8 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event) bool receiverDisabled = receiver && !receiver->isEnabled(); bool stealThisEvent = d->stealMouse; - if ((stealThisEvent || contains(localPos)) && (!receiver || !receiver->keepMouseGrab() || receiverDisabled)) { + bool receiverKeepsGrab = receiver && (receiver->keepMouseGrab() || receiver->keepTouchGrab()); + if ((stealThisEvent || contains(localPos)) && (!receiver || !receiverKeepsGrab || receiverDisabled)) { QScopedPointer<QMouseEvent> mouseEvent(QQuickWindowPrivate::cloneMouseEvent(event, &localPos)); mouseEvent->setAccepted(false); @@ -2323,7 +2332,7 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event) default: break; } - if ((receiver && stealThisEvent && !receiver->keepMouseGrab() && receiver != this) || receiverDisabled) { + if ((receiver && stealThisEvent && !receiverKeepsGrab && receiver != this) || receiverDisabled) { d->clearDelayedPress(); grabMouse(); } else if (d->delayedPressEvent) { @@ -2339,7 +2348,7 @@ bool QQuickFlickable::filterMouseEvent(QQuickItem *receiver, QMouseEvent *event) d->lastPosTime = -1; returnToBounds(); } - if (event->type() == QEvent::MouseButtonRelease || (receiver && receiver->keepMouseGrab() && !receiverDisabled)) { + if (event->type() == QEvent::MouseButtonRelease || (receiverKeepsGrab && !receiverDisabled)) { // mouse released, or another item has claimed the grab d->lastPosTime = -1; d->clearDelayedPress(); @@ -2696,7 +2705,11 @@ void QQuickFlickablePrivate::updateVelocity() The value is negative when the content is dragged or flicked beyond the beginning, and positive when beyond the end; \c 0.0 otherwise. - \sa verticalOvershoot, boundsBehavior + Whether the values are reported for dragging and/or flicking is determined by + \l boundsBehavior. The overshoot distance is reported even when \l boundsMovement + is \c Flickable.StopAtBounds. + + \sa verticalOvershoot, boundsBehavior, boundsMovement */ qreal QQuickFlickable::horizontalOvershoot() const { @@ -2713,7 +2726,11 @@ qreal QQuickFlickable::horizontalOvershoot() const The value is negative when the content is dragged or flicked beyond the beginning, and positive when beyond the end; \c 0.0 otherwise. - \sa horizontalOvershoot, boundsBehavior + Whether the values are reported for dragging and/or flicking is determined by + \l boundsBehavior. The overshoot distance is reported even when \l boundsMovement + is \c Flickable.StopAtBounds. + + \sa horizontalOvershoot, boundsBehavior, boundsMovement */ qreal QQuickFlickable::verticalOvershoot() const { @@ -2721,4 +2738,66 @@ qreal QQuickFlickable::verticalOvershoot() const return d->vData.overshoot; } +/*! + \qmlproperty enumeration QtQuick::Flickable::boundsMovement + \since 5.10 + + This property holds whether the flickable will give a feeling that the edges of the + view are soft, rather than a hard physical boundary. + + The \c boundsMovement can be one of: + + \list + \li Flickable.StopAtBounds - this allows implementing custom edge effects where the + contents do not follow drags or flicks beyond the bounds of the flickable. The values + of \l horizontalOvershoot and \l verticalOvershoot can be utilized to implement custom + edge effects. + \li Flickable.FollowBoundsBehavior (default) - whether the contents follow drags or + flicks beyond the bounds of the flickable is determined by \l boundsBehavior. + \endlist + + The following example keeps the contents within bounds and instead applies a flip + effect when flicked over horizontal bounds: + \code + Flickable { + id: flickable + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.DragAndOvershootBounds + transform: Rotation { + axis { x: 0; y: 1; z: 0 } + origin.x: flickable.width / 2 + origin.y: flickable.height / 2 + angle: Math.min(30, Math.max(-30, flickable.horizontalOvershoot)) + } + } + \endcode + + The following example keeps the contents within bounds and instead applies an opacity + effect when dragged over vertical bounds: + \code + Flickable { + boundsMovement: Flickable.StopAtBounds + boundsBehavior: Flickable.DragOverBounds + opacity: Math.max(0.5, 1.0 - Math.abs(verticalOvershoot) / height) + } + \endcode + + \sa boundsBehavior, verticalOvershoot, horizontalOvershoot +*/ +QQuickFlickable::BoundsMovement QQuickFlickable::boundsMovement() const +{ + Q_D(const QQuickFlickable); + return d->boundsMovement; +} + +void QQuickFlickable::setBoundsMovement(BoundsMovement movement) +{ + Q_D(QQuickFlickable); + if (d->boundsMovement == movement) + return; + + d->boundsMovement = movement; + emit boundsMovementChanged(); +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquickflickable_p.h b/src/quick/items/qquickflickable_p.h index 52cade1472..7558ee7df8 100644 --- a/src/quick/items/qquickflickable_p.h +++ b/src/quick/items/qquickflickable_p.h @@ -80,6 +80,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickFlickable : public QQuickItem Q_PROPERTY(qreal verticalVelocity READ verticalVelocity NOTIFY verticalVelocityChanged) Q_PROPERTY(BoundsBehavior boundsBehavior READ boundsBehavior WRITE setBoundsBehavior NOTIFY boundsBehaviorChanged) + Q_PROPERTY(BoundsMovement boundsMovement READ boundsMovement WRITE setBoundsMovement NOTIFY boundsMovementChanged REVISION 10) Q_PROPERTY(QQuickTransition *rebound READ rebound WRITE setRebound NOTIFY reboundChanged) Q_PROPERTY(qreal maximumFlickVelocity READ maximumFlickVelocity WRITE setMaximumFlickVelocity NOTIFY maximumFlickVelocityChanged) Q_PROPERTY(qreal flickDeceleration READ flickDeceleration WRITE setFlickDeceleration NOTIFY flickDecelerationChanged) @@ -132,6 +133,15 @@ public: BoundsBehavior boundsBehavior() const; void setBoundsBehavior(BoundsBehavior); + enum BoundsMovement { + // StopAtBounds = 0x0, + FollowBoundsBehavior = 0x1 + }; + Q_ENUM(BoundsMovement) + + BoundsMovement boundsMovement() const; + void setBoundsMovement(BoundsMovement movement); + QQuickTransition *rebound() const; void setRebound(QQuickTransition *transition); @@ -237,6 +247,7 @@ Q_SIGNALS: void flickableDirectionChanged(); void interactiveChanged(); void boundsBehaviorChanged(); + Q_REVISION(10) void boundsMovementChanged(); void reboundChanged(); void maximumFlickVelocityChanged(); void flickDecelerationChanged(); diff --git a/src/quick/items/qquickflickable_p_p.h b/src/quick/items/qquickflickable_p_p.h index 1ceff22dfc..8609a15fcd 100644 --- a/src/quick/items/qquickflickable_p_p.h +++ b/src/quick/items/qquickflickable_p_p.h @@ -247,6 +247,7 @@ public: QQuickFlickableVisibleArea *visibleArea; QQuickFlickable::FlickableDirection flickableDirection; QQuickFlickable::BoundsBehavior boundsBehavior; + QQuickFlickable::BoundsMovement boundsMovement; QQuickTransition *rebound; void viewportAxisMoved(AxisData &data, qreal minExtent, qreal maxExtent, qreal vSize, diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp index f3d7dc4b56..bf982117e8 100644 --- a/src/quick/items/qquickimage.cpp +++ b/src/quick/items/qquickimage.cpp @@ -559,7 +559,8 @@ void QQuickImage::updatePaintedGeometry() void QQuickImage::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { QQuickImageBase::geometryChanged(newGeometry, oldGeometry); - updatePaintedGeometry(); + if (newGeometry.size() != oldGeometry.size()) + updatePaintedGeometry(); } QRectF QQuickImage::boundingRect() const diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index 1edc54aca2..bfc4a59851 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -6780,8 +6780,27 @@ bool QQuickItem::heightValid() const } /*! - \internal - */ + \since 5.10 + + Returns the size of the item. + + \sa setSize, width, height + */ + +QSizeF QQuickItem::size() const +{ + Q_D(const QQuickItem); + return QSizeF(d->width, d->height); +} + + +/*! + \since 5.10 + + Sets the size of the item to \a size. + + \sa size, setWidth, setHeight + */ void QQuickItem::setSize(const QSizeF &size) { Q_D(QQuickItem); @@ -8456,19 +8475,19 @@ struct QQuickItemWrapper : public QObjectWrapper { struct QQuickItemWrapper : public QV4::QObjectWrapper { V4_OBJECT2(QQuickItemWrapper, QV4::QObjectWrapper) - static void markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *e); + static void markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack); }; DEFINE_OBJECT_VTABLE(QQuickItemWrapper); -void QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::ExecutionEngine *e) +void QQuickItemWrapper::markObjects(QV4::Heap::Base *that, QV4::MarkStack *markStack) { QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that); if (QQuickItem *item = static_cast<QQuickItem*>(This->object())) { for (QQuickItem *child : qAsConst(QQuickItemPrivate::get(item)->childItems)) - QV4::QObjectWrapper::markWrapper(child, e); + QV4::QObjectWrapper::markWrapper(child, markStack); } - QV4::QObjectWrapper::markObjects(that, e); + QV4::QObjectWrapper::markObjects(that, markStack); } quint64 QQuickItemPrivate::_q_createJSWrapper(QV4::ExecutionEngine *engine) diff --git a/src/quick/items/qquickitem.h b/src/quick/items/qquickitem.h index 279d052db9..1d174ca979 100644 --- a/src/quick/items/qquickitem.h +++ b/src/quick/items/qquickitem.h @@ -237,6 +237,7 @@ public: void setImplicitHeight(qreal); qreal implicitHeight() const; + QSizeF size() const; void setSize(const QSizeF &size); TransformOrigin transformOrigin() const; diff --git a/src/quick/items/qquickitemanimation.cpp b/src/quick/items/qquickitemanimation.cpp index 9873622f41..874130b137 100644 --- a/src/quick/items/qquickitemanimation.cpp +++ b/src/quick/items/qquickitemanimation.cpp @@ -327,7 +327,7 @@ QAbstractAnimationJob* QQuickParentAnimation::transition(QQuickStateActions &act } if (scale != 0) - rotation = qAtan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI; + rotation = qRadiansToDegrees(qAtan2(transform.m12() / scale, transform.m11() / scale)); else { qmlWarning(this) << QQuickParentAnimation::tr("Unable to preserve appearance under scale of 0"); ok = false; diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index eeffe1ee48..13f23c918a 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -273,7 +273,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterType<QQuickAnchorSet>(); qmlRegisterType<QQuickAnchorAnimation>(uri, major, minor,"AnchorAnimation"); qmlRegisterType<QQuickParentAnimation>(uri, major, minor,"ParentAnimation"); -#if QT_CONFIG(quick_canvas) +#if QT_CONFIG(quick_path) qmlRegisterType<QQuickPathAnimation>("QtQuick",2,0,"PathAnimation"); qmlRegisterType<QQuickPathInterpolator>("QtQuick",2,0,"PathInterpolator"); #endif @@ -389,6 +389,8 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) #if QT_CONFIG(quick_shadereffect) qmlRegisterType<QQuickShaderEffectSource, 2>(uri, 2, 9, "ShaderEffectSource"); #endif + + qmlRegisterType<QQuickFlickable, 10>(uri, 2, 10, "Flickable"); } static void initResources() diff --git a/src/quick/items/qquickpainteditem_p.h b/src/quick/items/qquickpainteditem_p.h index 742e786335..3e160ed55a 100644 --- a/src/quick/items/qquickpainteditem_p.h +++ b/src/quick/items/qquickpainteditem_p.h @@ -51,7 +51,9 @@ // We mean it. // +#include "qquickpainteditem.h" #include "qquickitem_p.h" +#include "qquickpainteditem.h" #include <QtGui/qcolor.h> QT_BEGIN_NAMESPACE diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp index 70fc5fa65f..05882d0464 100644 --- a/src/quick/items/qquickpositioners.cpp +++ b/src/quick/items/qquickpositioners.cpp @@ -48,19 +48,8 @@ QT_BEGIN_NAMESPACE -// The default item change types that positioners are interested in. -static const QQuickItemPrivate::ChangeTypes explicitSizeItemChangeTypes = - QQuickItemPrivate::Geometry - | QQuickItemPrivate::SiblingOrder - | QQuickItemPrivate::Visibility - | QQuickItemPrivate::Destroyed; - -// The item change types for positioners that are only interested in the implicit -// size of the items they manage. These are used if useImplicitSize is true. -// useImplicitSize should be set in the constructor, before any items are added. -static const QQuickItemPrivate::ChangeTypes implicitSizeItemChangeTypes = - QQuickItemPrivate::ImplicitWidth - | QQuickItemPrivate::ImplicitHeight +static const QQuickItemPrivate::ChangeTypes watchedChanges + = QQuickItemPrivate::Geometry | QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::Visibility | QQuickItemPrivate::Destroyed; @@ -68,15 +57,13 @@ static const QQuickItemPrivate::ChangeTypes implicitSizeItemChangeTypes = void QQuickBasePositionerPrivate::watchChanges(QQuickItem *other) { QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other); - otherPrivate->addItemChangeListener(this, useImplicitSize - ? implicitSizeItemChangeTypes : explicitSizeItemChangeTypes); + otherPrivate->addItemChangeListener(this, watchedChanges); } void QQuickBasePositionerPrivate::unwatchChanges(QQuickItem* other) { QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other); - otherPrivate->removeItemChangeListener(this, useImplicitSize - ? implicitSizeItemChangeTypes : explicitSizeItemChangeTypes); + otherPrivate->removeItemChangeListener(this, watchedChanges); } @@ -336,7 +323,7 @@ void QQuickBasePositioner::prePositioning() if (wIdx < 0) { d->watchChanges(child); posItem.isNew = true; - if (!childPrivate->explicitVisible || !d->itemWidth(child) || !d->itemHeight(child)) { + if (!childPrivate->explicitVisible || !child->width() || !child->height()) { posItem.isVisible = false; posItem.index = -1; unpositionedItems.append(posItem); @@ -358,7 +345,7 @@ void QQuickBasePositioner::prePositioning() PositionedItem *item = &oldItems[wIdx]; // Items are only omitted from positioning if they are explicitly hidden // i.e. their positioning is not affected if an ancestor is hidden. - if (!childPrivate->explicitVisible || !d->itemWidth(child) || !d->itemHeight(child)) { + if (!childPrivate->explicitVisible || !child->width() || !child->height()) { item->isVisible = false; item->index = -1; unpositionedItems.append(*item); @@ -957,7 +944,6 @@ QQuickColumn::QQuickColumn(QQuickItem *parent) void QQuickColumn::doPositioning(QSizeF *contentSize) { //Precondition: All items in the positioned list have a valid item pointer and should be positioned - QQuickBasePositionerPrivate *d = static_cast<QQuickBasePositionerPrivate*>(QQuickBasePositionerPrivate::get(this)); qreal voffset = topPadding(); const qreal padding = leftPadding() + rightPadding(); contentSize->setWidth(qMax(contentSize->width(), padding)); @@ -966,9 +952,9 @@ void QQuickColumn::doPositioning(QSizeF *contentSize) PositionedItem &child = positionedItems[ii]; positionItem(child.itemX() + leftPadding() - child.leftPadding, voffset, &child); child.updatePadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); - contentSize->setWidth(qMax(contentSize->width(), d->itemWidth(child.item) + padding)); + contentSize->setWidth(qMax(contentSize->width(), child.item->width() + padding)); - voffset += d->itemHeight(child.item); + voffset += child.item->height(); voffset += spacing(); } @@ -1236,9 +1222,9 @@ void QQuickRow::doPositioning(QSizeF *contentSize) hoffsets << hoffset; } - contentSize->setHeight(qMax(contentSize->height(), d->itemHeight(child.item) + padding)); + contentSize->setHeight(qMax(contentSize->height(), child.item->height() + padding)); - hoffset += d->itemWidth(child.item); + hoffset += child.item->width(); hoffset += spacing(); } @@ -1259,7 +1245,7 @@ void QQuickRow::doPositioning(QSizeF *contentSize) int acc = 0; for (int ii = 0; ii < positionedItems.count(); ++ii) { PositionedItem &child = positionedItems[ii]; - hoffset = end - hoffsets[acc++] - d->itemWidth(child.item); + hoffset = end - hoffsets[acc++] - child.item->width(); positionItem(hoffset, child.itemY() + topPadding() - child.topPadding, &child); child.updatePadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); } @@ -1760,12 +1746,10 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) break; const PositionedItem &child = positionedItems.at(childIndex++); - const qreal childWidth = d->itemWidth(child.item); - const qreal childHeight = d->itemHeight(child.item); - if (childWidth > maxColWidth[j]) - maxColWidth[j] = childWidth; - if (childHeight > maxRowHeight[i]) - maxRowHeight[i] = childHeight; + if (child.item->width() > maxColWidth[j]) + maxColWidth[j] = child.item->width(); + if (child.item->height() > maxRowHeight[i]) + maxRowHeight[i] = child.item->height(); } } } else { @@ -1780,12 +1764,10 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) break; const PositionedItem &child = positionedItems.at(childIndex++); - const qreal childWidth = d->itemWidth(child.item); - const qreal childHeight = d->itemHeight(child.item); - if (childWidth > maxColWidth[j]) - maxColWidth[j] = childWidth; - if (childHeight > maxRowHeight[i]) - maxRowHeight[i] = childHeight; + if (child.item->width() > maxColWidth[j]) + maxColWidth[j] = child.item->width(); + if (child.item->height() > maxRowHeight[i]) + maxRowHeight[i] = child.item->height(); } } } @@ -1827,22 +1809,20 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) for (int i = 0; i < positionedItems.count(); ++i) { PositionedItem &child = positionedItems[i]; qreal childXOffset = xoffset; - const qreal childWidth = d->itemWidth(child.item); - const qreal childHeight = d->itemHeight(child.item); if (effectiveHAlign() == AlignRight) - childXOffset += maxColWidth[curCol] - childWidth; + childXOffset += maxColWidth[curCol] - child.item->width(); else if (hItemAlign() == AlignHCenter) - childXOffset += (maxColWidth[curCol] - childWidth)/2.0; + childXOffset += (maxColWidth[curCol] - child.item->width())/2.0; if (!d->isLeftToRight()) childXOffset -= maxColWidth[curCol]; qreal alignYOffset = yoffset; if (m_vItemAlign == AlignVCenter) - alignYOffset += (maxRowHeight[curRow] - childHeight)/2.0; + alignYOffset += (maxRowHeight[curRow] - child.item->height())/2.0; else if (m_vItemAlign == AlignBottom) - alignYOffset += maxRowHeight[curRow] - childHeight; + alignYOffset += maxRowHeight[curRow] - child.item->height(); positionItem(childXOffset, alignYOffset, &child); child.updatePadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); @@ -2160,17 +2140,15 @@ void QQuickFlow::doPositioning(QSizeF *contentSize) for (int i = 0; i < positionedItems.count(); ++i) { PositionedItem &child = positionedItems[i]; - const qreal childWidth = d->itemWidth(child.item); - const qreal childHeight = d->itemHeight(child.item); if (d->flow == LeftToRight) { - if (widthValid() && hoffset != hoffset1 && hoffset + childWidth + hoffset2 > width()) { + if (widthValid() && hoffset != hoffset1 && hoffset + child.item->width() + hoffset2 > width()) { hoffset = hoffset1; voffset += linemax + spacing(); linemax = 0; } } else { - if (heightValid() && voffset != voffset1 && voffset + childHeight + bottomPadding() > height()) { + if (heightValid() && voffset != voffset1 && voffset + child.item->height() + bottomPadding() > height()) { voffset = voffset1; hoffset += linemax + spacing(); linemax = 0; @@ -2187,17 +2165,17 @@ void QQuickFlow::doPositioning(QSizeF *contentSize) child.bottomPadding = bottomPadding(); } - contentSize->setWidth(qMax(contentSize->width(), hoffset + childWidth + hoffset2)); - contentSize->setHeight(qMax(contentSize->height(), voffset + childHeight + bottomPadding())); + contentSize->setWidth(qMax(contentSize->width(), hoffset + child.item->width() + hoffset2)); + contentSize->setHeight(qMax(contentSize->height(), voffset + child.item->height() + bottomPadding())); if (d->flow == LeftToRight) { - hoffset += childWidth; + hoffset += child.item->width(); hoffset += spacing(); - linemax = qMax(linemax, childHeight); + linemax = qMax(linemax, child.item->height()); } else { - voffset += childHeight; + voffset += child.item->height(); voffset += spacing(); - linemax = qMax(linemax, childWidth); + linemax = qMax(linemax, child.item->width()); } } @@ -2212,7 +2190,7 @@ void QQuickFlow::doPositioning(QSizeF *contentSize) int acc = 0; for (int i = 0; i < positionedItems.count(); ++i) { PositionedItem &child = positionedItems[i]; - hoffset = end - hoffsets[acc++] - d->itemWidth(child.item); + hoffset = end - hoffsets[acc++] - child.item->width(); positionItemX(hoffset, &child); child.leftPadding = leftPadding(); child.rightPadding = rightPadding(); @@ -2236,18 +2214,4 @@ void QQuickFlow::reportConflictingAnchors() qmlWarning(this) << "Cannot specify anchors for items inside Flow." << " Flow will not function."; } -QQuickImplicitRow::QQuickImplicitRow(QQuickItem *parent) - : QQuickRow(parent) -{ - QQuickBasePositionerPrivate *d = QQuickBasePositioner::get(this); - d->useImplicitSize = true; -} - -QQuickImplicitGrid::QQuickImplicitGrid(QQuickItem *parent) - : QQuickGrid(parent) -{ - QQuickBasePositionerPrivate *d = QQuickBasePositioner::get(this); - d->useImplicitSize = true; -} - QT_END_NAMESPACE diff --git a/src/quick/items/qquickpositioners_p.h b/src/quick/items/qquickpositioners_p.h index cfe163b4c1..9ae7029d69 100644 --- a/src/quick/items/qquickpositioners_p.h +++ b/src/quick/items/qquickpositioners_p.h @@ -67,7 +67,7 @@ QT_BEGIN_NAMESPACE class QQuickBasePositionerPrivate; -class Q_QUICK_PRIVATE_EXPORT QQuickPositionerAttached : public QObject +class QQuickPositionerAttached : public QObject { Q_OBJECT @@ -132,11 +132,6 @@ public: static QQuickPositionerAttached *qmlAttachedProperties(QObject *obj); - static QQuickBasePositionerPrivate* get(QQuickBasePositioner *positioner) - { - return positioner->d_func(); - } - void updateAttachedProperties(QQuickPositionerAttached *specificProperty = 0, QQuickItem *specificPropertyOwner = 0) const; qreal padding() const; @@ -187,7 +182,7 @@ protected: virtual void doPositioning(QSizeF *contentSize)=0; virtual void reportConflictingAnchors()=0; - class Q_QUICK_PRIVATE_EXPORT PositionedItem + class PositionedItem { public : PositionedItem(QQuickItem *i); @@ -232,7 +227,7 @@ private: Q_DECLARE_PRIVATE(QQuickBasePositioner) }; -class Q_QUICK_PRIVATE_EXPORT QQuickColumn : public QQuickBasePositioner +class Q_AUTOTEST_EXPORT QQuickColumn : public QQuickBasePositioner { Q_OBJECT public: @@ -246,7 +241,7 @@ private: }; class QQuickRowPrivate; -class Q_QUICK_PRIVATE_EXPORT QQuickRow: public QQuickBasePositioner +class Q_AUTOTEST_EXPORT QQuickRow: public QQuickBasePositioner { Q_OBJECT Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) @@ -271,7 +266,7 @@ private: }; class QQuickGridPrivate; -class Q_QUICK_PRIVATE_EXPORT QQuickGrid : public QQuickBasePositioner +class Q_AUTOTEST_EXPORT QQuickGrid : public QQuickBasePositioner { Q_OBJECT Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged) @@ -358,7 +353,7 @@ private: }; class QQuickFlowPrivate; -class Q_QUICK_PRIVATE_EXPORT QQuickFlow: public QQuickBasePositioner +class Q_AUTOTEST_EXPORT QQuickFlow: public QQuickBasePositioner { Q_OBJECT Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) @@ -391,22 +386,6 @@ private: Q_DECLARE_PRIVATE(QQuickFlow) }; -class Q_QUICK_PRIVATE_EXPORT QQuickImplicitRow : public QQuickRow -{ - Q_OBJECT - -public: - QQuickImplicitRow(QQuickItem *parent = nullptr); -}; - -class Q_QUICK_PRIVATE_EXPORT QQuickImplicitGrid : public QQuickGrid -{ - Q_OBJECT - -public: - QQuickImplicitGrid(QQuickItem *parent = nullptr); -}; - QT_END_NAMESPACE @@ -414,8 +393,6 @@ QML_DECLARE_TYPE(QQuickColumn) QML_DECLARE_TYPE(QQuickRow) QML_DECLARE_TYPE(QQuickGrid) QML_DECLARE_TYPE(QQuickFlow) -QML_DECLARE_TYPE(QQuickImplicitRow) -QML_DECLARE_TYPE(QQuickImplicitGrid) QML_DECLARE_TYPE(QQuickBasePositioner) QML_DECLARE_TYPEINFO(QQuickBasePositioner, QML_HAS_ATTACHED_PROPERTIES) diff --git a/src/quick/items/qquickpositioners_p_p.h b/src/quick/items/qquickpositioners_p_p.h index 1a7051615c..0be4c56df6 100644 --- a/src/quick/items/qquickpositioners_p_p.h +++ b/src/quick/items/qquickpositioners_p_p.h @@ -89,14 +89,10 @@ public: QLazilyAllocated<ExtraData> extra; QQuickBasePositionerPrivate() - : spacing(0) - , type(QQuickBasePositioner::None) - , transitioner(0) - , positioningDirty(false) - , doingPositioning(false) - , anchorConflict(false) - , useImplicitSize(false) - , layoutDirection(Qt::LeftToRight) + : spacing(0), type(QQuickBasePositioner::None) + , transitioner(0), positioningDirty(false) + , doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight) + { } @@ -123,7 +119,6 @@ public: bool positioningDirty : 1; bool doingPositioning : 1; bool anchorConflict : 1; - bool useImplicitSize : 1; Qt::LayoutDirection layoutDirection; @@ -179,34 +174,6 @@ public: { } - void itemImplicitWidthChanged(QQuickItem *) override - { - Q_ASSERT(useImplicitSize); - setPositioningDirty(); - } - - void itemImplicitHeightChanged(QQuickItem *) override - { - Q_ASSERT(useImplicitSize); - setPositioningDirty(); - } - - qreal itemWidth(QQuickItem *item) const - { - if (Q_LIKELY(!useImplicitSize)) - return item->width(); - - return item->implicitWidth(); - } - - qreal itemHeight(QQuickItem *item) const - { - if (Q_LIKELY(!useImplicitSize)) - return item->height(); - - return item->implicitHeight(); - } - inline qreal padding() const { return extra.isAllocated() ? extra->padding : 0.0; } void setTopPadding(qreal value, bool reset = false); void setLeftPadding(qreal value, bool reset = false); diff --git a/src/quick/items/qquickrendercontrol.cpp b/src/quick/items/qquickrendercontrol.cpp index 03d96aea1f..e2a20f9e7e 100644 --- a/src/quick/items/qquickrendercontrol.cpp +++ b/src/quick/items/qquickrendercontrol.cpp @@ -385,6 +385,9 @@ QImage QQuickRenderControl::grab() cd->syncSceneGraph(); render(); grabContent = qt_gl_read_framebuffer(d->window->size() * d->window->effectiveDevicePixelRatio(), false, false); + if (QQuickRenderControl::renderWindowFor(d->window)) { + grabContent.setDevicePixelRatio(d->window->effectiveDevicePixelRatio()); + } #endif } else if (d->window->rendererInterface()->graphicsApi() == QSGRendererInterface::Software) { QQuickWindowPrivate *cd = QQuickWindowPrivate::get(d->window); diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index 436d7b33ce..d317c1d19b 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -873,9 +873,11 @@ void QQuickShaderEffectPrivate::updatePolish() q->m_impl->maybeUpdateShaders(); } +#if QT_CONFIG(opengl) bool QQuickShaderEffect::isOpenGLShaderEffect() const { return m_glImpl != Q_NULLPTR; } +#endif QT_END_NAMESPACE diff --git a/src/quick/items/qquickshadereffect_p.h b/src/quick/items/qquickshadereffect_p.h index d269dc5e50..30bd018098 100644 --- a/src/quick/items/qquickshadereffect_p.h +++ b/src/quick/items/qquickshadereffect_p.h @@ -117,7 +117,9 @@ public: bool isComponentComplete() const; QString parseLog(); +#if QT_CONFIG(opengl) bool isOpenGLShaderEffect() const; +#endif Q_SIGNALS: void fragmentShaderChanged(); diff --git a/src/quick/items/qquickstateoperations.cpp b/src/quick/items/qquickstateoperations.cpp index b40a9e2843..a4ce13a199 100644 --- a/src/quick/items/qquickstateoperations.cpp +++ b/src/quick/items/qquickstateoperations.cpp @@ -101,7 +101,7 @@ void QQuickParentChangePrivate::doChange(QQuickItem *targetParent, QQuickItem *s } if (scale != 0) - rotation = qAtan2(transform.m12()/scale, transform.m11()/scale) * 180/M_PI; + rotation = qRadiansToDegrees(qAtan2(transform.m12() / scale, transform.m11() / scale)); else { qmlWarning(q) << QQuickParentChange::tr("Unable to preserve appearance under scale of 0"); ok = false; diff --git a/src/quick/items/qquicktextcontrol.cpp b/src/quick/items/qquicktextcontrol.cpp index 555fd233b3..2dce3e9ec8 100644 --- a/src/quick/items/qquicktextcontrol.cpp +++ b/src/quick/items/qquicktextcontrol.cpp @@ -769,52 +769,8 @@ void QQuickTextControl::processEvent(QEvent *e, const QMatrix &matrix) case QEvent::ShortcutOverride: if (d->interactionFlags & Qt::TextEditable) { QKeyEvent* ke = static_cast<QKeyEvent *>(e); - if (ke->modifiers() == Qt::NoModifier - || ke->modifiers() == Qt::ShiftModifier - || ke->modifiers() == Qt::KeypadModifier) { - if (ke->key() < Qt::Key_Escape) { - ke->accept(); - } else { - switch (ke->key()) { - case Qt::Key_Return: - case Qt::Key_Enter: - case Qt::Key_Delete: - case Qt::Key_Home: - case Qt::Key_End: - case Qt::Key_Backspace: - case Qt::Key_Left: - case Qt::Key_Right: - case Qt::Key_Up: - case Qt::Key_Down: - case Qt::Key_Tab: - ke->accept(); - default: - break; - } - } -#if QT_CONFIG(shortcut) - } else if (ke == QKeySequence::Copy - || ke == QKeySequence::Paste - || ke == QKeySequence::Cut - || ke == QKeySequence::Redo - || ke == QKeySequence::Undo - || ke == QKeySequence::MoveToNextWord - || ke == QKeySequence::MoveToPreviousWord - || ke == QKeySequence::MoveToStartOfDocument - || ke == QKeySequence::MoveToEndOfDocument - || ke == QKeySequence::SelectNextWord - || ke == QKeySequence::SelectPreviousWord - || ke == QKeySequence::SelectStartOfLine - || ke == QKeySequence::SelectEndOfLine - || ke == QKeySequence::SelectStartOfBlock - || ke == QKeySequence::SelectEndOfBlock - || ke == QKeySequence::SelectStartOfDocument - || ke == QKeySequence::SelectEndOfDocument - || ke == QKeySequence::SelectAll - ) { + if (isCommonTextEditShortcut(ke)) ke->accept(); -#endif - } } break; default: diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 0105e84cd6..888b0cde85 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -748,6 +748,7 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) QQuickItem *oldGrabber = q->mouseGrabberItem(); qCDebug(DBG_MOUSE_TARGET) << "grabber" << oldGrabber << "->" << grabber; + bool fromTouch = false; if (grabber && touchMouseId != -1 && touchMouseDevice) { // update the touch item for mouse touch id to the new grabber @@ -758,6 +759,7 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) for (auto handler : point->passiveGrabbers()) point->cancelPassiveGrab(handler); } + fromTouch = true; } else { QQuickPointerEvent *event = QQuickPointerDevice::genericMouseDevice()->pointerEvent(); Q_ASSERT(event->pointCount() == 1); @@ -771,8 +773,11 @@ void QQuickWindowPrivate::setMouseGrabber(QQuickItem *grabber) if (oldGrabber) { QEvent e(QEvent::UngrabMouse); QSet<QQuickItem *> hasFiltered; - if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) + if (!sendFilteredMouseEvent(oldGrabber->parentItem(), oldGrabber, &e, &hasFiltered)) { oldGrabber->mouseUngrabEvent(); + if (fromTouch) + oldGrabber->touchUngrabEvent(); + } } } @@ -2503,7 +2508,6 @@ void QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QEvent *e } else for (; grabItem != grabber->end(); grabItem = grabber->release(grabItem)) { QDragMoveEvent *moveEvent = static_cast<QDragMoveEvent *>(event); if (deliverDragEvent(grabber, **grabItem, moveEvent)) { - moveEvent->setAccepted(true); for (++grabItem; grabItem != grabber->end();) { QPointF p = (**grabItem)->mapFromScene(moveEvent->pos()); if ((**grabItem)->contains(p)) { @@ -2578,7 +2582,10 @@ bool QQuickWindowPrivate::deliverDragEvent(QQuickDragGrabber *grabber, QQuickIte event->keyboardModifiers(), event->type()); QQuickDropEventEx::copyActions(&translatedEvent, *event); + translatedEvent.setAccepted(event->isAccepted()); QCoreApplication::sendEvent(item, &translatedEvent); + event->setAccepted(translatedEvent.isAccepted()); + event->setDropAction(translatedEvent.dropAction()); if (event->type() == QEvent::DragEnter) { if (translatedEvent.isAccepted()) { grabber->grab(item); @@ -2726,18 +2733,23 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ break; } + bool touchMouseUnset = (touchMouseId == -1); // Only deliver mouse event if it is the touchMouseId or it could become the touchMouseId - if (touchMouseId == -1 || touchMouseId == tp.id()) { + if (touchMouseUnset || touchMouseId == tp.id()) { // convert filteringParentTouchEvent (which is already transformed wrt local position, velocity, etc.) // into a synthetic mouse event, and let childMouseEventFilter() have another chance with that QScopedPointer<QMouseEvent> mouseEvent(touchToMouseEvent(t, tp, filteringParentTouchEvent.data(), item, false)); + // If a filtering item calls QQuickWindow::mouseGrabberItem(), it should + // report the touchpoint's grabber. Whenever we send a synthetic mouse event, + // touchMouseId and touchMouseDevice must be set, even if it's only temporarily and isn't grabbed. + touchMouseId = tp.id(); + touchMouseDevice = event->device(); if (filteringParent->childMouseEventFilter(item, mouseEvent.data())) { 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; - touchMouseId = tp.id(); - touchMouseDevice = event->device(); touchMouseDevice->pointerEvent()->pointById(tp.id())->setGrabberItem(filteringParent); + touchMouseUnset = false; // We want to leave touchMouseId and touchMouseDevice set filteringParent->grabMouse(); auto pointerEventPoint = pte->pointById(tp.id()); for (auto handler : pointerEventPoint->passiveGrabbers()) { @@ -2748,9 +2760,15 @@ bool QQuickWindowPrivate::sendFilteredPointerEvent(QQuickPointerEvent *event, QQ } ret = true; } + if (touchMouseUnset) { + // Now that we're done sending a synth mouse event, and it wasn't grabbed, + // the touchpoint is no longer acting as a synthetic mouse. Restore previous state. + touchMouseId = -1; + touchMouseDevice = nullptr; + } // Only one touchpoint can be treated as a synthetic mouse, so after childMouseEventFilter // has been called once, we're done with this loop over the touchpoints. - break; + break; } } } @@ -3542,6 +3560,11 @@ void QQuickWindow::setRenderTarget(QOpenGLFramebufferObject *fbo) The specified FBO must be created in the context of the window or one that shares with it. + \note \a fboId can also be set to 0. In this case rendering will target the + default framebuffer of whichever surface is current when the scenegraph + renders. \a size must still be valid, specifying the dimensions of the + surface. + \note This function only has an effect when using the default OpenGL scene graph adaptation. @@ -3648,6 +3671,7 @@ QImage QQuickWindow::grabWindow() bool alpha = format().alphaBufferSize() > 0 && color().alpha() < 255; QImage image = qt_gl_read_framebuffer(size() * effectiveDevicePixelRatio(), alpha, alpha); + image.setDevicePixelRatio(effectiveDevicePixelRatio()); d->cleanupNodesOnShutdown(); d->context->invalidate(); context.doneCurrent(); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp index 6856d34616..b3b8274a73 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop.cpp @@ -101,7 +101,7 @@ void QSGSoftwareRenderLoop::windowDestroyed(QQuickWindow *window) } } -void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window) +void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window, bool isNewExpose) { QQuickWindowPrivate *cd = QQuickWindowPrivate::get(window); if (!m_windows.contains(window)) @@ -174,7 +174,10 @@ void QSGSoftwareRenderLoop::renderWindow(QQuickWindow *window) if (alsoSwap && window->isVisible()) { //Flush backingstore to window - m_backingStores[window]->flush(softwareRenderer->flushRegion()); + if (!isNewExpose) + m_backingStores[window]->flush(softwareRenderer->flushRegion()); + else + m_backingStores[window]->flush(QRegion(QRect(QPoint(0,0), window->size()))); cd->fireFrameSwapped(); } @@ -206,7 +209,7 @@ void QSGSoftwareRenderLoop::exposureChanged(QQuickWindow *window) { if (window->isExposed()) { m_windows[window].updatePending = true; - renderWindow(window); + renderWindow(window, true); } } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h index 02dcf4eefa..c724d18298 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderloop_p.h @@ -69,7 +69,7 @@ public: void windowDestroyed(QQuickWindow *window) override; - void renderWindow(QQuickWindow *window); + void renderWindow(QQuickWindow *window, bool isNewExpose = false); void exposureChanged(QQuickWindow *window) override; QImage grab(QQuickWindow *window) override; diff --git a/src/quick/scenegraph/coreapi/qsgmaterial.cpp b/src/quick/scenegraph/coreapi/qsgmaterial.cpp index 8d666d3d0b..07dc87a643 100644 --- a/src/quick/scenegraph/coreapi/qsgmaterial.cpp +++ b/src/quick/scenegraph/coreapi/qsgmaterial.cpp @@ -419,6 +419,10 @@ void QSGMaterialShader::compile() \value DirtyMatrix Used to indicate that the matrix has changed and must be updated. \value DirtyOpacity Used to indicate that the opacity has changed and must be updated. + + \value DirtyCachedMaterialData Used to indicate that the cached material data have changed and must be updated. + + \value DirtyAll Used to indicate that everything needs to be updated. */ diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index 7ef75d4b4c..7ac3914023 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -112,6 +112,7 @@ static void qt_print_node_count() \value DirtyGeometry The geometry of a QSGGeometryNode has changed. \value DirtyMaterial The material of a QSGGeometryNode has changed. \value DirtyOpacity The opacity of a QSGOpacityNode has changed. + \value DirtySubtreeBlocked The subtree has been blocked. \sa QSGNode::markDirty() */ @@ -146,6 +147,7 @@ static void qt_print_node_count() \value TransformNodeType The type of QSGTransformNode \value ClipNodeType The type of QSGClipNode \value OpacityNodeType The type of QSGOpacityNode + \value RenderNodeType The type of QSGRenderNode \sa type() */ diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index 9207fdbc55..bb2671f6c3 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -134,6 +134,7 @@ QSGRenderer::QSGRenderer(QSGRenderContext *context) , m_bindable(0) , m_changed_emitted(false) , m_is_rendering(false) + , m_is_preprocessing(false) { } @@ -287,6 +288,8 @@ void QSGRenderer::nodeChanged(QSGNode *node, QSGNode::DirtyState state) void QSGRenderer::preprocess() { + m_is_preprocessing = true; + QSGRootNode *root = rootNode(); Q_ASSERT(root); @@ -298,6 +301,11 @@ void QSGRenderer::preprocess() for (QSet<QSGNode *>::const_iterator it = items.constBegin(); it != items.constEnd(); ++it) { QSGNode *n = *it; + + // If we are currently preprocessing, check this node hasn't been + // deleted or something. we don't want a use-after-free! + if (m_nodes_dont_preprocess.contains(n)) // skip + continue; if (!nodeUpdater()->isNodeBlocked(n, root)) { n->preprocess(); } @@ -315,8 +323,13 @@ void QSGRenderer::preprocess() updatePassTime = frameTimer.nsecsElapsed(); Q_QUICK_SG_PROFILE_RECORD(QQuickProfiler::SceneGraphRendererFrame, QQuickProfiler::SceneGraphRendererUpdate); + + m_is_preprocessing = false; + m_nodes_dont_preprocess.clear(); } + + void QSGRenderer::addNodesToPreprocess(QSGNode *node) { for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) @@ -329,8 +342,13 @@ void QSGRenderer::removeNodesToPreprocess(QSGNode *node) { for (QSGNode *c = node->firstChild(); c; c = c->nextSibling()) removeNodesToPreprocess(c); - if (node->flags() & QSGNode::UsePreprocess) + if (node->flags() & QSGNode::UsePreprocess) { m_nodes_to_preprocess.remove(node); + + // If preprocessing *now*, mark the node as gone. + if (m_is_preprocessing) + m_nodes_dont_preprocess.insert(node); + } } diff --git a/src/quick/scenegraph/coreapi/qsgrenderer_p.h b/src/quick/scenegraph/coreapi/qsgrenderer_p.h index 4589685765..1ea2775e6f 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgrenderer_p.h @@ -118,11 +118,13 @@ private: QSGNodeUpdater *m_node_updater; QSet<QSGNode *> m_nodes_to_preprocess; + QSet<QSGNode *> m_nodes_dont_preprocess; const QSGBindable *m_bindable; uint m_changed_emitted : 1; uint m_is_rendering : 1; + uint m_is_preprocessing : 1; }; class Q_QUICK_PRIVATE_EXPORT QSGBindable diff --git a/src/quick/scenegraph/qsgadaptationlayer.cpp b/src/quick/scenegraph/qsgadaptationlayer.cpp index 412023564f..f90706affe 100644 --- a/src/quick/scenegraph/qsgadaptationlayer.cpp +++ b/src/quick/scenegraph/qsgadaptationlayer.cpp @@ -40,7 +40,6 @@ #include "qsgadaptationlayer_p.h" #include <qmath.h> -#include <QtQuick/private/qsgdistancefieldutil_p.h> #include <QtQuick/private/qsgdistancefieldglyphnode_p.h> #include <QtQuick/private/qsgcontext_p.h> #include <private/qrawfont_p.h> @@ -57,9 +56,8 @@ static QElapsedTimer qsg_render_timer; QSGDistanceFieldGlyphCache::Texture QSGDistanceFieldGlyphCache::s_emptyTexture; -QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font) - : m_manager(man) - , m_pendingGlyphs(64) +QSGDistanceFieldGlyphCache::QSGDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) + : m_pendingGlyphs(64) { Q_ASSERT(font.isValid()); diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index a8e35b1ac1..ba146b884f 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -73,7 +73,6 @@ QT_BEGIN_NAMESPACE class QSGNode; class QImage; class TextureReference; -class QSGDistanceFieldGlyphCacheManager; class QSGDistanceFieldGlyphNode; class QOpenGLContext; class QSGInternalImageNode; @@ -409,7 +408,7 @@ public: class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCache { public: - QSGDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font); + QSGDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font); virtual ~QSGDistanceFieldGlyphCache(); struct Metrics { @@ -443,8 +442,6 @@ public: bool operator == (const Texture &other) const { return textureId == other.textureId; } }; - const QSGDistanceFieldGlyphCacheManager *manager() const { return m_manager; } - const QRawFont &referenceFont() const { return m_referenceFont; } qreal fontScale(qreal pixelSize) const @@ -514,8 +511,6 @@ protected: inline bool isCoreProfile() const { return m_coreProfile; } private: - QSGDistanceFieldGlyphCacheManager *m_manager; - QRawFont m_referenceFont; int m_glyphCount; diff --git a/src/quick/scenegraph/qsgcontext.cpp b/src/quick/scenegraph/qsgcontext.cpp index d52f69c7a3..ff2379ecb5 100644 --- a/src/quick/scenegraph/qsgcontext.cpp +++ b/src/quick/scenegraph/qsgcontext.cpp @@ -333,7 +333,6 @@ QSGRendererInterface *QSGContext::rendererInterface(QSGRenderContext *renderCont QSGRenderContext::QSGRenderContext(QSGContext *context) : m_sg(context) - , m_distanceFieldCacheManager(0) { } diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 2f5d5790ee..bd10453131 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -78,7 +78,6 @@ class QSGMaterial; class QSGRenderLoop; class QSGLayer; class QQuickTextureFactory; -class QSGDistanceFieldGlyphCacheManager; class QSGContext; class QQuickPaintedItem; class QSGRendererInterface; @@ -194,7 +193,7 @@ protected: QMutex m_mutex; QHash<QQuickTextureFactory *, QSGTexture *> m_textures; QSet<QSGTexture *> m_texturesToDelete; - QSGDistanceFieldGlyphCacheManager *m_distanceFieldCacheManager; + QHash<QRawFont, QSGDistanceFieldGlyphCache*> m_glyphCaches; QSet<QFontEngine *> m_fontEnginesToClean; }; diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp index d31a7025e5..be5fec9dab 100644 --- a/src/quick/scenegraph/qsgdefaultcontext.cpp +++ b/src/quick/scenegraph/qsgdefaultcontext.cpp @@ -39,7 +39,6 @@ #include "qsgdefaultcontext_p.h" -#include <QtQuick/private/qsgdistancefieldutil_p.h> #include <QtQuick/private/qsgdefaultinternalrectanglenode_p.h> #include <QtQuick/private/qsgdefaultinternalimagenode_p.h> #include <QtQuick/private/qsgdefaultpainternode_p.h> diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp index f0a336e229..ba25172d2f 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache.cpp @@ -42,7 +42,6 @@ #include <QtGui/private/qdistancefield_p.h> #include <QtGui/private/qopenglcontext_p.h> #include <QtQml/private/qqmlglobal_p.h> -#include <QtQuick/private/qsgdistancefieldutil_p.h> #include <qopenglfunctions.h> #include <qopenglframebufferobject.h> #include <qmath.h> @@ -60,8 +59,8 @@ DEFINE_BOOL_CONFIG_OPTION(qsgPreferFullSizeGlyphCacheTextures, QSG_PREFER_FULLSI # define QSG_DEFAULT_DISTANCEFIELD_GLYPH_CACHE_PADDING 2 #endif -QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font) - : QSGDistanceFieldGlyphCache(man, c, font) +QSGDefaultDistanceFieldGlyphCache::QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font) + : QSGDistanceFieldGlyphCache(c, font) , m_maxTextureSize(0) , m_maxTextureCount(3) , m_blitProgram(0) diff --git a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h index 57dc4a5d07..fe365495c2 100644 --- a/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h +++ b/src/quick/scenegraph/qsgdefaultdistancefieldglyphcache_p.h @@ -69,7 +69,7 @@ class QOpenGLFunctions_3_2_Core; class Q_QUICK_PRIVATE_EXPORT QSGDefaultDistanceFieldGlyphCache : public QSGDistanceFieldGlyphCache { public: - QSGDefaultDistanceFieldGlyphCache(QSGDistanceFieldGlyphCacheManager *man, QOpenGLContext *c, const QRawFont &font); + QSGDefaultDistanceFieldGlyphCache(QOpenGLContext *c, const QRawFont &font); virtual ~QSGDefaultDistanceFieldGlyphCache(); void requestGlyphs(const QSet<glyph_t> &glyphs) override; diff --git a/src/quick/scenegraph/qsgdefaultglyphnode.cpp b/src/quick/scenegraph/qsgdefaultglyphnode.cpp index b856d99bc1..0d42102f36 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode.cpp @@ -42,11 +42,33 @@ QT_BEGIN_NAMESPACE +QSGDefaultGlyphNode::QSGDefaultGlyphNode() + : m_glyphNodeType(RootGlyphNode) + , m_dirtyGeometry(false) +{ + setFlag(UsePreprocess); +} + +QSGDefaultGlyphNode::~QSGDefaultGlyphNode() +{ + if (m_glyphNodeType == SubGlyphNode) + return; + + qDeleteAll(m_nodesToDelete); + m_nodesToDelete.clear(); +} + void QSGDefaultGlyphNode::setMaterialColor(const QColor &color) { static_cast<QSGTextMaskMaterial *>(m_material)->setColor(color); } +void QSGDefaultGlyphNode::setGlyphs(const QPointF &position, const QGlyphRun &glyphs) +{ + QSGBasicGlyphNode::setGlyphs(position, glyphs); + m_dirtyGeometry = true; +} + void QSGDefaultGlyphNode::update() { QRawFont font = m_glyphs.rawFont(); @@ -84,4 +106,110 @@ void QSGDefaultGlyphNode::update() markDirty(DirtyGeometry); } +void QSGDefaultGlyphNode::preprocess() +{ + qDeleteAll(m_nodesToDelete); + m_nodesToDelete.clear(); + + if (m_dirtyGeometry) + updateGeometry(); +} + +void QSGDefaultGlyphNode::updateGeometry() +{ + // Remove previously created sub glyph nodes + // We assume all the children are sub glyph nodes + QSGNode *subnode = firstChild(); + while (subnode) { + // We can't delete the node now as it might be in the preprocess list + // It will be deleted in the next preprocess + m_nodesToDelete.append(subnode); + subnode = subnode->nextSibling(); + } + removeAllChildNodes(); + + GlyphInfo glyphInfo; + + const QVector<quint32> indexes = m_glyphs.glyphIndexes(); + const QVector<QPointF> positions = m_glyphs.positions(); + + const int maxGlyphs = (USHRT_MAX + 1) / 4; // 16384 + const int maxVertices = maxGlyphs * 4; // 65536 + const int maxIndexes = maxGlyphs * 6; // 98304 + + for (int i = 0; i < indexes.size(); ++i) { + const int glyphIndex = indexes.at(i); + const QPointF position = positions.at(i); + + // As we use UNSIGNED_SHORT indexing in the geometry, we overload the + // "glyphsInOtherNodes" concept as overflow for if there are more than + // 65536 (16384 * 4) vertices to render which would otherwise exceed + // the maximum index size. This will cause sub-nodes to be recursively + // created to handle any number of glyphs. + if (i >= maxGlyphs) { + glyphInfo.indexes.append(glyphIndex); + glyphInfo.positions.append(position); + continue; + } + } + + if (!glyphInfo.indexes.isEmpty()) { + QGlyphRun subNodeGlyphRun(m_glyphs); + subNodeGlyphRun.setGlyphIndexes(glyphInfo.indexes); + subNodeGlyphRun.setPositions(glyphInfo.positions); + + QSGDefaultGlyphNode *subNode = new QSGDefaultGlyphNode(); + subNode->setGlyphNodeType(SubGlyphNode); + subNode->setColor(m_color); + subNode->setStyle(m_style); + subNode->setStyleColor(m_styleColor); + subNode->setGlyphs(m_position, subNodeGlyphRun); + subNode->update(); + subNode->updateGeometry(); // we have to explicitly call this now as preprocess won't be called before it's rendered + appendChildNode(subNode); + + QSGGeometry *g = geometry(); + + QSGGeometry::TexturedPoint2D *vertexData = g->vertexDataAsTexturedPoint2D(); + quint16 *indexData = g->indexDataAsUShort(); + + QVector<QSGGeometry::TexturedPoint2D> tempVertexData(maxVertices); + QVector<quint16> tempIndexData(maxIndexes); + + for (int i = 0; i < maxGlyphs; i++) { + tempVertexData[i * 4 + 0] = vertexData[i * 4 + 0]; + tempVertexData[i * 4 + 1] = vertexData[i * 4 + 1]; + tempVertexData[i * 4 + 2] = vertexData[i * 4 + 2]; + tempVertexData[i * 4 + 3] = vertexData[i * 4 + 3]; + + tempIndexData[i * 6 + 0] = indexData[i * 6 + 0]; + tempIndexData[i * 6 + 1] = indexData[i * 6 + 1]; + tempIndexData[i * 6 + 2] = indexData[i * 6 + 2]; + tempIndexData[i * 6 + 3] = indexData[i * 6 + 3]; + tempIndexData[i * 6 + 4] = indexData[i * 6 + 4]; + tempIndexData[i * 6 + 5] = indexData[i * 6 + 5]; + } + + g->allocate(maxVertices, maxIndexes); + vertexData = g->vertexDataAsTexturedPoint2D(); + indexData = g->indexDataAsUShort(); + + for (int i = 0; i < maxGlyphs; i++) { + vertexData[i * 4 + 0] = tempVertexData[i * 4 + 0]; + vertexData[i * 4 + 1] = tempVertexData[i * 4 + 1]; + vertexData[i * 4 + 2] = tempVertexData[i * 4 + 2]; + vertexData[i * 4 + 3] = tempVertexData[i * 4 + 3]; + + indexData[i * 6 + 0] = tempIndexData[i * 6 + 0]; + indexData[i * 6 + 1] = tempIndexData[i * 6 + 1]; + indexData[i * 6 + 2] = tempIndexData[i * 6 + 2]; + indexData[i * 6 + 3] = tempIndexData[i * 6 + 3]; + indexData[i * 6 + 4] = tempIndexData[i * 6 + 4]; + indexData[i * 6 + 5] = tempIndexData[i * 6 + 5]; + } + } + + m_dirtyGeometry = false; +} + QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.h b/src/quick/scenegraph/qsgdefaultglyphnode_p.h index 0eb7a4e4bd..37a89c70b9 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.h +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.h @@ -59,8 +59,31 @@ QT_BEGIN_NAMESPACE class QSGDefaultGlyphNode : public QSGBasicGlyphNode { public: + QSGDefaultGlyphNode(); + ~QSGDefaultGlyphNode(); void setMaterialColor(const QColor &color) override; + void setGlyphs(const QPointF &position, const QGlyphRun &glyphs) override; void update() override; + void preprocess() override; + void updateGeometry(); + +private: + enum DefaultGlyphNodeType { + RootGlyphNode, + SubGlyphNode + }; + + void setGlyphNodeType(DefaultGlyphNodeType type) { m_glyphNodeType = type; } + + DefaultGlyphNodeType m_glyphNodeType; + QLinkedList<QSGNode *> m_nodesToDelete; + + struct GlyphInfo { + QVector<quint32> indexes; + QVector<QPointF> positions; + }; + + uint m_dirtyGeometry: 1; }; QT_END_NAMESPACE diff --git a/src/quick/scenegraph/qsgdefaultrendercontext.cpp b/src/quick/scenegraph/qsgdefaultrendercontext.cpp index 2c5b4ff5c8..7542068a53 100644 --- a/src/quick/scenegraph/qsgdefaultrendercontext.cpp +++ b/src/quick/scenegraph/qsgdefaultrendercontext.cpp @@ -45,7 +45,6 @@ #include <QtQuick/private/qsgrenderer_p.h> #include <QtQuick/private/qsgatlastexture_p.h> #include <QtQuick/private/qsgdefaultdistancefieldglyphcache_p.h> -#include <QtQuick/private/qsgdistancefieldutil_p.h> QT_BEGIN_NAMESPACE @@ -159,8 +158,8 @@ void QSGDefaultRenderContext::invalidate() delete m_depthStencilManager; m_depthStencilManager = 0; - delete m_distanceFieldCacheManager; - m_distanceFieldCacheManager = 0; + qDeleteAll(m_glyphCaches); + m_glyphCaches.clear(); if (m_gl->property(QSG_RENDERCONTEXT_PROPERTY) == QVariant::fromValue(this)) m_gl->setProperty(QSG_RENDERCONTEXT_PROPERTY, QVariant()); @@ -294,13 +293,10 @@ QT_END_NAMESPACE QSGDistanceFieldGlyphCache *QSGDefaultRenderContext::distanceFieldGlyphCache(const QRawFont &font) { - if (!m_distanceFieldCacheManager) - m_distanceFieldCacheManager = new QSGDistanceFieldGlyphCacheManager; - - QSGDistanceFieldGlyphCache *cache = m_distanceFieldCacheManager->cache(font); + QSGDistanceFieldGlyphCache *cache = m_glyphCaches.value(font, 0); if (!cache) { - cache = new QSGDefaultDistanceFieldGlyphCache(m_distanceFieldCacheManager, openglContext(), font); - m_distanceFieldCacheManager->insertCache(font, cache); + cache = new QSGDefaultDistanceFieldGlyphCache(openglContext(), font); + m_glyphCaches.insert(font, cache); } return cache; diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp index 456a197ba1..32eda2d142 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode.cpp @@ -39,7 +39,6 @@ #include "qsgdistancefieldglyphnode_p.h" #include "qsgdistancefieldglyphnode_p_p.h" -#include <QtQuick/private/qsgdistancefieldutil_p.h> #include <QtQuick/private/qsgcontext_p.h> QT_BEGIN_NAMESPACE @@ -76,9 +75,6 @@ QSGDistanceFieldGlyphNode::~QSGDistanceFieldGlyphNode() m_glyph_cache->unregisterGlyphNode(this); m_glyph_cache->unregisterOwnerElement(ownerElement()); } - - while (m_nodesToDelete.count()) - delete m_nodesToDelete.takeLast(); } void QSGDistanceFieldGlyphNode::setColor(const QColor &color) @@ -158,9 +154,6 @@ void QSGDistanceFieldGlyphNode::preprocess() { Q_ASSERT(m_glyph_cache); - while (m_nodesToDelete.count()) - delete m_nodesToDelete.takeLast(); - m_glyph_cache->processPendingGlyphs(); m_glyph_cache->update(); @@ -188,13 +181,12 @@ void QSGDistanceFieldGlyphNode::updateGeometry() // Remove previously created sub glyph nodes // We assume all the children are sub glyph nodes QSGNode *subnode = firstChild(); + QSGNode *nextNode = 0; while (subnode) { - // We can't delete the node now as it might be in the preprocess list - // It will be deleted in the next preprocess - m_nodesToDelete.append(subnode); - subnode = subnode->nextSibling(); + nextNode = subnode->nextSibling(); + delete subnode; + subnode = nextNode; } - removeAllChildNodes(); QSGGeometry *g = geometry(); diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp index fc66f2f2cc..a67c659c99 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp @@ -38,7 +38,6 @@ ****************************************************************************/ #include "qsgdistancefieldglyphnode_p_p.h" -#include <QtQuick/private/qsgdistancefieldutil_p.h> #include <QtQuick/private/qsgtexture_p.h> #include <QtGui/qopenglfunctions.h> #include <QtGui/qsurface.h> @@ -58,7 +57,7 @@ public: protected: void initialize() override; - void updateAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc); + void updateAlphaRange(); void updateColor(const QVector4D &c); void updateTextureScale(const QVector2D &ts); @@ -98,7 +97,31 @@ QSGDistanceFieldTextMaterialShader::QSGDistanceFieldTextMaterialShader() setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/distancefieldtext.frag")); } -void QSGDistanceFieldTextMaterialShader::updateAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc) +static float qt_sg_envFloat(const char *name, float defaultValue) +{ + if (Q_LIKELY(!qEnvironmentVariableIsSet(name))) + return defaultValue; + bool ok = false; + const float value = qgetenv(name).toFloat(&ok); + return ok ? value : defaultValue; +} + +static float thresholdFunc(float glyphScale) +{ + static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f); + static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f); + static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f); + static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f); + return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev); +} + +static float spreadFunc(float glyphScale) +{ + static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f); + return range / glyphScale; +} + +void QSGDistanceFieldTextMaterialShader::updateAlphaRange() { float combinedScale = m_fontScale * m_matrixScale; float base = thresholdFunc(combinedScale); @@ -169,8 +192,7 @@ void QSGDistanceFieldTextMaterialShader::updateState(const RenderState &state, Q updateRange = true; } if (updateRange) { - updateAlphaRange(material->glyphCache()->manager()->thresholdFunc(), - material->glyphCache()->manager()->antialiasingSpreadFunc()); + updateAlphaRange(); } Q_ASSERT(material->glyphCache()); @@ -334,7 +356,7 @@ public: protected: void initialize() override; - void updateOutlineAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc, int dfRadius); + void updateOutlineAlphaRange(int dfRadius); int m_outlineAlphaMax0_id; int m_outlineAlphaMax1_id; @@ -355,9 +377,7 @@ void DistanceFieldOutlineTextMaterialShader::initialize() m_outlineAlphaMax1_id = program()->uniformLocation("outlineAlphaMax1"); } -void DistanceFieldOutlineTextMaterialShader::updateOutlineAlphaRange(ThresholdFunc thresholdFunc, - AntialiasingSpreadFunc spreadFunc, - int dfRadius) +void DistanceFieldOutlineTextMaterialShader::updateOutlineAlphaRange(int dfRadius) { float combinedScale = m_fontScale * m_matrixScale; float base = thresholdFunc(combinedScale); @@ -381,9 +401,7 @@ void DistanceFieldOutlineTextMaterialShader::updateState(const RenderState &stat if (oldMaterial == 0 || material->fontScale() != oldMaterial->fontScale() || state.isMatrixDirty()) - updateOutlineAlphaRange(material->glyphCache()->manager()->thresholdFunc(), - material->glyphCache()->manager()->antialiasingSpreadFunc(), - material->glyphCache()->distanceFieldRadius()); + updateOutlineAlphaRange(material->glyphCache()->distanceFieldRadius()); } diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h index c0c6bda718..7008f20925 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.h @@ -59,7 +59,6 @@ QT_BEGIN_NAMESPACE class QSGRenderContext; -class QSGDistanceFieldGlyphCacheManager; class QSGDistanceFieldTextMaterial; class QSGDistanceFieldGlyphNode: public QSGGlyphNode, public QSGDistanceFieldGlyphConsumer { @@ -107,7 +106,6 @@ private: AntialiasingMode m_antialiasingMode; QRectF m_boundingRect; const QSGDistanceFieldGlyphCache::Texture *m_texture; - QLinkedList<QSGNode *> m_nodesToDelete; struct GlyphInfo { QVector<quint32> indexes; diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index bb581c5e36..293a706c2e 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -423,6 +423,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (data.grabOnly) { bool alpha = window->format().alphaBufferSize() > 0 && window->color().alpha() != 255; grabContent = qt_gl_read_framebuffer(window->size() * window->effectiveDevicePixelRatio(), alpha, alpha); + grabContent.setDevicePixelRatio(window->effectiveDevicePixelRatio()); data.grabOnly = false; } diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 17a2c62a4e..560fddd580 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -432,6 +432,7 @@ bool QSGRenderThread::event(QEvent *e) qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- grabbing result"; bool alpha = ce->window->format().alphaBufferSize() > 0 && ce->window->color().alpha() != 255; *ce->image = qt_gl_read_framebuffer(windowSize * ce->window->effectiveDevicePixelRatio(), alpha, alpha); + ce->image->setDevicePixelRatio(ce->window->effectiveDevicePixelRatio()); } qCDebug(QSG_LOG_RENDERLOOP) << QSG_RT_PAD << "- waking gui to handle result"; waitCondition.wakeOne(); diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index e944ddbc4f..eff6763a16 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -325,6 +325,7 @@ QImage QSGWindowsRenderLoop::grab(QQuickWindow *window) bool alpha = window->format().alphaBufferSize() > 0 && window->color().alpha() != 255; QImage image = qt_gl_read_framebuffer(window->size() * window->effectiveDevicePixelRatio(), alpha, alpha); + image.setDevicePixelRatio(window->effectiveDevicePixelRatio()); return image; } diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index 38c3b8dd85..c6db3df158 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -45,7 +45,6 @@ HEADERS += \ $$PWD/util/qsgtexture.h \ $$PWD/util/qsgtexture_p.h \ $$PWD/util/qsgtextureprovider.h \ - $$PWD/util/qsgdistancefieldutil_p.h \ $$PWD/util/qsgflatcolormaterial.h \ $$PWD/util/qsgsimplematerial.h \ $$PWD/util/qsgtexturematerial.h \ @@ -62,7 +61,6 @@ SOURCES += \ $$PWD/util/qsgsimpletexturenode.cpp \ $$PWD/util/qsgtexture.cpp \ $$PWD/util/qsgtextureprovider.cpp \ - $$PWD/util/qsgdistancefieldutil.cpp \ $$PWD/util/qsgflatcolormaterial.cpp \ $$PWD/util/qsgsimplematerial.cpp \ $$PWD/util/qsgtexturematerial.cpp \ diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp b/src/quick/scenegraph/util/qsgdistancefieldutil.cpp deleted file mode 100644 index 9ca9cdb107..0000000000 --- a/src/quick/scenegraph/util/qsgdistancefieldutil.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/**************************************************************************** -** -** 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 "qsgdistancefieldutil_p.h" - -#include <private/qsgadaptationlayer_p.h> -#if QT_CONFIG(opengl) -# include <QtGui/private/qopenglengineshadersource_p.h> -#endif -#include <QtQuick/private/qsgcontext_p.h> - -QT_BEGIN_NAMESPACE - -static float qt_sg_envFloat(const char *name, float defaultValue) -{ - if (Q_LIKELY(!qEnvironmentVariableIsSet(name))) - return defaultValue; - bool ok = false; - const float value = qgetenv(name).toFloat(&ok); - return ok ? value : defaultValue; -} - -static float defaultThresholdFunc(float glyphScale) -{ - static const float base = qt_sg_envFloat("QT_DF_BASE", 0.5f); - static const float baseDev = qt_sg_envFloat("QT_DF_BASEDEVIATION", 0.065f); - static const float devScaleMin = qt_sg_envFloat("QT_DF_SCALEFORMAXDEV", 0.15f); - static const float devScaleMax = qt_sg_envFloat("QT_DF_SCALEFORNODEV", 0.3f); - return base - ((qBound(devScaleMin, glyphScale, devScaleMax) - devScaleMin) / (devScaleMax - devScaleMin) * -baseDev + baseDev); -} - -static float defaultAntialiasingSpreadFunc(float glyphScale) -{ - static const float range = qt_sg_envFloat("QT_DF_RANGE", 0.06f); - return range / glyphScale; -} - -QSGDistanceFieldGlyphCacheManager::QSGDistanceFieldGlyphCacheManager() - : m_threshold_func(defaultThresholdFunc) - , m_antialiasingSpread_func(defaultAntialiasingSpreadFunc) -{ -} - -QSGDistanceFieldGlyphCacheManager::~QSGDistanceFieldGlyphCacheManager() -{ - qDeleteAll(m_caches); -} - -QSGDistanceFieldGlyphCache *QSGDistanceFieldGlyphCacheManager::cache(const QRawFont &font) -{ - return m_caches.value(font, 0); -} - -void QSGDistanceFieldGlyphCacheManager::insertCache(const QRawFont &font, QSGDistanceFieldGlyphCache *cache) -{ - m_caches.insert(font, cache); -} - -QT_END_NAMESPACE diff --git a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h b/src/quick/scenegraph/util/qsgdistancefieldutil_p.h deleted file mode 100644 index ad366cb4d4..0000000000 --- a/src/quick/scenegraph/util/qsgdistancefieldutil_p.h +++ /dev/null @@ -1,91 +0,0 @@ -/**************************************************************************** -** -** 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 QSGDISTANCEFIELDUTIL_H -#define QSGDISTANCEFIELDUTIL_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 <qrawfont.h> -#include <private/qfontengine_p.h> -#include <private/qsgadaptationlayer_p.h> - -QT_BEGIN_NAMESPACE - -typedef float (*ThresholdFunc)(float glyphScale); -typedef float (*AntialiasingSpreadFunc)(float glyphScale); - -class QOpenGLShaderProgram; -class QSGDistanceFieldGlyphCache; -class QSGContext; - -class Q_QUICK_PRIVATE_EXPORT QSGDistanceFieldGlyphCacheManager -{ -public: - QSGDistanceFieldGlyphCacheManager(); - ~QSGDistanceFieldGlyphCacheManager(); - - QSGDistanceFieldGlyphCache *cache(const QRawFont &font); - void insertCache(const QRawFont &font, QSGDistanceFieldGlyphCache *cache); - - ThresholdFunc thresholdFunc() const { return m_threshold_func; } - void setThresholdFunc(ThresholdFunc func) { m_threshold_func = func; } - - AntialiasingSpreadFunc antialiasingSpreadFunc() const { return m_antialiasingSpread_func; } - void setAntialiasingSpreadFunc(AntialiasingSpreadFunc func) { m_antialiasingSpread_func = func; } - -private: - QHash<QRawFont, QSGDistanceFieldGlyphCache *> m_caches; - - ThresholdFunc m_threshold_func; - AntialiasingSpreadFunc m_antialiasingSpread_func; -}; - -QT_END_NAMESPACE - -#endif // QSGDISTANCEFIELDUTIL_H diff --git a/src/quick/scenegraph/util/qsgengine.cpp b/src/quick/scenegraph/util/qsgengine.cpp index 09e4cdf5a7..259e45c978 100644 --- a/src/quick/scenegraph/util/qsgengine.cpp +++ b/src/quick/scenegraph/util/qsgengine.cpp @@ -84,6 +84,8 @@ QT_BEGIN_NAMESPACE will delete the GL texture when the texture object is deleted. \value TextureCanUseAtlas The image can be uploaded into a texture atlas. + + \value TextureIsOpaque The texture object is opaque. */ QSGEnginePrivate::QSGEnginePrivate() diff --git a/src/quick/scenegraph/util/qsgsimplematerial.cpp b/src/quick/scenegraph/util/qsgsimplematerial.cpp index 7214a255df..f29c58ad9e 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.cpp +++ b/src/quick/scenegraph/util/qsgsimplematerial.cpp @@ -194,19 +194,15 @@ /*! \fn const char *QSGSimpleMaterialShader::uniformMatrixName() const - Reimplement this function to give a different name to the uniform for - item transformation. The default value is \c qt_Matrix. - + Returns the name for the transform matrix uniform of this item. + The default value is \c qt_Matrix. */ /*! \fn const char *QSGSimpleMaterialShader::uniformOpacityName() const - Reimplement this function to give a different name to the uniform for - item opacity. The default value is \c qt_Opacity. - - If the shader program does not implement the item opacity, the - implemented function should return a null pointer. + Returns the name for the opacity uniform of this item. + The default value is \c qt_Opacity. */ /*! diff --git a/src/quick/scenegraph/util/qsgsimplematerial.h b/src/quick/scenegraph/util/qsgsimplematerial.h index 0e7219d7bd..b5b8815b4a 100644 --- a/src/quick/scenegraph/util/qsgsimplematerial.h +++ b/src/quick/scenegraph/util/qsgsimplematerial.h @@ -71,6 +71,7 @@ public: resolveUniforms(); } + // ### Qt 6: make both virtual and fix docs const char *uniformMatrixName() const { return "qt_Matrix"; } const char *uniformOpacityName() const { return "qt_Opacity"; } diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index bc59c49162..591b679ec4 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -256,6 +256,8 @@ 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 used, causing values above 1 and below 0 to repeat. diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index 4aacb09c97..89007cff1f 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -345,12 +345,13 @@ void QQuickTransformAnimatorJob::postSync() } QQuickItemPrivate *d = QQuickItemPrivate::get(m_target); +#if QT_CONFIG(quick_shadereffect) if (d->extra.isAllocated() && d->extra->layer && d->extra->layer->enabled()) { d = QQuickItemPrivate::get(d->extra->layer->m_effectSource); } - +#endif m_helper->node = d->itemNode(); } diff --git a/src/quick/util/qquickimageprovider.cpp b/src/quick/util/qquickimageprovider.cpp index a026abe762..c4a98c69f9 100644 --- a/src/quick/util/qquickimageprovider.cpp +++ b/src/quick/util/qquickimageprovider.cpp @@ -183,7 +183,12 @@ QString QQuickImageResponse::errorString() const It may be reimplemented to cancel a request in the provider side, however, it is not mandatory. - A cancelled QQuickImageResponse still needs to emit finished(). + A cancelled QQuickImageResponse still needs to emit finished() so that the + engine may clean up the QQuickImageResponse. + + \note finished() should not be emitted until the response is complete, + regardless of whether or not cancel() was called. If it is called prematurely, + the engine may destroy the response while it is still active, leading to a crash. */ void QQuickImageResponse::cancel() { @@ -192,7 +197,12 @@ void QQuickImageResponse::cancel() /*! \fn void QQuickImageResponse::finished() - Signals that the job execution has finished (be it successfully, because an error happened or because it was cancelled). + Signals that the job execution has finished (be it successfully, because an + error happened or because it was cancelled). + + \note Emission of this signal must be the final action the response performs: + once the signal is received, the response will subsequently be destroyed by + the engine. */ /*! @@ -603,7 +613,6 @@ void QQuickImageProviderOptions::setPreserveAspectRatioFit(bool preserveAspectRa d->preserveAspectRatioFit = preserveAspectRatioFit; } - QQuickImageProviderWithOptions::QQuickImageProviderWithOptions(ImageType type, Flags flags) : QQuickAsyncImageProvider() { @@ -660,5 +669,49 @@ QQuickImageResponse *QQuickImageProviderWithOptions::requestImageResponse(const return requestImageResponse(id, requestedSize); } +/*! + Returns the recommended scaled image size for loading and storage. This is + calculated according to the native pixel size of the image \a originalSize, + the requested sourceSize \a requestedSize, the image file format \a format, + and \a options. If the calculation otherwise concludes that scaled loading + is not recommended, an invalid size is returned. +*/ +QSize QQuickImageProviderWithOptions::loadSize(const QSize &originalSize, const QSize &requestedSize, const QByteArray &format, const QQuickImageProviderOptions &options) +{ + QSize res; + if ((requestedSize.width() <= 0 && requestedSize.height() <= 0) || originalSize.isEmpty()) + return res; + + const bool preserveAspectCropOrFit = options.preserveAspectRatioCrop() || options.preserveAspectRatioFit(); + const bool force_scale = (format == "svg" || format == "svgz"); + + qreal ratio = 0.0; + if (requestedSize.width() && (preserveAspectCropOrFit || force_scale || requestedSize.width() < originalSize.width())) { + ratio = qreal(requestedSize.width()) / originalSize.width(); + } + if (requestedSize.height() && (preserveAspectCropOrFit || force_scale || requestedSize.height() < originalSize.height())) { + qreal hr = qreal(requestedSize.height()) / originalSize.height(); + if (ratio == 0.0) + ratio = hr; + else if (!preserveAspectCropOrFit && (hr < ratio)) + ratio = hr; + else if (preserveAspectCropOrFit && (hr > ratio)) + ratio = hr; + } + if (ratio > 0.0) { + res.setHeight(qRound(originalSize.height() * ratio)); + res.setWidth(qRound(originalSize.width() * ratio)); + } + return res; +} + +QQuickImageProviderWithOptions *QQuickImageProviderWithOptions::checkedCast(QQuickImageProvider *provider) +{ + if (provider && provider->d && provider->d->isProviderWithOptions) + return static_cast<QQuickImageProviderWithOptions *>(provider); + + return nullptr; +} + QT_END_NAMESPACE diff --git a/src/quick/util/qquickimageprovider.h b/src/quick/util/qquickimageprovider.h index c77ff95f32..681de4b6c2 100644 --- a/src/quick/util/qquickimageprovider.h +++ b/src/quick/util/qquickimageprovider.h @@ -88,7 +88,6 @@ Q_SIGNALS: class Q_QUICK_EXPORT QQuickImageProvider : public QQmlImageProviderBase { friend class QQuickImageProviderWithOptions; // ### Qt 6 Remove - friend class QQuickPixmapReader; // ### Qt 6 Remove public: QQuickImageProvider(ImageType type, Flags flags = Flags()); virtual ~QQuickImageProvider(); diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 395b89c784..7d88935402 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -141,6 +141,7 @@ public: QUrl url; bool loading; + QQuickImageProviderOptions providerOptions; int redirectCount; class Event : public QEvent { @@ -204,7 +205,7 @@ protected: private: friend class QQuickPixmapReaderThreadObject; void processJobs(); - void processJob(QQuickPixmapReply *, const QUrl &, const QString &, const QQuickImageProviderOptions &, QQuickImageProvider::ImageType, QQuickImageProvider *); + void processJob(QQuickPixmapReply *, const QUrl &, const QString &, QQuickImageProvider::ImageType, QQuickImageProvider *); #if QT_CONFIG(qml_network) void networkRequestDone(QNetworkReply *); #endif @@ -386,37 +387,15 @@ static bool readImage(const QUrl& url, QIODevice *dev, QImage *image, QString *e const QSize &requestSize, const QQuickImageProviderOptions &providerOptions, QQuickImageProviderOptions::AutoTransform *appliedTransform = nullptr) { - const bool preserveAspectCropOrFit = providerOptions.preserveAspectRatioCrop() || providerOptions.preserveAspectRatioFit(); - QImageReader imgio(dev); if (providerOptions.autoTransform() != QQuickImageProviderOptions::UsePluginDefaultTransform) imgio.setAutoTransform(providerOptions.autoTransform() == QQuickImageProviderOptions::ApplyTransform); else if (appliedTransform) *appliedTransform = imgio.autoTransform() ? QQuickImageProviderOptions::ApplyTransform : QQuickImageProviderOptions::DoNotApplyTransform; - const bool force_scale = imgio.format() == "svg" || imgio.format() == "svgz"; - - if (requestSize.width() > 0 || requestSize.height() > 0) { - QSize s = imgio.size(); - qreal ratio = 0.0; - if (requestSize.width() && (preserveAspectCropOrFit || force_scale || requestSize.width() < s.width())) { - ratio = qreal(requestSize.width())/s.width(); - } - if (requestSize.height() && (preserveAspectCropOrFit || force_scale || requestSize.height() < s.height())) { - qreal hr = qreal(requestSize.height())/s.height(); - if (ratio == 0.0) - ratio = hr; - else if (!preserveAspectCropOrFit && (hr < ratio)) - ratio = hr; - else if (preserveAspectCropOrFit && (hr > ratio)) - ratio = hr; - } - if (ratio > 0.0) { - s.setHeight(qRound(s.height() * ratio)); - s.setWidth(qRound(s.width() * ratio)); - imgio.setScaledSize(s); - } - } + QSize scSize = QQuickImageProviderWithOptions::loadSize(imgio.size(), requestSize, imgio.format(), providerOptions); + if (scSize.isValid()) + imgio.setScaledSize(scSize); if (impsize) *impsize = imgio.size(); @@ -664,7 +643,7 @@ void QQuickPixmapReader::processJobs() PIXMAP_PROFILE(pixmapStateChanged<QQuickProfiler::PixmapLoadingStarted>(url)); locker.unlock(); - processJob(job, url, localFile, job->data->providerOptions, imageType, provider); + processJob(job, url, localFile, imageType, provider); locker.relock(); } } @@ -676,7 +655,6 @@ void QQuickPixmapReader::processJobs() } void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &url, const QString &localFile, - const QQuickImageProviderOptions &providerOptions, QQuickImageProvider::ImageType imageType, QQuickImageProvider *provider) { // fetch @@ -693,8 +671,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u return; } - QQuickImageProviderWithOptions *providerV2 = provider->d->isProviderWithOptions ? static_cast<QQuickImageProviderWithOptions *>(provider) - : nullptr; + QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider); switch (imageType) { case QQuickImageProvider::Invalid: @@ -707,7 +684,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u { QImage image; if (providerV2) { - image = providerV2->requestImage(imageId(url), &readSize, runningJob->requestSize, providerOptions); + image = providerV2->requestImage(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions); } else { image = provider->requestImage(imageId(url), &readSize, runningJob->requestSize); } @@ -728,7 +705,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u { QPixmap pixmap; if (providerV2) { - pixmap = providerV2->requestPixmap(imageId(url), &readSize, runningJob->requestSize, providerOptions); + pixmap = providerV2->requestPixmap(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions); } else { pixmap = provider->requestPixmap(imageId(url), &readSize, runningJob->requestSize); } @@ -749,7 +726,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u { QQuickTextureFactory *t; if (providerV2) { - t = providerV2->requestTexture(imageId(url), &readSize, runningJob->requestSize, providerOptions); + t = providerV2->requestTexture(imageId(url), &readSize, runningJob->requestSize, runningJob->providerOptions); } else { t = provider->requestTexture(imageId(url), &readSize, runningJob->requestSize); } @@ -772,7 +749,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u { QQuickImageResponse *response; if (providerV2) { - response = providerV2->requestImageResponse(imageId(url), runningJob->requestSize, providerOptions); + response = providerV2->requestImageResponse(imageId(url), runningJob->requestSize, runningJob->providerOptions); } else { QQuickAsyncImageProvider *asyncProvider = static_cast<QQuickAsyncImageProvider*>(provider); response = asyncProvider->requestImageResponse(imageId(url), runningJob->requestSize); @@ -794,7 +771,7 @@ void QQuickPixmapReader::processJob(QQuickPixmapReply *runningJob, const QUrl &u QFile f(localFile); QSize readSize; if (f.open(QIODevice::ReadOnly)) { - if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize, providerOptions)) + if (!readImage(url, &f, &image, &errorStr, &readSize, runningJob->requestSize, runningJob->providerOptions)) errorCode = QQuickPixmapReply::Loading; } else { errorStr = QQuickPixmap::tr("Cannot open: %1").arg(url.toString()); @@ -1075,7 +1052,7 @@ void QQuickPixmap::purgeCache() } QQuickPixmapReply::QQuickPixmapReply(QQuickPixmapData *d) -: data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), redirectCount(0) +: data(d), engineForReader(0), requestSize(d->requestSize), url(d->url), loading(false), providerOptions(d->providerOptions), redirectCount(0) { if (finishedIndex == -1) { finishedIndex = QMetaMethod::fromSignal(&QQuickPixmapReply::finished).methodIndex(); @@ -1196,6 +1173,7 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QQuickImageProvider::ImageType imageType = QQuickImageProvider::Invalid; QQuickImageProvider *provider = static_cast<QQuickImageProvider *>(engine->imageProvider(imageProviderId(url))); + QQuickImageProviderWithOptions *providerV2 = QQuickImageProviderWithOptions::checkedCast(provider); if (provider) imageType = provider->imageType(); @@ -1205,7 +1183,8 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q QQuickPixmap::tr("Invalid image provider: %1").arg(url.toString())); case QQuickImageProvider::Texture: { - QQuickTextureFactory *texture = provider->requestTexture(imageId(url), &readSize, requestSize); + QQuickTextureFactory *texture = providerV2 ? providerV2->requestTexture(imageId(url), &readSize, requestSize, providerOptions) + : provider->requestTexture(imageId(url), &readSize, requestSize); if (texture) { *ok = true; return new QQuickPixmapData(declarativePixmap, url, texture, readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform); @@ -1215,7 +1194,8 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q case QQuickImageProvider::Image: { - QImage image = provider->requestImage(imageId(url), &readSize, requestSize); + QImage image = providerV2 ? providerV2->requestImage(imageId(url), &readSize, requestSize, providerOptions) + : provider->requestImage(imageId(url), &readSize, requestSize); if (!image.isNull()) { *ok = true; return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(image), readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform); @@ -1224,7 +1204,8 @@ static QQuickPixmapData* createPixmapDataSync(QQuickPixmap *declarativePixmap, Q } case QQuickImageProvider::Pixmap: { - QPixmap pixmap = provider->requestPixmap(imageId(url), &readSize, requestSize); + QPixmap pixmap = providerV2 ? providerV2->requestPixmap(imageId(url), &readSize, requestSize, providerOptions) + : provider->requestPixmap(imageId(url), &readSize, requestSize); if (!pixmap.isNull()) { *ok = true; return new QQuickPixmapData(declarativePixmap, url, QQuickTextureFactory::textureFactoryForImage(pixmap.toImage()), readSize, requestSize, providerOptions, QQuickImageProviderOptions::UsePluginDefaultTransform); diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h index 93d5a1cf56..91fb1ed3bb 100644 --- a/src/quick/util/qquickpixmapcache_p.h +++ b/src/quick/util/qquickpixmapcache_p.h @@ -204,6 +204,9 @@ public: virtual QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize, const QQuickImageProviderOptions &options); virtual QQuickTextureFactory *requestTexture(const QString &id, QSize *size, const QSize &requestedSize, const QQuickImageProviderOptions &options); virtual QQuickImageResponse *requestImageResponse(const QString &id, const QSize &requestedSize, const QQuickImageProviderOptions &options); + + static QSize loadSize(const QSize &originalSize, const QSize &requestedSize, const QByteArray &format, const QQuickImageProviderOptions &options); + static QQuickImageProviderWithOptions *checkedCast(QQuickImageProvider *provider); }; QT_END_NAMESPACE diff --git a/src/quickwidgets/qquickwidget.cpp b/src/quickwidgets/qquickwidget.cpp index 4ed41a0c6c..a2ea0f7af2 100644 --- a/src/quickwidgets/qquickwidget.cpp +++ b/src/quickwidgets/qquickwidget.cpp @@ -76,10 +76,6 @@ QT_BEGIN_NAMESPACE -#if QT_CONFIG(opengl) -extern Q_GUI_EXPORT QImage qt_gl_read_framebuffer(const QSize &size, bool alpha_format, bool include_alpha); -#endif - class QQuickWidgetRenderControl : public QQuickRenderControl { public: @@ -157,6 +153,16 @@ void QQuickWidgetPrivate::invalidateRenderControl() #endif renderControl->invalidate(); + + // Many things can happen inside the above invalidate() call, including a + // change of current context. Restore if needed since some code will rely + // on the fact that this function makes and leaves the context current. +#if QT_CONFIG(opengl) + if (!useSoftwareRenderer && context) { + if (QOpenGLContext::currentContext() != context) + context->makeCurrent(offscreenSurface); + } +#endif } void QQuickWidgetPrivate::handleWindowChange() @@ -353,10 +359,9 @@ QImage QQuickWidgetPrivate::grabFramebuffer() return renderControl->grab(); } -QObject *QQuickWidgetPrivate::focusObject() -{ - return offscreenWindow ? offscreenWindow->focusObject() : 0; -} +// Intentionally not overriding the QQuickWindow's focusObject. +// Key events should go to our key event handlers, and then to the +// QQuickWindow, not any in-scene item. /*! \module QtQuickWidgets @@ -1140,7 +1145,7 @@ QSize QQuickWidget::initialSize() const /*! Returns the view's root \l {QQuickItem} {item}. Can be null - when setContents/setSource has not been called, if they were called with + when setSource() has not been called, if it was called with broken QtQuick code or while the QtQuick contents are being created. */ QQuickItem *QQuickWidget::rootObject() const diff --git a/src/quickwidgets/qquickwidget_p.h b/src/quickwidgets/qquickwidget_p.h index 559321cd51..6892e6e0b4 100644 --- a/src/quickwidgets/qquickwidget_p.h +++ b/src/quickwidgets/qquickwidget_p.h @@ -99,8 +99,6 @@ public: void destroyContext(); void handleContextCreationFailure(const QSurfaceFormat &format, bool isEs); - QObject *focusObject() Q_DECL_OVERRIDE; - #if QT_CONFIG(opengl) GLuint textureId() const Q_DECL_OVERRIDE; QImage grabFramebuffer() Q_DECL_OVERRIDE; diff --git a/src/src.pro b/src/src.pro index 4e2de8da14..3bb399acd4 100644 --- a/src/src.pro +++ b/src/src.pro @@ -10,7 +10,7 @@ qtHaveModule(gui):qtConfig(animation) { quick \ qmltest - qtConfig(quick-sprite):qtConfig(opengl(es1|es2)?): \ + qtConfig(quick-particles): \ SUBDIRS += particles qtHaveModule(widgets): SUBDIRS += quickwidgets } diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 1e80f1bf65..f9c1fcce91 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -18,4 +18,7 @@ qtHaveModule(gui):qtConfig(opengl(es1|es2)?) { # console applications not supported uikit: SUBDIRS -= qmltest +# Restricted sub-set for now +boot2qt: SUBDIRS = qml + installed_cmake.depends = cmake diff --git a/tests/auto/particles/qquickparticlesystem/data/crashaffectors.qml b/tests/auto/particles/qquickparticlesystem/data/crashaffectors.qml new file mode 100644 index 0000000000..de105916a7 --- /dev/null +++ b/tests/auto/particles/qquickparticlesystem/data/crashaffectors.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2017 reMarkable A/S +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Particles 2.0 + +ParticleSystem { + running: false + Affector { Component.onCompleted: destroy() } + Emitter { + Timer { interval: 1; running: true; onTriggered: parent.lifeSpan = 1 } + } +} diff --git a/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp b/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp index 5c82b946e5..5f9db12144 100644 --- a/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp +++ b/tests/auto/particles/qquickparticlesystem/tst_qquickparticlesystem.cpp @@ -42,6 +42,7 @@ public: private slots: void initTestCase(); void test_basic(); + void test_affectorscrash(); }; void tst_qquickparticlesystem::initTestCase() @@ -78,6 +79,12 @@ void tst_qquickparticlesystem::test_basic() delete view; QVERIFY(extremelyFuzzyCompare(stillAlive, 500, 5));//Small simulation variance is permissible. } +void tst_qquickparticlesystem::test_affectorscrash() +{ + QScopedPointer<QQuickView> view (createView(testFileUrl("crashaffectors.qml"), 600)); + + // This should have crashed by now +} QTEST_MAIN(tst_qquickparticlesystem); diff --git a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp index 8be82c30f9..f193d3928a 100644 --- a/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp +++ b/tests/auto/qml/debugger/qdebugmessageservice/tst_qdebugmessageservice.cpp @@ -135,7 +135,7 @@ void QQmlDebugMsgClient::messageReceived(const QByteArray &data) QVERIFY(ds.atEnd()); QVERIFY(type >= QtDebugMsg); - QVERIFY(type <= QtFatalMsg); + QVERIFY(type <= QtInfoMsg); QVERIFY(timestamp > 0); LogEntry entry((QtMsgType)type, QString::fromUtf8(message)); diff --git a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp index 31b8d63ec2..d248cf9708 100644 --- a/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp +++ b/tests/auto/qml/debugger/qqmldebugjs/qqmldebugjs/tst_qqmldebugjs.cpp @@ -257,7 +257,7 @@ public: stringify = jsEngine.evaluate(QLatin1String("JSON.stringify")); } - void connect(); + void connect(bool redundantRefs = false, bool namesAsObjects = false); void interrupt(); void continueDebugging(StepAction stepAction); @@ -304,9 +304,13 @@ public: }; -void QJSDebugClient::connect() +void QJSDebugClient::connect(bool redundantRefs, bool namesAsObjects) { - sendMessage(packMessage(CONNECT)); + QJSValue jsonVal = parser.call(QJSValueList() << QLatin1String("{}")); + jsonVal.setProperty("redundantRefs", QJSValue(redundantRefs)); + jsonVal.setProperty("namesAsObjects", QJSValue(namesAsObjects)); + sendMessage(packMessage(CONNECT, + stringify.call(QJSValueList() << jsonVal).toString().toUtf8())); } void QJSDebugClient::interrupt() @@ -870,8 +874,10 @@ void tst_QQmlDebugJS::interrupt() //void connect() QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene); - client->connect(); + client->connect(redundantRefs, namesAsObjects); client->interrupt(); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(interruptRequested()))); @@ -882,8 +888,10 @@ void tst_QQmlDebugJS::getVersion() //void version() QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(connected()))); client->version(); @@ -894,9 +902,11 @@ void tst_QQmlDebugJS::getVersionWhenAttaching() { //void version() QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene, QLatin1String(TIMER_QMLFILE), false); - client->connect(); + client->connect(redundantRefs, namesAsObjects); client->version(); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); @@ -907,8 +917,10 @@ void tst_QQmlDebugJS::disconnect() //void disconnect() QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene); - client->connect(); + client->connect(redundantRefs, namesAsObjects); client->disconnect(); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(result()))); @@ -918,12 +930,14 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnCompleted() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); QString jsonString(client->response); @@ -939,12 +953,14 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, CREATECOMPONENT_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); QString jsonString(client->response); @@ -959,10 +975,12 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComponentCreated() void tst_QQmlDebugJS::setBreakpointInScriptOnTimerCallback() { QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 35; init(qmlscene, TIMER_QMLFILE); - client->connect(); + client->connect(redundantRefs, namesAsObjects); //We can set the breakpoint after connect() here because the timer is repeating and if we miss //its first iteration we can still catch the second one. client->setBreakpoint(QLatin1String(TIMER_QMLFILE), sourceLine, -1, true); @@ -981,12 +999,14 @@ void tst_QQmlDebugJS::setBreakpointInScriptInDifferentFile() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 31; init(qmlscene, LOADJSFILE_QMLFILE); client->setBreakpoint(QLatin1String(TEST_JSFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); QString jsonString(client->response); @@ -1002,13 +1022,15 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnComment() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; int actualLine = 36; init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()), 1)); @@ -1025,13 +1047,15 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnEmptyLine() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 35; int actualLine = 36; init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QEXPECT_FAIL("", "Relocation of breakpoints is disabled right now", Abort); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()), 1)); @@ -1048,12 +1072,14 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() { //void setBreakpoint(QString type, QString target, int line = -1, int column = -1, bool enabled = false, QString condition = QString(), int ignoreCount = -1) QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 39; init(qmlscene, BREAKPOINTRELOCATION_QMLFILE); client->setBreakpoint(QLatin1String(BREAKPOINTRELOCATION_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); QString jsonString(client->response); @@ -1068,11 +1094,13 @@ void tst_QQmlDebugJS::setBreakpointInScriptOnOptimizedBinding() void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() { QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int out = 10; int sourceLine = 37; init(qmlscene, CONDITION_QMLFILE); - client->connect(); + client->connect(redundantRefs, namesAsObjects); //The breakpoint is in a timer loop so we can set it after connect(). client->setBreakpoint(QLatin1String(CONDITION_QMLFILE), sourceLine, 1, true, QLatin1String("a > 10")); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); @@ -1105,12 +1133,14 @@ void tst_QQmlDebugJS::setBreakpointInScriptWithCondition() void tst_QQmlDebugJS::setBreakpointInScriptThatQuits() { QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene, QUIT_QMLFILE); int sourceLine = 36; client->setBreakpoint(QLatin1String(QUIT_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); QString jsonString(client->response); @@ -1147,12 +1177,14 @@ void tst_QQmlDebugJS::clearBreakpoint() { //void clearBreakpoint(int breakpoint); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine1 = 37; int sourceLine2 = 38; init(qmlscene, CHANGEBREAKPOINT_QMLFILE); - client->connect(); + client->connect(redundantRefs, namesAsObjects); //The breakpoints are in a timer loop so we can set them after connect(). //Furthermore the breakpoints should be hit in the right order because setting of breakpoints //can only occur in the QML event loop. (see QCOMPARE for sourceLine2 below) @@ -1195,10 +1227,12 @@ void tst_QQmlDebugJS::setExceptionBreak() { //void setExceptionBreak(QString type, bool enabled = false); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene, EXCEPTION_QMLFILE); client->setExceptionBreak(QJSDebugClient::All,true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); } @@ -1206,12 +1240,14 @@ void tst_QQmlDebugJS::stepNext() { //void continueDebugging(StepAction stepAction, int stepCount = 1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 37; init(qmlscene, STEPACTION_QMLFILE); client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->continueDebugging(QJSDebugClient::Next); @@ -1230,13 +1266,15 @@ void tst_QQmlDebugJS::stepIn() { //void continueDebugging(StepAction stepAction, int stepCount = 1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 41; int actualLine = 37; init(qmlscene, STEPACTION_QMLFILE); client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, 1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->continueDebugging(QJSDebugClient::In); @@ -1255,13 +1293,15 @@ void tst_QQmlDebugJS::stepOut() { //void continueDebugging(StepAction stepAction, int stepCount = 1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 37; int actualLine = 41; init(qmlscene, STEPACTION_QMLFILE); client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->continueDebugging(QJSDebugClient::Out); @@ -1280,6 +1320,8 @@ void tst_QQmlDebugJS::continueDebugging() { //void continueDebugging(StepAction stepAction, int stepCount = 1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine1 = 41; int sourceLine2 = 38; @@ -1287,7 +1329,7 @@ void tst_QQmlDebugJS::continueDebugging() client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine1, -1, true); client->setBreakpoint(QLatin1String(STEPACTION_QMLFILE), sourceLine2, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->continueDebugging(QJSDebugClient::Continue); @@ -1306,12 +1348,14 @@ void tst_QQmlDebugJS::backtrace() { //void backtrace(int fromFrame = -1, int toFrame = -1, bool bottom = false); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->backtrace(); @@ -1322,12 +1366,14 @@ void tst_QQmlDebugJS::getFrameDetails() { //void frame(int number = -1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->frame(); @@ -1338,12 +1384,14 @@ void tst_QQmlDebugJS::getScopeDetails() { //void scope(int number = -1, int frameNumber = -1); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->scope(); @@ -1374,11 +1422,13 @@ void tst_QQmlDebugJS::evaluateInLocalScope() //void evaluate(QString expr, bool global = false, bool disableBreak = false, int frame = -1, const QVariantMap &addContext = QVariantMap()); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); int sourceLine = 34; init(qmlscene, ONCOMPLETED_QMLFILE); client->setBreakpoint(QLatin1String(ONCOMPLETED_QMLFILE), sourceLine, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->frame(); @@ -1461,10 +1511,12 @@ void tst_QQmlDebugJS::getScripts() //void scripts(int types = -1, QList<int> ids = QList<int>(), bool includeSource = false, QVariant filter = QVariant()); QFETCH(bool, qmlscene); + QFETCH(bool, redundantRefs); + QFETCH(bool, namesAsObjects); init(qmlscene); client->setBreakpoint(QString(TEST_QMLFILE), 35, -1, true); - client->connect(); + client->connect(redundantRefs, namesAsObjects); QVERIFY(QQmlDebugTest::waitForSignal(client, SIGNAL(stopped()))); client->scripts(); @@ -1482,8 +1534,16 @@ void tst_QQmlDebugJS::getScripts() void tst_QQmlDebugJS::targetData() { QTest::addColumn<bool>("qmlscene"); - QTest::newRow("custom") << false; - QTest::newRow("qmlscene") << true; + QTest::addColumn<bool>("redundantRefs"); + QTest::addColumn<bool>("namesAsObjects"); + QTest::newRow("custom / redundant / objects") << false << true << true; + QTest::newRow("qmlscene / redundant / objects") << true << true << true; + QTest::newRow("custom / redundant / strings") << false << true << false; + QTest::newRow("qmlscene / redundant / strings") << true << true << false; + QTest::newRow("custom / sparse / objects") << false << false << true; + QTest::newRow("qmlscene / sparse / objects") << true << false << true; + QTest::newRow("custom / sparse / strings") << false << false << false; + QTest::newRow("qmlscene / sparse / strings") << true << false << false; } QTEST_MAIN(tst_QQmlDebugJS) diff --git a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp index 8c30a82317..d9a4777115 100644 --- a/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp +++ b/tests/auto/qml/debugger/qqmlenginedebugservice/tst_qqmlenginedebugservice.cpp @@ -180,14 +180,9 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( { const QMetaObject *meta = o->metaObject(); - QQmlType *type = QQmlMetaType::qmlType(meta); - QString className = type ? QString(type->qmlTypeName()) - : QString(meta->className()); - className = className.mid(className.lastIndexOf(QLatin1Char('/'))+1); - QCOMPARE(oref.debugId, QQmlDebugService::idForObject(o)); QCOMPARE(oref.name, o->objectName()); - QCOMPARE(oref.className, className); + QCOMPARE(oref.className, QQmlMetaType::prettyTypeName(o)); QCOMPARE(oref.contextDebugId, QQmlDebugService::idForObject( qmlContext(o))); @@ -201,6 +196,7 @@ void tst_QQmlEngineDebugService::recursiveObjectTest( QmlDebugObjectReference cref; foreach (const QmlDebugObjectReference &ref, oref.children) { + QVERIFY(!ref.className.isEmpty()); if (ref.debugId == debugId) { cref = ref; break; @@ -369,6 +365,7 @@ void tst_QQmlEngineDebugService::setMethodBody() { bool success; QmlDebugObjectReference obj = findRootObject(2); + QVERIFY(!obj.className.isEmpty()); QObject *root = m_components.at(2); // Without args @@ -410,6 +407,7 @@ void tst_QQmlEngineDebugService::setMethodBody() void tst_QQmlEngineDebugService::watch_property() { QmlDebugObjectReference obj = findRootObject(); + QVERIFY(!obj.className.isEmpty()); QmlDebugPropertyReference prop = findProperty(obj.properties, "width"); bool success; @@ -454,6 +452,7 @@ void tst_QQmlEngineDebugService::watch_property() void tst_QQmlEngineDebugService::watch_object() { QmlDebugObjectReference obj = findRootObject(); + QVERIFY(!obj.className.isEmpty()); bool success; @@ -519,6 +518,7 @@ void tst_QQmlEngineDebugService::watch_expression() int origWidth = m_rootItem->property("width").toInt(); QmlDebugObjectReference obj = findRootObject(); + QVERIFY(!obj.className.isEmpty()); bool success; @@ -654,6 +654,7 @@ void tst_QQmlEngineDebugService::queryObject() bool success; QmlDebugObjectReference rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); recursive ? unconnected->queryObjectRecursive(rootObject, &success) : unconnected->queryObject(rootObject, &success); @@ -665,6 +666,7 @@ void tst_QQmlEngineDebugService::queryObject() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QmlDebugObjectReference obj = m_dbg->object(); + QVERIFY(!obj.className.isEmpty()); // check source as defined in main() QmlDebugFileReference source = obj.source; @@ -676,12 +678,15 @@ void tst_QQmlEngineDebugService::queryObject() recursiveObjectTest(m_rootItem, obj, recursive); if (recursive) { - foreach (const QmlDebugObjectReference &child, obj.children) + foreach (const QmlDebugObjectReference &child, obj.children) { + QVERIFY(!child.className.isEmpty()); QVERIFY(child.properties.count() > 0); + } QmlDebugObjectReference rect; QmlDebugObjectReference text; foreach (const QmlDebugObjectReference &child, obj.children) { + QVERIFY(!child.className.isEmpty()); if (child.className == "Rectangle") rect = child; else if (child.className == "Text") @@ -695,8 +700,10 @@ void tst_QQmlEngineDebugService::queryObject() QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue"))); } else { - foreach (const QmlDebugObjectReference &child, obj.children) + foreach (const QmlDebugObjectReference &child, obj.children) { + QVERIFY(!child.className.isEmpty()); QCOMPARE(child.properties.count(), 0); + } } } @@ -715,6 +722,7 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() bool success; QmlDebugObjectReference rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); const QString fileName = QFileInfo(rootObject.source.url.toString()).fileName(); int lineNumber = rootObject.source.lineNumber; @@ -737,6 +745,7 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() QCOMPARE(m_dbg->objects().count(), 1); QmlDebugObjectReference obj = m_dbg->objects().first(); + QVERIFY(!obj.className.isEmpty()); // check source as defined in main() QmlDebugFileReference source = obj.source; @@ -748,12 +757,15 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() recursiveObjectTest(m_rootItem, obj, recursive); if (recursive) { - foreach (const QmlDebugObjectReference &child, obj.children) + foreach (const QmlDebugObjectReference &child, obj.children) { + QVERIFY(!child.className.isEmpty()); QVERIFY(child.properties.count() > 0); + } QmlDebugObjectReference rect; QmlDebugObjectReference text; foreach (const QmlDebugObjectReference &child, obj.children) { + QVERIFY(!child.className.isEmpty()); if (child.className == "Rectangle") rect = child; else if (child.className == "Text") @@ -767,8 +779,10 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation() QCOMPARE(findProperty(text.properties, "color").value, qVariantFromValue(QColor("blue"))); } else { - foreach (const QmlDebugObjectReference &child, obj.children) + foreach (const QmlDebugObjectReference &child, obj.children) { + QVERIFY(!child.className.isEmpty()); QCOMPARE(child.properties.count(), 0); + } } } @@ -783,6 +797,7 @@ void tst_QQmlEngineDebugService::queryObjectsForLocation_data() void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() { QmlDebugObjectReference rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); int contextId = rootObject.contextDebugId; QQmlContext *context = qobject_cast<QQmlContext *>(QQmlDebugService::objectForId(contextId)); QQmlComponent component(context->engine()); @@ -809,6 +824,7 @@ void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); foreach (QmlDebugObjectReference child, rootObject.children) { + QVERIFY(!child.className.isEmpty()); success = false; lineNumber = child.source.lineNumber; columnNumber = child.source.columnNumber; @@ -831,6 +847,7 @@ void tst_QQmlEngineDebugService::regression_QTCREATORBUG_7451() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); foreach (QmlDebugObjectReference child, rootObject.children) { + QVERIFY(!child.className.isEmpty()); success = false; lineNumber = child.source.lineNumber; columnNumber = child.source.columnNumber; @@ -846,6 +863,7 @@ void tst_QQmlEngineDebugService::queryObjectWithNonStreamableTypes() bool success; QmlDebugObjectReference rootObject = findRootObject(4, true); + QVERIFY(!rootObject.className.isEmpty()); QQmlEngineDebugClient *unconnected = new QQmlEngineDebugClient(0); unconnected->queryObject(rootObject, &success); @@ -857,6 +875,7 @@ void tst_QQmlEngineDebugService::queryObjectWithNonStreamableTypes() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); QmlDebugObjectReference obj = m_dbg->object(); + QVERIFY(!obj.className.isEmpty()); QCOMPARE(findProperty(obj.properties, "modelIndex").value, QVariant()); } @@ -950,6 +969,7 @@ void tst_QQmlEngineDebugService::queryExpressionResultBC_data() void tst_QQmlEngineDebugService::setBindingForObject() { QmlDebugObjectReference rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); QVERIFY(rootObject.debugId != -1); QmlDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width"); @@ -967,6 +987,7 @@ void tst_QQmlEngineDebugService::setBindingForObject() QCOMPARE(m_dbg->valid(), true); rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); widthPropertyRef = findProperty(rootObject.properties, "width"); QCOMPARE(widthPropertyRef.value, QVariant(15)); @@ -982,6 +1003,7 @@ void tst_QQmlEngineDebugService::setBindingForObject() QCOMPARE(m_dbg->valid(), true); rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); widthPropertyRef = findProperty(rootObject.properties, "width"); QCOMPARE(widthPropertyRef.value, QVariant(20)); @@ -992,13 +1014,14 @@ void tst_QQmlEngineDebugService::setBindingForObject() // set handler // rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); QCOMPARE(rootObject.children.size(), 5); // Rectangle, Text, MouseArea, Component.onCompleted, NonScriptPropertyElement QmlDebugObjectReference mouseAreaObject = rootObject.children.at(2); + QVERIFY(!mouseAreaObject.className.isEmpty()); m_dbg->queryObjectRecursive(mouseAreaObject, &success); QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); mouseAreaObject = m_dbg->object(); - QCOMPARE(mouseAreaObject.className, QString("MouseArea")); QmlDebugPropertyReference onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered"); @@ -1015,11 +1038,14 @@ void tst_QQmlEngineDebugService::setBindingForObject() QCOMPARE(m_dbg->valid(), true); rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); mouseAreaObject = rootObject.children.at(2); + QVERIFY(!mouseAreaObject.className.isEmpty()); m_dbg->queryObjectRecursive(mouseAreaObject, &success); QVERIFY(success); QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); mouseAreaObject = m_dbg->object(); + QVERIFY(!mouseAreaObject.className.isEmpty()); onEnteredRef = findProperty(mouseAreaObject.properties, "onEntered"); QCOMPARE(onEnteredRef.name, QString("onEntered")); QCOMPARE(onEnteredRef.value, QVariant("function() { [code] }")); @@ -1028,6 +1054,7 @@ void tst_QQmlEngineDebugService::setBindingForObject() void tst_QQmlEngineDebugService::resetBindingForObject() { QmlDebugObjectReference rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); QVERIFY(rootObject.debugId != -1); QmlDebugPropertyReference widthPropertyRef = findProperty(rootObject.properties, "width"); @@ -1048,6 +1075,7 @@ void tst_QQmlEngineDebugService::resetBindingForObject() QCOMPARE(m_dbg->valid(), true); rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); widthPropertyRef = findProperty(rootObject.properties, "width"); QCOMPARE(widthPropertyRef.value, QVariant(0)); @@ -1063,6 +1091,7 @@ void tst_QQmlEngineDebugService::resetBindingForObject() QCOMPARE(m_dbg->valid(), true); rootObject = findRootObject(); + QVERIFY(!rootObject.className.isEmpty()); QmlDebugPropertyReference boldPropertyRef = findProperty(rootObject.properties, "font.bold"); QCOMPARE(boldPropertyRef.value.toBool(), false); @@ -1076,7 +1105,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() const int sourceIndex = 3; QmlDebugObjectReference obj = findRootObject(sourceIndex); - + QVERIFY(!obj.className.isEmpty()); QVERIFY(obj.debugId != -1); QVERIFY(obj.children.count() >= 2); bool success; @@ -1092,6 +1121,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); obj = findRootObject(sourceIndex); + QVERIFY(!obj.className.isEmpty()); QCOMPARE(findProperty(obj.properties,"width").value.toInt(),200); @@ -1102,6 +1132,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() obj = findRootObject(sourceIndex, true); + QVERIFY(!obj.className.isEmpty()); QCOMPARE(findProperty(obj.properties,"width").value.toInt(),100); @@ -1111,6 +1142,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() QVERIFY(state.children.count() > 0); QmlDebugObjectReference propertyChange = state.children[0]; + QVERIFY(!propertyChange.className.isEmpty()); QVERIFY(propertyChange.debugId != -1); m_dbg->setBindingForObject(propertyChange.debugId, "width",QVariant(300),true, @@ -1120,6 +1152,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() // check properties changed in state obj = findRootObject(sourceIndex); + QVERIFY(!obj.className.isEmpty()); QCOMPARE(findProperty(obj.properties,"width").value.toInt(),100); @@ -1128,6 +1161,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); obj = findRootObject(sourceIndex); + QVERIFY(!obj.className.isEmpty()); QCOMPARE(findProperty(obj.properties,"width").value.toInt(),300); // check changing properties of base state from within a state @@ -1141,6 +1175,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); obj = findRootObject(sourceIndex); + QVERIFY(!obj.className.isEmpty()); QCOMPARE(findProperty(obj.properties,"width").value.toInt(),300); m_dbg->queryExpressionResult(obj.debugId,QString("state=\"\""), &success); @@ -1148,6 +1183,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); obj = findRootObject(sourceIndex); + QVERIFY(!obj.className.isEmpty()); QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 400); // reset binding while in a state @@ -1156,6 +1192,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() QVERIFY(QQmlDebugTest::waitForSignal(m_dbg, SIGNAL(result()))); obj = findRootObject(sourceIndex); + QVERIFY(!obj.className.isEmpty()); QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 300); m_dbg->resetBindingForObject(propertyChange.debugId, "width", &success); @@ -1164,6 +1201,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() QCOMPARE(m_dbg->valid(), true); obj = findRootObject(sourceIndex); + QVERIFY(!obj.className.isEmpty()); QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 400); // re-add binding @@ -1174,6 +1212,7 @@ void tst_QQmlEngineDebugService::setBindingInStates() QCOMPARE(m_dbg->valid(), true); obj = findRootObject(sourceIndex); + QVERIFY(!obj.className.isEmpty()); QCOMPARE(findProperty(obj.properties,"width").value.toInt(), 300); } @@ -1182,7 +1221,7 @@ void tst_QQmlEngineDebugService::queryObjectTree() const int sourceIndex = 3; QmlDebugObjectReference obj = findRootObject(sourceIndex, true); - + QVERIFY(!obj.className.isEmpty()); QVERIFY(obj.debugId != -1); QVERIFY(obj.children.count() >= 2); @@ -1192,16 +1231,16 @@ void tst_QQmlEngineDebugService::queryObjectTree() QVERIFY(state.children.count() > 0); QmlDebugObjectReference propertyChange = state.children[0]; + QVERIFY(!propertyChange.className.isEmpty()); QVERIFY(propertyChange.debugId != -1); QmlDebugPropertyReference propertyChangeTarget = findProperty(propertyChange.properties,"target"); QCOMPARE(propertyChangeTarget.objectDebugId, propertyChange.debugId); QmlDebugObjectReference targetReference = qvariant_cast<QmlDebugObjectReference>(propertyChangeTarget.value); + QVERIFY(!targetReference.className.isEmpty()); QVERIFY(targetReference.debugId != -1); - - // check transition QmlDebugObjectReference transition = obj.children[0]; QCOMPARE(transition.className, QString("Transition")); @@ -1210,12 +1249,14 @@ void tst_QQmlEngineDebugService::queryObjectTree() QVERIFY(transition.children.count() > 0); QmlDebugObjectReference animation = transition.children[0]; + QVERIFY(!animation.className.isEmpty()); QVERIFY(animation.debugId != -1); QmlDebugPropertyReference animationTarget = findProperty(animation.properties,"target"); QCOMPARE(animationTarget.objectDebugId, animation.debugId); targetReference = qvariant_cast<QmlDebugObjectReference>(animationTarget.value); + QVERIFY(!targetReference.className.isEmpty()); QVERIFY(targetReference.debugId != -1); QCOMPARE(findProperty(animation.properties,"property").value.toString(), QString("width")); diff --git a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp index 6793596174..584bd10151 100644 --- a/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp +++ b/tests/auto/qml/debugger/qv4debugger/tst_qv4debugger.cpp @@ -120,7 +120,6 @@ public: typedef QV4DataCollector::Refs Refs; typedef QV4DataCollector::Ref Ref; struct NamedRefs { - QJsonArray refs; QJsonObject scope; int size() const { @@ -131,15 +130,6 @@ public: return scope.contains(name); } - QJsonObject resolveRef(int ref) const { - foreach (const QJsonValue &val, refs) { - QJsonObject obj = val.toObject(); - if (obj.value(QLatin1String("handle")).toInt() == ref) - return obj; - } - return QJsonObject(); - } - #define DUMP_JSON(x) {\ QJsonDocument doc(x);\ qDebug() << #x << "=" << doc.toJson(QJsonDocument::Indented);\ @@ -176,6 +166,7 @@ public: , m_captureContextInfo(false) , m_thrownValue(-1) , collector(engine) + , m_resumeSpeed(QV4Debugger::FullThrottle) , m_debugger(0) { } @@ -208,13 +199,12 @@ public slots: request.expression, &collector); debugger->runInEngine(&job); m_expressionResults << job.returnValue(); - m_expressionRefs << job.refs(); } if (m_captureContextInfo) captureContextInfo(debugger); - debugger->resume(QV4Debugger::FullThrottle); + debugger->resume(m_resumeSpeed); } public: @@ -234,24 +224,17 @@ public: ScopeJob job(&collector, i, 0); debugger->runInEngine(&job); NamedRefs &refs = m_capturedScope.last(); - refs.refs = job.refs(); QJsonObject object = job.returnValue(); object = object.value(QLatin1String("object")).toObject(); - int ref = object.value(QLatin1String("ref")).toInt(); - object = refs.resolveRef(ref); + if (object.contains("ref") && !object.contains("properties")) { + QVERIFY(collector.redundantRefs()); + object = collector.lookupRef(object.value("ref").toInt(), true); + QVERIFY(object.contains("properties")); + } foreach (const QJsonValue &value, object.value(QLatin1String("properties")).toArray()) { QJsonObject property = value.toObject(); QString name = property.value(QLatin1String("name")).toString(); property.remove(QLatin1String("name")); - if (property.contains(QLatin1String("ref"))) { - int childRef = property.value(QLatin1String("ref")).toInt(); - if (childRef >= 0 && refs.refs.size() > childRef) { - property.remove(QLatin1String("ref")); - property.insert(QLatin1String("properties"), - refs.resolveRef(childRef).value( - QLatin1String("properties")).toArray()); - } - } refs.scope.insert(name, property); } } @@ -280,8 +263,8 @@ public: int context; }; QVector<ExpressionRequest> m_expressionRequests; + QV4Debugger::Speed m_resumeSpeed; QList<QJsonObject> m_expressionResults; - QList<QJsonArray> m_expressionRefs; QV4Debugger *m_debugger; // Utility methods: @@ -313,9 +296,13 @@ private slots: void conditionalBreakPointInQml(); // context access: + void readArguments_data() { redundancy_data(); } void readArguments(); + void readLocals_data() { redundancy_data(); } void readLocals(); + void readObject_data() { redundancy_data(); } void readObject(); + void readContextInAllFrames_data() { redundancy_data(); } void readContextInAllFrames(); // exceptions: @@ -323,8 +310,13 @@ private slots: void breakInCatch(); void breakInWith(); + void evaluateExpression_data() { redundancy_data(); } void evaluateExpression(); + void stepToEndOfScript_data() { redundancy_data(); } + void stepToEndOfScript(); + void lastLineOfConditional_data(); + void lastLineOfConditional(); private: QV4Debugger *debugger() const { @@ -338,6 +330,8 @@ private: waitForSignal(m_engine, SIGNAL(evaluateFinished()), /*timeout*/0); } + void redundancy_data(); + TestEngine *m_engine; QV4::ExecutionEngine *m_v4; TestAgent *m_debuggerAgent; @@ -532,6 +526,9 @@ void tst_qv4debugger::conditionalBreakPointInQml() void tst_qv4debugger::readArguments() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + m_debuggerAgent->m_captureContextInfo = true; QString script = "function f(a, b, c, d) {\n" @@ -555,6 +552,9 @@ void tst_qv4debugger::readArguments() void tst_qv4debugger::readLocals() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + m_debuggerAgent->m_captureContextInfo = true; QString script = "function f(a, b) {\n" @@ -578,6 +578,9 @@ void tst_qv4debugger::readLocals() void tst_qv4debugger::readObject() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + m_debuggerAgent->m_captureContextInfo = true; QString script = "function f(a) {\n" @@ -594,6 +597,12 @@ void tst_qv4debugger::readObject() QVERIFY(frame0.contains("b")); QCOMPARE(frame0.type("b"), QStringLiteral("object")); QJsonObject b = frame0.rawValue("b"); + QVERIFY(b.contains(QStringLiteral("ref"))); + QVERIFY(b.contains(QStringLiteral("value"))); + QVERIFY(!b.contains(QStringLiteral("properties"))); + QVERIFY(b.value("value").isDouble()); + QCOMPARE(b.value("value").toInt(), 2); + b = m_debuggerAgent->collector.lookupRef(b.value("ref").toInt(), true); QVERIFY(b.contains(QStringLiteral("properties"))); QVERIFY(b.value("properties").isArray()); QJsonArray b_props = b.value("properties").toArray(); @@ -609,7 +618,8 @@ void tst_qv4debugger::readObject() QCOMPARE(b_tail.value("name").toString(), QStringLiteral("tail")); QVERIFY(b_tail.contains("ref")); - QJsonObject b_tail_value = m_debuggerAgent->collector.lookupRef(b_tail.value("ref").toInt()); + QJsonObject b_tail_value = m_debuggerAgent->collector.lookupRef(b_tail.value("ref").toInt(), + true); QCOMPARE(b_tail_value.value("type").toString(), QStringLiteral("object")); QVERIFY(b_tail_value.contains("properties")); QJsonArray b_tail_props = b_tail_value.value("properties").toArray(); @@ -626,6 +636,9 @@ void tst_qv4debugger::readObject() void tst_qv4debugger::readContextInAllFrames() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + m_debuggerAgent->m_captureContextInfo = true; QString script = "function fact(n) {\n" @@ -673,7 +686,8 @@ void tst_qv4debugger::pauseOnThrow() QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Throwing); QCOMPARE(m_debuggerAgent->m_stackTrace.size(), 2); QVERIFY(m_debuggerAgent->m_thrownValue >= qint64(0)); - QJsonObject exception = m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_thrownValue); + QJsonObject exception = m_debuggerAgent->collector.lookupRef(m_debuggerAgent->m_thrownValue, + true); // DUMP_JSON(exception); QCOMPARE(exception.value("type").toString(), QStringLiteral("string")); QCOMPARE(exception.value("value").toString(), QStringLiteral("hard")); @@ -717,6 +731,9 @@ void tst_qv4debugger::breakInWith() void tst_qv4debugger::evaluateExpression() { + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + QString script = "function testFunction() {\n" " var x = 10\n" @@ -745,19 +762,119 @@ void tst_qv4debugger::evaluateExpression() evaluateJavaScript(script, "evaluateExpression"); - QCOMPARE(m_debuggerAgent->m_expressionRefs.count(), 4); - QCOMPARE(m_debuggerAgent->m_expressionRefs[0].size(), 1); - QJsonObject result0 = m_debuggerAgent->m_expressionRefs[0].first().toObject(); + QCOMPARE(m_debuggerAgent->m_expressionResults.count(), 4); + QJsonObject result0 = m_debuggerAgent->m_expressionResults[0]; QCOMPARE(result0.value("type").toString(), QStringLiteral("number")); QCOMPARE(result0.value("value").toInt(), 10); for (int i = 1; i < 4; ++i) { - QCOMPARE(m_debuggerAgent->m_expressionRefs[i].size(), 1); - QJsonObject result1 = m_debuggerAgent->m_expressionRefs[1].first().toObject(); + QJsonObject result1 = m_debuggerAgent->m_expressionResults[i]; QCOMPARE(result1.value("type").toString(), QStringLiteral("number")); QCOMPARE(result1.value("value").toInt(), 20); } } +void tst_qv4debugger::stepToEndOfScript() +{ + QFETCH(bool, redundantRefs); + m_debuggerAgent->collector.setRedundantRefs(redundantRefs); + + QString script = + "var ret = 0;\n" + "ret += 4;\n" + "ret += 1;\n" + "ret += 5;\n"; + + debugger()->addBreakPoint("toEnd", 1); + m_debuggerAgent->m_resumeSpeed = QV4Debugger::StepOver; + evaluateJavaScript(script, "toEnd"); + QVERIFY(m_debuggerAgent->m_wasPaused); + QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Step); + QCOMPARE(m_debuggerAgent->m_statesWhenPaused.count(), 5); + for (int i = 0; i < 4; ++i) { + QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(i); + QCOMPARE(state.fileName, QString("toEnd")); + QCOMPARE(state.lineNumber, i + 1); + } + + QV4Debugger::ExecutionState state = m_debuggerAgent->m_statesWhenPaused.at(4); + QCOMPARE(state.fileName, QString("toEnd")); + QCOMPARE(state.lineNumber, -4); // A return instruction without proper line number. +} + +void tst_qv4debugger::lastLineOfConditional_data() +{ + QTest::addColumn<QString>("head"); + QTest::addColumn<QString>("tail"); + QTest::addColumn<int>("breakPoint"); + QTest::addColumn<int>("lastLine"); + + QTest::newRow("for {block}") << "for (var i = 0; i < 10; ++i) {\n" << "}" << 4 << 7; + QTest::newRow("for..in {block}") << "for (var i in [0, 1, 2, 3, 4]) {\n" << "}" << 4 << 7; + QTest::newRow("while {block}") << "while (ret < 10) {\n" << "}" << 4 << 7; + QTest::newRow("do..while {block}") << "do {\n" << "} while (ret < 10);" << 4 << 7; + + QTest::newRow("if true {block}") << "if (true) {\n" << "}" + << 4 << 7; + QTest::newRow("if false {block}") << "if (false) {\n" << "}" + << 2 << 8; + QTest::newRow("if true else {block}") << "if (true) {\n" << "} else {\n ret += 8;\n}" + << 4 << 7; + QTest::newRow("if false else {block}") << "if (false) {\n" << "} else {\n ret += 8;\n}" + << 8 << 9; + + QTest::newRow("for statement") << "for (var i = 0; i < 10; ++i)\n" << "" << 4 << 2; + QTest::newRow("for..in statement") << "for (var i in [0, 1, 2, 3, 4])\n" << "" << 4 << 2; + QTest::newRow("while statement") << "while (ret < 10)\n" << "" << 4 << 2; + QTest::newRow("do..while statement") << "do\n" << "while (ret < 10);" << 4 << 7; + + // For two nested if statements without blocks, we need to map the jump from the inner to the + // outer one on the outer "if". There is just no better place. + QTest::newRow("if true statement") << "if (true)\n" << "" << 4 << 2; + QTest::newRow("if false statement") << "if (false)\n" << "" << 2 << 8; + + // Also two nested ifs without blocks. + QTest::newRow("if true else statement") << "if (true)\n" << "else\n ret += 8;" << 4 << 2; + QTest::newRow("if false else statement") << "if (false)\n" << "else\n ret += 8;" << 8 << 9; +} + +void tst_qv4debugger::lastLineOfConditional() +{ + QFETCH(QString, head); + QFETCH(QString, tail); + QFETCH(int, breakPoint); + QFETCH(int, lastLine); + + QString script = + "var ret = 2;\n" + + head + + " if (ret == 2)\n" + " ret += 4;\n" // breakpoint, then step over + " else\n" + " ret += 1;\n" + + tail + "\n" + + "ret -= 5;"; + + debugger()->addBreakPoint("trueBranch", breakPoint); + m_debuggerAgent->m_resumeSpeed = QV4Debugger::StepOver; + evaluateJavaScript(script, "trueBranch"); + QVERIFY(m_debuggerAgent->m_wasPaused); + QCOMPARE(m_debuggerAgent->m_pauseReason, QV4Debugger::Step); + QVERIFY(m_debuggerAgent->m_statesWhenPaused.count() > 1); + QV4Debugger::ExecutionState firstState = m_debuggerAgent->m_statesWhenPaused.first(); + QCOMPARE(firstState.fileName, QString("trueBranch")); + QCOMPARE(firstState.lineNumber, breakPoint); + QV4Debugger::ExecutionState secondState = m_debuggerAgent->m_statesWhenPaused.at(1); + QCOMPARE(secondState.fileName, QString("trueBranch")); + QCOMPARE(secondState.lineNumber, lastLine); +} + +void tst_qv4debugger::redundancy_data() +{ + QTest::addColumn<bool>("redundantRefs"); + QTest::addRow("redundant") << true; + QTest::addRow("sparse") << false; +} + QTEST_MAIN(tst_qv4debugger) #include "tst_qv4debugger.moc" diff --git a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp index 3e27951d78..c0252a0290 100644 --- a/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp +++ b/tests/auto/qml/debugger/shared/qqmlenginedebugclient.cpp @@ -386,6 +386,7 @@ void QQmlEngineDebugClient::decode(QPacket &ds, { QmlDebugObjectReference obj; obj.debugId = prop.value.toInt(); + obj.className = prop.valueTypeName; prop.value = qVariantFromValue(obj); break; } diff --git a/tests/manual/v4/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index 27498de473..27498de473 100644 --- a/tests/manual/v4/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations diff --git a/tests/auto/qml/ecmascripttests/ecmascripttests.pro b/tests/auto/qml/ecmascripttests/ecmascripttests.pro new file mode 100644 index 0000000000..6d3ee12307 --- /dev/null +++ b/tests/auto/qml/ecmascripttests/ecmascripttests.pro @@ -0,0 +1,20 @@ +CONFIG += testcase +TARGET = tst_ecmascripttests +QT += testlib +macos:CONFIG -= app_bundle +SOURCES += tst_ecmascripttests.cpp +DEFINES += SRCDIR=\\\"$$PWD\\\" + +TESTSCRIPT=$$PWD/test262.py +isEmpty(V4CMD): V4CMD = qmljs + +checkjittarget.target = check-jit +checkjittarget.commands = python $$TESTSCRIPT --command=$$V4CMD --parallel --with-test-expectations --update-expectations +checkjittarget.depends = all +QMAKE_EXTRA_TARGETS += checkjittarget + +checkmothtarget.target = check-interpreter +checkmothtarget.commands = python $$TESTSCRIPT --command=\"$$V4CMD --interpret\" --parallel --with-test-expectations +checkmothtarget.depends = all +QMAKE_EXTRA_TARGETS += checkmothtarget + diff --git a/tests/auto/qml/ecmascripttests/test262 b/tests/auto/qml/ecmascripttests/test262 new file mode 160000 +Subproject d60c4ed97e69639bc5bc1db43a98828debf80c8 diff --git a/tests/manual/v4/test262.py b/tests/auto/qml/ecmascripttests/test262.py index 3b5bfa119a..99f029cffd 100755 --- a/tests/manual/v4/test262.py +++ b/tests/auto/qml/ecmascripttests/test262.py @@ -555,6 +555,7 @@ class TestSuite(object): print if update_expectations: self.expectations.update(progress) + return progress.failed == 0 def Print(self, tests): cases = self.EnumerateTests(tests) @@ -567,6 +568,7 @@ def Main(): # Uncomment the next line for more logging info. #logging.basicConfig(level=logging.DEBUG) os.environ["TZ"] = "PST8PDT" + os.environ["LANG"] = "en_US.UTF-8" parser = BuildOptions() (options, args) = parser.parse_args() ValidateOptions(options) @@ -578,18 +580,21 @@ def Main(): test_suite.Validate() if options.cat: test_suite.Print(args) + return 0 else: - test_suite.Run(options.command, args, - options.summary or options.full_summary, - options.full_summary, - options.parallel, - options.update_expectations) + if test_suite.Run(options.command, args, + options.summary or options.full_summary, + options.full_summary, + options.parallel, + options.update_expectations): + return 0 + else: + return 1 if __name__ == '__main__': try: - Main() - sys.exit(0) + sys.exit(Main()) except Test262Error, e: print "Error: %s" % e.message sys.exit(1) diff --git a/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp b/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp new file mode 100644 index 0000000000..5d7009e7c8 --- /dev/null +++ b/tests/auto/qml/ecmascripttests/tst_ecmascripttests.cpp @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QProcess> +#include <QLibraryInfo> + +class tst_EcmaScriptTests : public QObject +{ + Q_OBJECT +private slots: + void runTests_data(); + void runTests(); +}; + +void tst_EcmaScriptTests::runTests_data() +{ + QTest::addColumn<QString>("qmljsParameter"); + + QTest::newRow("jit") << QStringLiteral("--jit"); + // Not passing yet: QTest::newRow("interpreter") << QStringLiteral("--interpret"); +} + +void tst_EcmaScriptTests::runTests() +{ +#if defined(Q_OS_LINUX) && defined(Q_PROCESSOR_X86_64) + QFETCH(QString, qmljsParameter); + + QProcess process; + process.setProcessChannelMode(QProcess::ForwardedChannels); + process.setWorkingDirectory(QLatin1String(SRCDIR)); + process.setProgram("python"); + process.setArguments(QStringList() << "test262.py" << "--command=" + QLibraryInfo::location(QLibraryInfo::BinariesPath) + "/qmljs " + qmljsParameter << "--parallel" << "--with-test-expectations"); + + qDebug() << "Going to run" << process.program() << process.arguments() << "in" << process.workingDirectory(); + + process.start(); + QVERIFY(process.waitForStarted()); + const int timeoutInMSecs = 20 * 60 * 1000; + QVERIFY2(process.waitForFinished(timeoutInMSecs), "Tests did not terminate in time -- see output above for details"); + QVERIFY2(process.exitStatus() == QProcess::NormalExit, "Running the test harness failed -- see output above for details"); + QVERIFY2(process.exitCode() == 0, "Tests failed -- see output above for details"); +#else + QSKIP("Currently the ecmascript tests are only run on Linux/x86-64"); +#endif +} + +QTEST_GUILESS_MAIN(tst_EcmaScriptTests) + +#include "tst_ecmascripttests.moc" + diff --git a/tests/auto/qml/qml.pro b/tests/auto/qml/qml.pro index 7d182b7255..59566ad927 100644 --- a/tests/auto/qml/qml.pro +++ b/tests/auto/qml/qml.pro @@ -7,6 +7,9 @@ PUBLICTESTS += \ parserstress \ qjsvalueiterator \ qjsonbinding \ + +!boot2qt { +PUBLICTESTS += \ qmlmin \ qqmlcomponent \ qqmlconsole \ @@ -25,15 +28,22 @@ PUBLICTESTS += \ qquickfolderlistmodel \ qqmlapplicationengine \ qqmlsettings \ - qqmlstatemachine + qqmlstatemachine \ + qmldiskcache +} PRIVATETESTS += \ - animation \ qqmlcpputils \ + qqmldirparser \ + v4misc \ + qmlcachegen + +!boot2qt { +PRIVATETESTS += \ + animation \ qqmlecmascript \ qqmlcontext \ qqmlexpression \ - qqmldirparser \ qqmlglobal \ qqmllanguage \ qqmlopenmetaobject \ @@ -57,12 +67,12 @@ PRIVATETESTS += \ qqmltimer \ qqmlinstantiator \ qqmlenginecleanup \ - v4misc \ qqmltranslation \ qqmlimport \ qqmlobjectmodel \ - qmldiskcache \ - qv4mm + qv4mm \ + ecmascripttests +} qtHaveModule(widgets) { PUBLICTESTS += \ @@ -70,14 +80,17 @@ qtHaveModule(widgets) { qjsvalue } -SUBDIRS += $$PUBLICTESTS \ - qqmlextensionplugin +SUBDIRS += $$PUBLICTESTS SUBDIRS += $$METATYPETESTS -qtConfig(process) { +qtConfig(process):!boot2qt { !contains(QT_CONFIG, no-qml-debug): SUBDIRS += debugger SUBDIRS += qmllint qmlplugindump } +qtConfig(library) { + SUBDIRS += qqmlextensionplugin +} + qtConfig(private_tests): \ SUBDIRS += $$PRIVATETESTS diff --git a/tests/auto/qml/qmlcachegen/qmlcachegen.pro b/tests/auto/qml/qmlcachegen/qmlcachegen.pro new file mode 100644 index 0000000000..8d8b37be15 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/qmlcachegen.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qmlcachegen +macos:CONFIG -= app_bundle + +SOURCES += tst_qmlcachegen.cpp + +QT += core-private qml-private testlib diff --git a/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp new file mode 100644 index 0000000000..b7e616a050 --- /dev/null +++ b/tests/auto/qml/qmlcachegen/tst_qmlcachegen.cpp @@ -0,0 +1,163 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** 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 General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> + +#include <QQmlComponent> +#include <QQmlEngine> +#include <QProcess> +#include <QLibraryInfo> +#include <QSysInfo> + +class tst_qmlcachegen: public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void loadGeneratedFile(); + void translationExpressionSupport(); +}; + +// A wrapper around QQmlComponent to ensure the temporary reference counts +// on the type data as a result of the main thread <> loader thread communication +// are dropped. Regular Synchronous loading will leave us with an event posted +// to the gui thread and an extra refcount that will only be dropped after the +// event delivery. A plain sendPostedEvents() however is insufficient because +// we can't be sure that the event is posted after the constructor finished. +class CleanlyLoadingComponent : public QQmlComponent +{ +public: + CleanlyLoadingComponent(QQmlEngine *engine, const QUrl &url) + : QQmlComponent(engine, url, QQmlComponent::Asynchronous) + { waitForLoad(); } + CleanlyLoadingComponent(QQmlEngine *engine, const QString &fileName) + : QQmlComponent(engine, fileName, QQmlComponent::Asynchronous) + { waitForLoad(); } + + void waitForLoad() + { + QTRY_VERIFY(status() == QQmlComponent::Ready || status() == QQmlComponent::Error); + } +}; + +static bool generateCache(const QString &qmlFileName) +{ + QProcess proc; + proc.setProcessChannelMode(QProcess::ForwardedChannels); + proc.setProgram(QLibraryInfo::location(QLibraryInfo::BinariesPath) + QDir::separator() + QLatin1String("qmlcachegen")); + proc.setArguments(QStringList() << (QLatin1String("--target-architecture=") + QSysInfo::buildCpuArchitecture()) << (QLatin1String("--target-abi=") + QSysInfo::buildAbi()) << qmlFileName); + proc.start(); + if (!proc.waitForFinished()) + return false; + if (proc.exitStatus() != QProcess::NormalExit) + return false; + return proc.exitCode() == 0; +} + +void tst_qmlcachegen::initTestCase() +{ + qputenv("QML_FORCE_DISK_CACHE", "1"); +} + +void tst_qmlcachegen::loadGeneratedFile() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile("test.qml", "import QtQml 2.0\n" + "QtObject {\n" + " property int value: Math.min(100, 42);\n" + "}"); + + QVERIFY(generateCache(testFilePath)); + + const QString cacheFilePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(cacheFilePath)); + QVERIFY(QFile::remove(testFilePath)); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 42); +} + +void tst_qmlcachegen::translationExpressionSupport() +{ + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + const QString testFilePath = writeTempFile("test.qml", "import QtQml.Models 2.2\n" + "import QtQml 2.2\n" + "QtObject {\n" + " property ListModel model: ListModel {\n" + " ListElement {\n" + " text: qsTr(\"All\")\n" + " }\n" + " ListElement {\n" + " text: QT_TR_NOOP(\"Ok\")\n" + " }\n" + " }\n" + " property string text: model.get(0).text + \" \" + model.get(1).text\n" + "}"); + + + QVERIFY(generateCache(testFilePath)); + + const QString cacheFilePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(cacheFilePath)); + QVERIFY(QFile::remove(testFilePath)); + + QQmlEngine engine; + CleanlyLoadingComponent component(&engine, QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("text").toString(), QString("All Ok")); +} + +QTEST_GUILESS_MAIN(tst_qmlcachegen) + +#include "tst_qmlcachegen.moc" diff --git a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp index b265607fd1..3402aeebc1 100644 --- a/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp +++ b/tests/auto/qml/qmldiskcache/tst_qmldiskcache.cpp @@ -58,6 +58,7 @@ private slots: void localAliases(); void cacheResources(); void stableOrderOfDependentCompositeTypes(); + void singletonDependency(); }; // A wrapper around QQmlComponent to ensure the temporary reference counts @@ -174,7 +175,7 @@ struct TestCompiler { QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = v4->iselFactory->createUnitForLoading(); - return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), v4->iselFactory.data(), &lastErrorString); + return unit->loadFromDisk(QUrl::fromLocalFile(testFilePath), QFileInfo(testFilePath).lastModified(), v4->iselFactory.data(), &lastErrorString); } void closeMapping() @@ -737,6 +738,58 @@ void tst_qmldiskcache::stableOrderOfDependentCompositeTypes() } } +void tst_qmldiskcache::singletonDependency() +{ + QScopedPointer<QQmlEngine> engine(new QQmlEngine); + + QTemporaryDir tempDir; + QVERIFY(tempDir.isValid()); + + const auto writeTempFile = [&tempDir](const QString &fileName, const char *contents) { + QFile f(tempDir.path() + '/' + fileName); + const bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); + Q_ASSERT(ok); + f.write(contents); + return f.fileName(); + }; + + writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 42 }"); + writeTempFile("qmldir", "singleton MySingleton 1.0 MySingleton.qml"); + const QString testFilePath = writeTempFile("main.qml", "import QtQml 2.0\nimport \".\"\nQtObject {\n" + " property int value: MySingleton.value\n" + "}"); + + { + CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 42); + } + + const QString testFileCachePath = testFilePath + QLatin1Char('c'); + QVERIFY(QFile::exists(testFileCachePath)); + QDateTime initialCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); + + engine.reset(new QQmlEngine); + waitForFileSystem(); + + writeTempFile("MySingleton.qml", "import QtQml 2.0\npragma Singleton\nQtObject { property int value: 100 }"); + waitForFileSystem(); + + { + CleanlyLoadingComponent component(engine.data(), QUrl::fromLocalFile(testFilePath)); + QScopedPointer<QObject> obj(component.create()); + QVERIFY(!obj.isNull()); + QCOMPARE(obj->property("value").toInt(), 100); + } + + { + QVERIFY(QFile::exists(testFileCachePath)); + QDateTime newCacheTimeStamp = QFileInfo(testFileCachePath).lastModified(); + QVERIFY2(newCacheTimeStamp > initialCacheTimeStamp, qPrintable(newCacheTimeStamp.toString())); + } +} + QTEST_MAIN(tst_qmldiskcache) #include "tst_qmldiskcache.moc" diff --git a/tests/auto/qml/qmlmin/tst_qmlmin.cpp b/tests/auto/qml/qmlmin/tst_qmlmin.cpp index 3ed0aa7446..171c2bda8a 100644 --- a/tests/auto/qml/qmlmin/tst_qmlmin.cpp +++ b/tests/auto/qml/qmlmin/tst_qmlmin.cpp @@ -29,7 +29,9 @@ #include <qtest.h> #include <QLibraryInfo> #include <QDir> +#if QT_CONFIG(process) #include <QProcess> +#endif #include <QDebug> #include <QQmlError> #include <cstdlib> @@ -42,7 +44,7 @@ public: private slots: void initTestCase(); -#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled +#if QT_CONFIG(process) && !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled void qmlMinify_data(); void qmlMinify(); #endif @@ -82,6 +84,7 @@ void tst_qmlmin::initTestCase() excludedDirs << "doc/src/snippets/qtquick1/qtbinding"; excludedDirs << "doc/src/snippets/qtquick1/imports"; excludedDirs << "tests/manual/v4"; + excludedDirs << "tests/auto/qml/ecmascripttests"; excludedDirs << "tests/auto/qml/qmllint"; // Add invalid files (i.e. files with syntax errors) @@ -166,7 +169,7 @@ Examples are any .qml files under the examples/ directory that start with a lower case letter. */ -#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled +#if QT_CONFIG(process) && !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled void tst_qmlmin::qmlMinify_data() { QTest::addColumn<QString>("file"); @@ -183,7 +186,7 @@ void tst_qmlmin::qmlMinify_data() } #endif -#if !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled +#if QT_CONFIG(process) && !defined(QTEST_CROSS_COMPILED) // sources not available when cross compiled void tst_qmlmin::qmlMinify() { QFETCH(QString, file); diff --git a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp index 838966e2a0..68e11e3551 100644 --- a/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp +++ b/tests/auto/qml/qmlplugindump/tst_qmlplugindump.cpp @@ -105,12 +105,12 @@ void tst_qmlplugindump::singleton() args << QLatin1String("tests.dumper.CompositeSingleton") << QLatin1String("1.0") << QLatin1String("."); dumper.start(qmlplugindumpPath, args); - dumper.waitForFinished(); + QVERIFY2(dumper.waitForStarted(), qPrintable(dumper.errorString())); + QVERIFY2(dumper.waitForFinished(), qPrintable(dumper.errorString())); const QString &result = dumper.readAllStandardOutput(); - qDebug() << "result: " << result; - QVERIFY(result.contains(QLatin1String("exports: [\"Singleton 1.0\"]"))); - QVERIFY(result.contains(QLatin1String("exportMetaObjectRevisions: [0]"))); + QVERIFY2(result.contains(QLatin1String("exports: [\"Singleton 1.0\"]")), qPrintable(result)); + QVERIFY2(result.contains(QLatin1String("exportMetaObjectRevisions: [0]")), qPrintable(result)); } QTEST_MAIN(tst_qmlplugindump) diff --git a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp index 74add85cb0..f7748b2da9 100644 --- a/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp +++ b/tests/auto/qml/qqmlapplicationengine/tst_qqmlapplicationengine.cpp @@ -29,7 +29,9 @@ #include "../../shared/util.h" #include <QQmlApplicationEngine> #include <QSignalSpy> +#if QT_CONFIG(process) #include <QProcess> +#endif #include <QDebug> class tst_qqmlapplicationengine : public QQmlDataTest diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 91ceed7697..6c9cb331a2 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -8170,6 +8170,8 @@ void tst_qqmlecmascript::stringify_qtbug_50592() QCOMPARE(obj->property("source").toString(), QString::fromLatin1("http://example.org/some_nonexistant_image.png")); } +// Tests for the JS-only instanceof. Tests for the QML extensions for +// instanceof belong in tst_qqmllanguage! void tst_qqmlecmascript::instanceof_data() { QTest::addColumn<QString>("setupCode"); diff --git a/tests/auto/qml/qqmllanguage/data/instanceOf/CustomMouseArea.qml b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomMouseArea.qml new file mode 100644 index 0000000000..f6ec5848c1 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomMouseArea.qml @@ -0,0 +1,6 @@ +import QtQuick 2.6 + +MouseArea { + +} + diff --git a/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangle.qml b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangle.qml new file mode 100644 index 0000000000..b3fa43a671 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangle.qml @@ -0,0 +1,4 @@ +import QtQuick 2.6 + +Rectangle { +} diff --git a/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangleWithProp.qml b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangleWithProp.qml new file mode 100644 index 0000000000..cf566b9315 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceOf/CustomRectangleWithProp.qml @@ -0,0 +1,6 @@ +import QtQuick 2.6 + +Rectangle { + property int somethingCustom: 0 +} + diff --git a/tests/auto/qml/qqmllanguage/data/instanceOf/qmldir b/tests/auto/qml/qqmllanguage/data/instanceOf/qmldir new file mode 100644 index 0000000000..144c93d8e3 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceOf/qmldir @@ -0,0 +1,2 @@ +CustomRectangle 1.0 CustomRectangle.qml +CustomMouseArea 1.0 CustomMouseArea.qml diff --git a/tests/auto/qml/qqmllanguage/data/instanceof_qtqml.qml b/tests/auto/qml/qqmllanguage/data/instanceof_qtqml.qml new file mode 100644 index 0000000000..d74b172cf8 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceof_qtqml.qml @@ -0,0 +1,13 @@ +import QtQml 2.0 + +QtObject { + id: qtobjectInstance + + property Timer aTimer: Timer { + id: timerInstance + } + + property Connections aConnections: Connections { + id: connectionsInstance + } +} diff --git a/tests/auto/qml/qqmllanguage/data/instanceof_qtqml_qualified.qml b/tests/auto/qml/qqmllanguage/data/instanceof_qtqml_qualified.qml new file mode 100644 index 0000000000..a8e303363e --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceof_qtqml_qualified.qml @@ -0,0 +1,13 @@ +import QtQml 2.0 as QmlImport + +QmlImport.QtObject { + id: qtobjectInstance + + property QmlImport.Timer aTimer: QmlImport.Timer { + id: timerInstance + } + + property QmlImport.Connections aConnections: QmlImport.Connections { + id: connectionsInstance + } +} diff --git a/tests/auto/qml/qqmllanguage/data/instanceof_qtquick.qml b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick.qml new file mode 100644 index 0000000000..9c1808d515 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick.qml @@ -0,0 +1,14 @@ +import QtQuick 2.0 + +Item { + id: itemInstance + + Rectangle { + id: rectangleInstance + } + + MouseArea { + id: mouseAreaInstance + } +} + diff --git a/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite.qml b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite.qml new file mode 100644 index 0000000000..78fc112805 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite.qml @@ -0,0 +1,26 @@ +import QtQuick 2.0 +import "instanceOf" + +Item { + id: itemInstance + + Rectangle { + id: rectangleInstance + } + + MouseArea { + id: mouseAreaInstance + } + + CustomRectangle { + id: customRectangleInstance + } + CustomRectangleWithProp { + id: customRectangleWithPropInstance + } + CustomMouseArea { + id: customMouseAreaInstance + } +} + + diff --git a/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite_qualified.qml b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite_qualified.qml new file mode 100644 index 0000000000..97361b7334 --- /dev/null +++ b/tests/auto/qml/qqmllanguage/data/instanceof_qtquick_composite_qualified.qml @@ -0,0 +1,27 @@ +import QtQuick 2.0 as QuickImport +import "instanceOf" as CustomImport + +QuickImport.Item { + id: itemInstance + + QuickImport.Rectangle { + id: rectangleInstance + } + + QuickImport.MouseArea { + id: mouseAreaInstance + } + + CustomImport.CustomRectangle { + id: customRectangleInstance + } + CustomImport.CustomRectangleWithProp { + id: customRectangleWithPropInstance + } + CustomImport.CustomMouseArea { + id: customMouseAreaInstance + } +} + + + diff --git a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp index 750c32cc3c..e67fa18309 100644 --- a/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp +++ b/tests/auto/qml/qqmllanguage/tst_qqmllanguage.cpp @@ -263,6 +263,9 @@ private slots: void qmlTypeCanBeResolvedByName_data(); void qmlTypeCanBeResolvedByName(); + void instanceof_data(); + void instanceof(); + private: QQmlEngine engine; QStringList defaultImportPathList; @@ -309,7 +312,7 @@ private: if (!errorfile) { \ if (qgetenv("DEBUG") != "" && !component.errors().isEmpty()) \ qWarning() << "Unexpected Errors:" << component.errors(); \ - QVERIFY(!component.isError()); \ + QVERIFY2(!component.isError(), qPrintable(component.errorString())); \ QVERIFY(component.errors().isEmpty()); \ } else { \ DETERMINE_ERRORS(errorfile,expected,actual);\ @@ -4338,6 +4341,200 @@ void tst_qqmllanguage::qmlTypeCanBeResolvedByName() QVERIFY(!o.isNull()); } +// Tests for the QML-only extensions of instanceof. Tests for the regular JS +// instanceof belong in tst_qqmlecmascript! +void tst_qqmllanguage::instanceof_data() +{ + QTest::addColumn<QUrl>("documentToTestIn"); + QTest::addColumn<QVariant>("expectedValue"); + + // so the way this works is that the name of the test tag defines the test + // to run. + // + // the expectedValue is either a boolean true or false for whether the two + // operands are indeed an instanceof each other, or a string for the + // expected error message. + + // assert that basic types don't convert to QObject + QTest::newRow("1 instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + QTest::newRow("true instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + QTest::newRow("\"foobar\" instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + + // assert that Managed don't either + QTest::newRow("new String(\"foobar\") instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + QTest::newRow("new Object() instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + QTest::newRow("new Date() instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant("TypeError: Type error"); + + // test that simple QtQml comparisons work + QTest::newRow("qtobjectInstance instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(true); + QTest::newRow("qtobjectInstance instanceof Timer") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(false); + QTest::newRow("timerInstance instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(true); + QTest::newRow("timerInstance instanceof Timer") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(true); + QTest::newRow("connectionsInstance instanceof QtObject") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(true); + QTest::newRow("connectionsInstance instanceof Timer") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(false); + QTest::newRow("connectionsInstance instanceof Connections") + << testFileUrl("instanceof_qtqml.qml") + << QVariant(true); + + // make sure they still work when imported with a qualifier + QTest::newRow("qtobjectInstance instanceof QmlImport.QtObject") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(true); + QTest::newRow("qtobjectInstance instanceof QmlImport.Timer") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(false); + QTest::newRow("timerInstance instanceof QmlImport.QtObject") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(true); + QTest::newRow("timerInstance instanceof QmlImport.Timer") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(true); + QTest::newRow("connectionsInstance instanceof QmlImport.QtObject") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(true); + QTest::newRow("connectionsInstance instanceof QmlImport.Timer") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(false); + QTest::newRow("connectionsInstance instanceof QmlImport.Connections") + << testFileUrl("instanceof_qtqml_qualified.qml") + << QVariant(true); + + // test that Quick C++ types work ok + QTest::newRow("itemInstance instanceof QtObject") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(true); + QTest::newRow("itemInstance instanceof Timer") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(false); + QTest::newRow("itemInstance instanceof Rectangle") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(false); + QTest::newRow("rectangleInstance instanceof Item") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(true); + QTest::newRow("rectangleInstance instanceof Rectangle") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(true); + QTest::newRow("rectangleInstance instanceof MouseArea") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(false); + QTest::newRow("mouseAreaInstance instanceof Item") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(true); + QTest::newRow("mouseAreaInstance instanceof Rectangle") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(false); + QTest::newRow("mouseAreaInstance instanceof MouseArea") + << testFileUrl("instanceof_qtquick.qml") + << QVariant(true); + + // test that unqualified quick composite types work ok + QTest::newRow("rectangleInstance instanceof CustomRectangle") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(false); + QTest::newRow("customRectangleInstance instanceof Rectangle") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(true); + QTest::newRow("customRectangleInstance instanceof Item") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(true); + QTest::newRow("customRectangleWithPropInstance instanceof CustomRectangleWithProp") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(true); + QTest::newRow("customRectangleWithPropInstance instanceof CustomRectangle") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(false); // ### XXX: QTBUG-58477 + QTest::newRow("customRectangleWithPropInstance instanceof Rectangle") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(true); + QTest::newRow("customRectangleInstance instanceof MouseArea") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(false); + QTest::newRow("customMouseAreaInstance instanceof MouseArea") + << testFileUrl("instanceof_qtquick_composite.qml") + << QVariant(true); + + // test that they still work when qualified + QTest::newRow("rectangleInstance instanceof CustomImport.CustomRectangle") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(false); + QTest::newRow("customRectangleInstance instanceof QuickImport.Rectangle") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(true); + QTest::newRow("customRectangleInstance instanceof QuickImport.Item") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(true); + QTest::newRow("customRectangleWithPropInstance instanceof CustomImport.CustomRectangleWithProp") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(true); + QTest::newRow("customRectangleWithPropInstance instanceof CustomImport.CustomRectangle") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(false); // ### XXX: QTBUG-58477 + QTest::newRow("customRectangleWithPropInstance instanceof QuickImport.Rectangle") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(true); + QTest::newRow("customRectangleInstance instanceof QuickImport.MouseArea") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(false); + QTest::newRow("customMouseAreaInstance instanceof QuickImport.MouseArea") + << testFileUrl("instanceof_qtquick_composite_qualified.qml") + << QVariant(true); +} + +void tst_qqmllanguage::instanceof() +{ + QFETCH(QUrl, documentToTestIn); + QFETCH(QVariant, expectedValue); + + QQmlEngine engine; + QQmlComponent component(&engine, documentToTestIn); + VERIFY_ERRORS(0); + + QScopedPointer<QObject> o(component.create()); + QVERIFY(o != 0); + + QQmlExpression expr(engine.contextForObject(o.data()), 0, QString::fromLatin1(QTest::currentDataTag())); + QVariant ret = expr.evaluate(); + + if (expectedValue.type() == QVariant::Bool) { + // no error expected + QVERIFY2(!expr.hasError(), qPrintable(expr.error().description())); + bool returnValue = ret.toBool(); + + if (QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomRectangle") || + QTest::currentDataTag() == QLatin1String("customRectangleWithPropInstance instanceof CustomImport.CustomRectangle")) + QEXPECT_FAIL("", "QTBUG-58477: QML type rules are a little lax", Continue); + QCOMPARE(returnValue, expectedValue.toBool()); + } else { + QVERIFY(expr.hasError()); + QCOMPARE(expr.error().description(), expectedValue.toString()); + } +} + QTEST_MAIN(tst_qqmllanguage) #include "tst_qqmllanguage.moc" diff --git a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp index 9fbd719d7b..30e517c8f9 100644 --- a/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp +++ b/tests/auto/qml/qqmlmetatype/tst_qqmlmetatype.cpp @@ -52,6 +52,7 @@ private slots: void qmlPropertyValueInterceptorCast(); void qmlType(); void invalidQmlTypeName(); + void prettyTypeName(); void registrationType(); void compositeType(); void externalEnums(); @@ -72,6 +73,16 @@ public: }; QML_DECLARE_TYPE(TestType); +class TestType2 : public QObject +{ + Q_OBJECT +}; + +class TestType3 : public QObject +{ + Q_OBJECT +}; + class ExternalEnums : public QObject { Q_OBJECT @@ -214,13 +225,28 @@ void tst_qqmlmetatype::invalidQmlTypeName() { QStringList currFailures = QQmlMetaType::typeRegistrationFailures(); QCOMPARE(qmlRegisterType<TestType>("TestNamespace", 1, 0, "Test$Type"), -1); // should fail due to invalid QML type name. + QCOMPARE(qmlRegisterType<TestType>("Test", 1, 0, "EndingInSlash/"), -1); QStringList nowFailures = QQmlMetaType::typeRegistrationFailures(); foreach (const QString &f, currFailures) nowFailures.removeOne(f); - QCOMPARE(nowFailures.size(), 1); + QCOMPARE(nowFailures.size(), 2); QCOMPARE(nowFailures.at(0), QStringLiteral("Invalid QML element name \"Test$Type\"")); + QCOMPARE(nowFailures.at(1), QStringLiteral("Invalid QML element name \"EndingInSlash/\"")); +} + +void tst_qqmlmetatype::prettyTypeName() +{ + TestType2 obj2; + QCOMPARE(QQmlMetaType::prettyTypeName(&obj2), QString("TestType2")); + QVERIFY(qmlRegisterType<TestType2>("Test", 1, 0, "") >= 0); + QCOMPARE(QQmlMetaType::prettyTypeName(&obj2), QString("TestType2")); + + TestType3 obj3; + QCOMPARE(QQmlMetaType::prettyTypeName(&obj3), QString("TestType3")); + QVERIFY(qmlRegisterType<TestType3>("Test", 1, 0, "OtherName") >= 0); + QCOMPARE(QQmlMetaType::prettyTypeName(&obj3), QString("OtherName")); } void tst_qqmlmetatype::isList() diff --git a/tests/auto/qml/v4misc/tst_v4misc.cpp b/tests/auto/qml/v4misc/tst_v4misc.cpp index 88b6ae92a8..55a1f65608 100644 --- a/tests/auto/qml/v4misc/tst_v4misc.cpp +++ b/tests/auto/qml/v4misc/tst_v4misc.cpp @@ -26,6 +26,7 @@ ** ****************************************************************************/ +#include <qhashfunctions.h> #include <qtest.h> #define V4_AUTOTEST @@ -46,17 +47,12 @@ private slots: void moveMapping_2(); }; -QT_BEGIN_NAMESPACE -// Avoid QHash randomization so that the temp numbering is stable. -extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed; // from qhash.cpp -QT_END_NAMESPACE - using namespace QT_PREPEND_NAMESPACE(QV4::IR); void tst_v4misc::initTestCase() { - qt_qhash_seed.store(0); - QCOMPARE(qt_qhash_seed.load(), 0); + qSetGlobalQHashSeed(0); + QCOMPARE(qGlobalQHashSeed(), 0); } // split between two ranges diff --git a/tests/auto/qmltest/BLACKLIST b/tests/auto/qmltest/BLACKLIST index c38347b42a..e0a4ce1743 100644 --- a/tests/auto/qmltest/BLACKLIST +++ b/tests/auto/qmltest/BLACKLIST @@ -9,3 +9,8 @@ linux linux [ListView::test_listInteractiveCurrentIndexEnforce] linux +macos-10.12 +[TextEdit::test_textentry] +macos-10.12 +[TextEdit::test_textentry_char] +macos-10.12 diff --git a/tests/auto/qmltest/selftests/tst_selftests.qml b/tests/auto/qmltest/selftests/tst_selftests.qml index 439ea7a70d..5555876014 100644 --- a/tests/auto/qmltest/selftests/tst_selftests.qml +++ b/tests/auto/qmltest/selftests/tst_selftests.qml @@ -167,6 +167,16 @@ TestCase { caught = true } verify(caught) + + caught = false; + try { + testCase.verify(true, "foo", "bar") + } catch (e) { + compare(e.message, "QtQuickTest::fail") + compare(functions.failmsg, "More than two arguments given to verify(). Did you mean tryVerify() or tryCompare()?") + caught = true + } + verify(caught) } function test_compare() { diff --git a/tests/auto/quick/examples/tst_examples.cpp b/tests/auto/quick/examples/tst_examples.cpp index d5c9aaeb90..b6742b9efe 100644 --- a/tests/auto/quick/examples/tst_examples.cpp +++ b/tests/auto/quick/examples/tst_examples.cpp @@ -29,7 +29,6 @@ #include <qtest.h> #include <QLibraryInfo> #include <QDir> -#include <QProcess> #include <QDebug> #include <QtQuick/QQuickItem> #include <QtQuick/QQuickView> diff --git a/tests/auto/quick/nokeywords/tst_nokeywords.cpp b/tests/auto/quick/nokeywords/tst_nokeywords.cpp index ad77743ddd..e6655589a3 100644 --- a/tests/auto/quick/nokeywords/tst_nokeywords.cpp +++ b/tests/auto/quick/nokeywords/tst_nokeywords.cpp @@ -55,7 +55,6 @@ #include <QtQuick/private/qsgdefaultinternalrectanglenode_p.h> #include <QtQuick/private/qsgdepthstencilbuffer_p.h> #include <QtQuick/private/qsgdistancefieldglyphnode_p.h> -#include <QtQuick/private/qsgdistancefieldutil_p.h> #endif #include <QtQuick/private/qsggeometry_p.h> #include <QtQuick/private/qsgnode_p.h> diff --git a/tests/auto/quick/qquickapplication/BLACKLIST b/tests/auto/quick/qquickapplication/BLACKLIST new file mode 100644 index 0000000000..81592db56f --- /dev/null +++ b/tests/auto/quick/qquickapplication/BLACKLIST @@ -0,0 +1,2 @@ +[active] +osx-10.11 diff --git a/tests/auto/quick/qquickflickable/BLACKLIST b/tests/auto/quick/qquickflickable/BLACKLIST index 647bf819a5..f35397f119 100644 --- a/tests/auto/quick/qquickflickable/BLACKLIST +++ b/tests/auto/quick/qquickflickable/BLACKLIST @@ -17,3 +17,5 @@ osx osx-10.10 [flickVelocity] osx-10.10 +[nestedSliderUsingTouch:keepNeither] +ubuntu-16.04 diff --git a/tests/auto/quick/qquickflickable/data/nestedSlider.qml b/tests/auto/quick/qquickflickable/data/nestedSlider.qml new file mode 100644 index 0000000000..2fd0cbfcc8 --- /dev/null +++ b/tests/auto/quick/qquickflickable/data/nestedSlider.qml @@ -0,0 +1,36 @@ +import QtQuick 2.0 +import Test 1.0 + +Flickable { + width: 240 + height: 320 + contentWidth: width * 1.5 + contentHeight: height * 1.5 + contentY: height * 0.25 + + Rectangle { + id: slider + width: 50 + height: 200 + color: "lightgray" + border.color: drag.active ? "green" : "black" + anchors.centerIn: parent + radius: 4 + + TouchDragArea { + id: drag + objectName: "drag" + anchors.fill: parent + } + + Rectangle { + width: parent.width - 2 + height: 20 + radius: 5 + color: "darkgray" + border.color: "black" + x: 1 + y: Math.min(slider.height - height, Math.max(0, drag.pos.y - height / 2)) + } + } +} diff --git a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp index 9ead271bfe..ef6e444580 100644 --- a/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp +++ b/tests/auto/quick/qquickflickable/tst_qquickflickable.cpp @@ -46,12 +46,107 @@ using namespace QQuickViewTestUtil; using namespace QQuickVisualTestUtil; +// an abstract Slider which only handles touch events +class TouchDragArea : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QPointF pos READ pos NOTIFY posChanged) + Q_PROPERTY(bool active READ active NOTIFY activeChanged) + Q_PROPERTY(bool keepMouseGrab READ keepMouseGrab WRITE setKeepMouseGrab NOTIFY keepMouseGrabChanged) + Q_PROPERTY(bool keepTouchGrab READ keepTouchGrab WRITE setKeepTouchGrab NOTIFY keepTouchGrabChanged) + +public: + TouchDragArea(QQuickItem *parent = 0) + : QQuickItem(parent) + , touchEvents(0) + , touchUpdates(0) + , touchReleases(0) + , ungrabs(0) + , m_active(false) + { } + + QPointF pos() const { return m_pos; } + + bool active() const { return m_active; } + + void setKeepMouseGrab(bool keepMouseGrab) + { + QQuickItem::setKeepMouseGrab(keepMouseGrab); + emit keepMouseGrabChanged(); + } + + void setKeepTouchGrab(bool keepTouchGrab) + { + QQuickItem::setKeepTouchGrab(keepTouchGrab); + emit keepTouchGrabChanged(); + } + + int touchEvents; + int touchUpdates; + int touchReleases; + int ungrabs; + QVector<Qt::TouchPointState> touchPointStates; + +protected: + void touchEvent(QTouchEvent *ev) override + { + QCOMPARE(ev->touchPoints().count(), 1); + auto touchpoint = ev->touchPoints().first(); + switch (touchpoint.state()) { + case Qt::TouchPointPressed: + QVERIFY(!m_active); + m_active = true; + emit activeChanged(); + grabTouchPoints(QVector<int>() << touchpoint.id()); + break; + case Qt::TouchPointMoved: + ++touchUpdates; + break; + case Qt::TouchPointReleased: + QVERIFY(m_active); + m_active = false; + ++touchReleases; + emit activeChanged(); + case Qt::TouchPointStationary: + break; + } + touchPointStates << touchpoint.state(); + ++touchEvents; + m_pos = touchpoint.pos(); + emit posChanged(); + } + + void touchUngrabEvent() override + { + ++ungrabs; + QVERIFY(m_active); + emit ungrabbed(); + m_active = false; + emit activeChanged(); + } + +signals: + void ungrabbed(); + void posChanged(); + void keepMouseGrabChanged(); + void keepTouchGrabChanged(); + void activeChanged(); + +private: + QPointF m_pos; + bool m_active; +}; + class tst_qquickflickable : public QQmlDataTest { Q_OBJECT public: + tst_qquickflickable() + : touchDevice(QTest::createTouchDevice()) + {} private slots: + void initTestCase() override; void create(); void horizontalViewportSize(); void verticalViewportSize(); @@ -89,6 +184,8 @@ private slots: void stopAtBounds(); void stopAtBounds_data(); void nestedMouseAreaUsingTouch(); + void nestedSliderUsingTouch(); + void nestedSliderUsingTouch_data(); void pressDelayWithLoader(); void movementFromProgrammaticFlick(); void cleanup(); @@ -101,9 +198,16 @@ private slots: void overshoot_reentrant(); private: - void flickWithTouch(QQuickWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to); + void flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to); + QTouchDevice *touchDevice; }; +void tst_qquickflickable::initTestCase() +{ + QQmlDataTest::initTestCase(); + qmlRegisterType<TouchDragArea>("Test",1,0,"TouchDragArea"); +} + void tst_qquickflickable::cleanup() { QVERIFY(QGuiApplication::topLevelWindows().isEmpty()); @@ -1530,12 +1634,6 @@ void tst_qquickflickable::clickAndDragWhenTransformed() void tst_qquickflickable::flickTwiceUsingTouches() { - QTouchDevice *touchDevice = new QTouchDevice; - touchDevice->setName("Fake Touchscreen"); - touchDevice->setType(QTouchDevice::TouchScreen); - touchDevice->setCapabilities(QTouchDevice::Position); - QWindowSystemInterface::registerTouchDevice(touchDevice); - QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("longList.qml")); QTRY_COMPARE(window->status(), QQuickView::Ready); @@ -1548,7 +1646,7 @@ void tst_qquickflickable::flickTwiceUsingTouches() QVERIFY(flickable != 0); QCOMPARE(flickable->contentY(), 0.0f); - flickWithTouch(window.data(), touchDevice, QPoint(100, 400), QPoint(100, 240)); + flickWithTouch(window.data(), QPoint(100, 400), QPoint(100, 240)); qreal contentYAfterFirstFlick = flickable->contentY(); qDebug() << "contentYAfterFirstFlick " << contentYAfterFirstFlick; @@ -1556,7 +1654,7 @@ void tst_qquickflickable::flickTwiceUsingTouches() // Wait until view stops moving QTRY_VERIFY(!flickable->isMoving()); - flickWithTouch(window.data(), touchDevice, QPoint(100, 400), QPoint(100, 240)); + flickWithTouch(window.data(), QPoint(100, 400), QPoint(100, 240)); // In the original bug, that second flick would cause Flickable to halt immediately qreal contentYAfterSecondFlick = flickable->contentY(); @@ -1564,7 +1662,7 @@ void tst_qquickflickable::flickTwiceUsingTouches() QTRY_VERIFY(contentYAfterSecondFlick > (contentYAfterFirstFlick + 80.0f)); } -void tst_qquickflickable::flickWithTouch(QQuickWindow *window, QTouchDevice *touchDevice, const QPoint &from, const QPoint &to) +void tst_qquickflickable::flickWithTouch(QQuickWindow *window, const QPoint &from, const QPoint &to) { QTest::touchEvent(window, touchDevice).press(0, from, window); QQuickTouchUtils::flush(window); @@ -1869,12 +1967,6 @@ void tst_qquickflickable::stopAtBounds() void tst_qquickflickable::nestedMouseAreaUsingTouch() { - QTouchDevice *touchDevice = new QTouchDevice; - touchDevice->setName("Fake Touchscreen"); - touchDevice->setType(QTouchDevice::TouchScreen); - touchDevice->setCapabilities(QTouchDevice::Position); - QWindowSystemInterface::registerTouchDevice(touchDevice); - QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("nestedmousearea.qml")); QTRY_COMPARE(window->status(), QQuickView::Ready); @@ -1887,7 +1979,7 @@ void tst_qquickflickable::nestedMouseAreaUsingTouch() QVERIFY(flickable != 0); QCOMPARE(flickable->contentY(), 50.0f); - flickWithTouch(window.data(), touchDevice, QPoint(100, 300), QPoint(100, 200)); + flickWithTouch(window.data(), QPoint(100, 300), QPoint(100, 200)); // flickable should not have moved QCOMPARE(flickable->contentY(), 50.0); @@ -1897,6 +1989,65 @@ void tst_qquickflickable::nestedMouseAreaUsingTouch() QVERIFY(nested->y() < 100.0); } +void tst_qquickflickable::nestedSliderUsingTouch_data() +{ + QTest::addColumn<bool>("keepMouseGrab"); + QTest::addColumn<bool>("keepTouchGrab"); + QTest::addColumn<int>("updates"); + QTest::addColumn<int>("releases"); + QTest::addColumn<int>("ungrabs"); + + QTest::newRow("keepBoth") << true << true << 8 << 1 << 0; + QTest::newRow("keepMouse") << true << false << 8 << 1 << 0; + QTest::newRow("keepTouch") << false << true << 8 << 1 << 0; + QTest::newRow("keepNeither") << false << false << 6 << 0 << 1; +} + +void tst_qquickflickable::nestedSliderUsingTouch() +{ + QFETCH(bool, keepMouseGrab); + QFETCH(bool, keepTouchGrab); + QFETCH(int, updates); + QFETCH(int, releases); + QFETCH(int, ungrabs); + + QQuickView *window = new QQuickView; + QScopedPointer<QQuickView> windowPtr(window); + windowPtr->setSource(testFileUrl("nestedSlider.qml")); + QTRY_COMPARE(window->status(), QQuickView::Ready); + QQuickViewTestUtil::centerOnScreen(window); + QQuickViewTestUtil::moveMouseAway(window); + window->show(); + QVERIFY(QTest::qWaitForWindowActive(window)); + QVERIFY(window->rootObject() != 0); + + QQuickFlickable *flickable = qobject_cast<QQuickFlickable*>(window->rootObject()); + QVERIFY(flickable); + + TouchDragArea *tda = flickable->findChild<TouchDragArea*>("drag"); + QVERIFY(tda); + + // Drag down and a little to the right: flickable will steal the grab only if tda allows it + const int dragThreshold = qApp->styleHints()->startDragDistance(); + tda->setKeepMouseGrab(keepMouseGrab); + tda->setKeepTouchGrab(keepTouchGrab); + QPoint p0 = tda->mapToScene(QPoint(20, 20)).toPoint(); + QTest::touchEvent(window, touchDevice).press(0, p0, window); + QQuickTouchUtils::flush(window); + for (int i = 0; i < 8; ++i) { + p0 += QPoint(dragThreshold / 6, dragThreshold / 4); + QTest::touchEvent(window, touchDevice).move(0, p0, window); + QQuickTouchUtils::flush(window); + } + QCOMPARE(tda->active(), !ungrabs); + QTest::touchEvent(window, touchDevice).release(0, p0, window); + QQuickTouchUtils::flush(window); + QTRY_COMPARE(tda->touchPointStates.first(), Qt::TouchPointPressed); + QTRY_COMPARE(tda->touchUpdates, updates); + QTRY_COMPARE(tda->touchReleases, releases); + QTRY_COMPARE(tda->ungrabs, ungrabs); +} + // QTBUG-31328 void tst_qquickflickable::pressDelayWithLoader() { @@ -2065,6 +2216,7 @@ Q_DECLARE_METATYPE(QQuickFlickable::BoundsBehavior) void tst_qquickflickable::overshoot() { QFETCH(QQuickFlickable::BoundsBehavior, boundsBehavior); + QFETCH(int, boundsMovement); QScopedPointer<QQuickView> window(new QQuickView); window->setSource(testFileUrl("overshoot.qml")); @@ -2081,6 +2233,7 @@ void tst_qquickflickable::overshoot() QCOMPARE(flickable->contentHeight(), 400.0); flickable->setBoundsBehavior(boundsBehavior); + flickable->setBoundsMovement(QQuickFlickable::BoundsMovement(boundsMovement)); // drag past the beginning QTest::mousePress(window.data(), Qt::LeftButton, 0, QPoint(10, 10)); @@ -2089,23 +2242,30 @@ void tst_qquickflickable::overshoot() QTest::mouseMove(window.data(), QPoint(40, 40)); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(50, 50)); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::DragOverBounds)) { + QVERIFY(flickable->property("minContentX").toReal() < 0.0); + QVERIFY(flickable->property("minContentY").toReal() < 0.0); + } else { + QCOMPARE(flickable->property("minContentX").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 0.0); + } if (boundsBehavior & QQuickFlickable::DragOverBounds) { - QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); QVERIFY(flickable->property("minHorizontalOvershoot").toReal() < 0.0); - QCOMPARE(flickable->property("minContentY").toReal(), - flickable->property("minVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("minContentX").toReal(), - flickable->property("minHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); } else { - QCOMPARE(flickable->property("minContentY").toReal(), 0.0); - QCOMPARE(flickable->property("minContentX").toReal(), 0.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + } + if (bool(boundsMovement == QQuickFlickable::FollowBoundsBehavior) == bool(boundsBehavior & QQuickFlickable::DragOverBounds)) { + QCOMPARE(flickable->property("minContentX").toReal(), + flickable->property("minHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("minContentY").toReal(), + flickable->property("minVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("maxContentY").toReal(), 0.0); QCOMPARE(flickable->property("maxContentX").toReal(), 0.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 0.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); flickable->setContentX(20.0); flickable->setContentY(20.0); @@ -2115,23 +2275,30 @@ void tst_qquickflickable::overshoot() flick(window.data(), QPoint(10, 10), QPoint(50, 50), 100); QTRY_VERIFY(!flickable->property("flicking").toBool()); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QVERIFY(flickable->property("minContentX").toReal() < 0.0); + QVERIFY(flickable->property("minContentY").toReal() < 0.0); + } else { + QCOMPARE(flickable->property("minContentX").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 0.0); + } if (boundsBehavior & QQuickFlickable::OvershootBounds) { - QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); QVERIFY(flickable->property("minHorizontalOvershoot").toReal() < 0.0); - QCOMPARE(flickable->property("minContentY").toReal(), - flickable->property("minVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("minContentX").toReal(), - flickable->property("minHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("minVerticalOvershoot").toReal() < 0.0); } else { - QCOMPARE(flickable->property("minContentY").toReal(), 0.0); - QCOMPARE(flickable->property("minContentX").toReal(), 0.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + } + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QCOMPARE(flickable->property("minContentX").toReal(), + flickable->property("minHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("minContentY").toReal(), + flickable->property("minVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("maxContentY").toReal(), 20.0); QCOMPARE(flickable->property("maxContentX").toReal(), 20.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 20.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); flickable->setContentX(200.0); flickable->setContentY(200.0); @@ -2144,23 +2311,30 @@ void tst_qquickflickable::overshoot() QTest::mouseMove(window.data(), QPoint(20, 20)); QTest::mouseRelease(window.data(), Qt::LeftButton, 0, QPoint(10, 10)); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::DragOverBounds)) { + QVERIFY(flickable->property("maxContentX").toReal() > 200.0); + QVERIFY(flickable->property("maxContentX").toReal() > 200.0); + } else { + QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); + } if (boundsBehavior & QQuickFlickable::DragOverBounds) { - QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); QVERIFY(flickable->property("maxHorizontalOvershoot").toReal() > 0.0); - QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, - flickable->property("maxVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, - flickable->property("maxHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); } else { - QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); - QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + } + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::DragOverBounds)) { + QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, + flickable->property("maxHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, + flickable->property("maxVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("minContentY").toReal(), 200.0); QCOMPARE(flickable->property("minContentX").toReal(), 200.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 200.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); flickable->setContentX(180.0); flickable->setContentY(180.0); @@ -2170,37 +2344,59 @@ void tst_qquickflickable::overshoot() flick(window.data(), QPoint(50, 50), QPoint(10, 10), 100); QTRY_VERIFY(!flickable->property("flicking").toBool()); + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) && (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QVERIFY(flickable->property("maxContentX").toReal() > 200.0); + QVERIFY(flickable->property("maxContentY").toReal() > 200.0); + } else { + QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); + QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); + } if (boundsBehavior & QQuickFlickable::OvershootBounds) { - QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); QVERIFY(flickable->property("maxHorizontalOvershoot").toReal() > 0.0); - QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, - flickable->property("maxVerticalOvershoot").toReal()); - QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, - flickable->property("maxHorizontalOvershoot").toReal()); + QVERIFY(flickable->property("maxVerticalOvershoot").toReal() > 0.0); } else { - QCOMPARE(flickable->property("maxContentY").toReal(), 200.0); - QCOMPARE(flickable->property("maxContentX").toReal(), 200.0); - QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); QCOMPARE(flickable->property("maxHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("maxVerticalOvershoot").toReal(), 0.0); + } + if ((boundsMovement == QQuickFlickable::FollowBoundsBehavior) == (boundsBehavior & QQuickFlickable::OvershootBounds)) { + QCOMPARE(flickable->property("maxContentX").toReal() - 200.0, + flickable->property("maxHorizontalOvershoot").toReal()); + QCOMPARE(flickable->property("maxContentY").toReal() - 200.0, + flickable->property("maxVerticalOvershoot").toReal()); } - QCOMPARE(flickable->property("minContentY").toReal(), 180.0); QCOMPARE(flickable->property("minContentX").toReal(), 180.0); - QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minContentY").toReal(), 180.0); QCOMPARE(flickable->property("minHorizontalOvershoot").toReal(), 0.0); + QCOMPARE(flickable->property("minVerticalOvershoot").toReal(), 0.0); } void tst_qquickflickable::overshoot_data() { QTest::addColumn<QQuickFlickable::BoundsBehavior>("boundsBehavior"); - - QTest::newRow("StopAtBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds); - QTest::newRow("DragOverBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds); - QTest::newRow("OvershootBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds); - QTest::newRow("DragAndOvershootBounds") - << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds); + QTest::addColumn<int>("boundsMovement"); + + QTest::newRow("StopAtBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::StopAtBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + QTest::newRow("DragOverBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + QTest::newRow("OvershootBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + QTest::newRow("DragAndOvershootBounds,FollowBoundsBehavior") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) + << int(QQuickFlickable::FollowBoundsBehavior); + + QTest::newRow("DragOverBounds,StopAtBounds") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragOverBounds) + << int(QQuickFlickable::StopAtBounds); + QTest::newRow("OvershootBounds,StopAtBounds") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::OvershootBounds) + << int(QQuickFlickable::StopAtBounds); + QTest::newRow("DragAndOvershootBounds,StopAtBounds") + << QQuickFlickable::BoundsBehavior(QQuickFlickable::DragAndOvershootBounds) + << int(QQuickFlickable::StopAtBounds); } void tst_qquickflickable::overshoot_reentrant() diff --git a/tests/auto/quick/qquickimage/tst_qquickimage.cpp b/tests/auto/quick/qquickimage/tst_qquickimage.cpp index 2681f1a966..36d99ad48d 100644 --- a/tests/auto/quick/qquickimage/tst_qquickimage.cpp +++ b/tests/auto/quick/qquickimage/tst_qquickimage.cpp @@ -375,7 +375,7 @@ void tst_qquickimage::mirror() } QImage img = expected.toImage(); - QCOMPARE(screenshots[fillMode], img); + QCOMPARE(screenshots[fillMode].convertToFormat(img.format()), img); } } diff --git a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST index 1777af9e0c..cab6e2f7bf 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST +++ b/tests/auto/quick/qquickmultipointtoucharea/BLACKLIST @@ -1,2 +1,4 @@ -[inFlickable] -* +[nonOverlapping] +ubuntu-16.04 +[nested] +ubuntu-16.04 diff --git a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp index 2872556a94..87acd67f6a 100644 --- a/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp +++ b/tests/auto/quick/qquickmultipointtoucharea/tst_qquickmultipointtoucharea.cpp @@ -800,7 +800,7 @@ void tst_QQuickMultiPointTouchArea::inFlickable2() QVERIFY(flickable->contentY() < 0); QVERIFY(flickable->isMoving()); - QCOMPARE(point11->pressed(), true); + QCOMPARE(point11->pressed(), false); QTest::touchEvent(window.data(), device).release(0, p1); QQuickTouchUtils::flush(window.data()); diff --git a/tests/auto/quick/qquickpositioners/data/implicitGridOneItem.qml b/tests/auto/quick/qquickpositioners/data/implicitGridOneItem.qml deleted file mode 100644 index 8da9fc270d..0000000000 --- a/tests/auto/quick/qquickpositioners/data/implicitGridOneItem.qml +++ /dev/null @@ -1,12 +0,0 @@ -import QtQuick 2.9 -import PositionerTest 1.0 - -ImplicitGrid { - columns: 2 - rows: 1 - - Text { - text: "Text" - width: parent.width - } -} diff --git a/tests/auto/quick/qquickpositioners/data/implicitRowOneItem.qml b/tests/auto/quick/qquickpositioners/data/implicitRowOneItem.qml deleted file mode 100644 index 25a287cf31..0000000000 --- a/tests/auto/quick/qquickpositioners/data/implicitRowOneItem.qml +++ /dev/null @@ -1,9 +0,0 @@ -import QtQuick 2.9 -import PositionerTest 1.0 - -ImplicitRow { - Text { - text: "Text" - width: parent.width - } -} diff --git a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp index 4c4afb7a7b..1b3939401a 100644 --- a/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp +++ b/tests/auto/quick/qquickpositioners/tst_qquickpositioners.cpp @@ -98,8 +98,6 @@ private slots: void test_attachedproperties(); void test_attachedproperties_data(); void test_attachedproperties_dynamic(); - void test_useImplicitSize_oneItem_data(); - void test_useImplicitSize_oneItem(); void populateTransitions_row(); void populateTransitions_row_data(); @@ -306,8 +304,6 @@ void tst_qquickpositioners::moveTransitions_flow_data() tst_qquickpositioners::tst_qquickpositioners() { - qmlRegisterType<QQuickImplicitRow>("PositionerTest", 1, 0, "ImplicitRow"); - qmlRegisterType<QQuickImplicitGrid>("PositionerTest", 1, 0, "ImplicitGrid"); } void tst_qquickpositioners::test_horizontal() @@ -4008,46 +4004,6 @@ void tst_qquickpositioners::test_attachedproperties_dynamic() } -void tst_qquickpositioners::test_useImplicitSize_oneItem_data() -{ - QTest::addColumn<QString>("positionerType"); - - QTest::newRow("Grid") << "Grid"; - QTest::newRow("Row") << "Row"; -} - -void tst_qquickpositioners::test_useImplicitSize_oneItem() -{ - QFETCH(QString, positionerType); - - QQuickView view; - view.setSource(testFileUrl(QString::fromLatin1("implicit%1OneItem.qml").arg(positionerType))); - QCOMPARE(view.status(), QQuickView::Ready); - view.show(); - QVERIFY(QTest::qWaitForWindowExposed(&view)); - - QQuickItem *positioner = view.rootObject(); - QVERIFY(positioner); - const qreal oldPositionerImplicitWidth = positioner->implicitWidth(); - - QQuickText *text = qobject_cast<QQuickText*>(positioner->childItems().first()); - QVERIFY(text); - const qreal oldTextImplicitWidth = text->implicitWidth(); - QCOMPARE(positioner->implicitWidth(), text->implicitWidth()); - - // Ensure that the implicit size of the positioner changes when the implicit size - // of one of its children changes. - text->setText(QLatin1String("Even More Text")); - const qreal textImplicitWidthIncrease = text->implicitWidth() - oldTextImplicitWidth; - QVERIFY(textImplicitWidthIncrease > 0); - QTRY_COMPARE(positioner->implicitWidth(), oldPositionerImplicitWidth + textImplicitWidthIncrease); - - // Ensure that the implicit size of the positioner does not change when the - // explicit size of one of its children changes. - text->setWidth(10); - QTRY_COMPARE(positioner->implicitWidth(), oldPositionerImplicitWidth + textImplicitWidthIncrease); -} - QQuickView *tst_qquickpositioners::createView(const QString &filename, bool wait) { QQuickView *window = new QQuickView(0); diff --git a/tests/auto/quick/qquickwindow/BLACKLIST b/tests/auto/quick/qquickwindow/BLACKLIST new file mode 100644 index 0000000000..157808fdbf --- /dev/null +++ b/tests/auto/quick/qquickwindow/BLACKLIST @@ -0,0 +1,4 @@ +[requestActivate] +osx-10.11 +[attachedProperty] +osx-10.11 diff --git a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp index 578f737c4d..4fea13eb49 100644 --- a/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp +++ b/tests/auto/quick/qquickwindow/tst_qquickwindow.cpp @@ -28,6 +28,7 @@ #include <qtest.h> #include <QDebug> +#include <QMimeData> #include <QTouchEvent> #include <QtQuick/QQuickItem> #include <QtQuick/QQuickView> @@ -385,6 +386,8 @@ private slots: void grabContentItemToImage(); + void testDragEventPropertyPropagation(); + private: QTouchDevice *touchDevice; QTouchDevice *touchDeviceWithVelocity; @@ -2617,6 +2620,261 @@ void tst_qquickwindow::grabContentItemToImage() QTRY_COMPARE(created->property("success").toInt(), 1); } +class TestDropTarget : public QQuickItem +{ + Q_OBJECT +public: + TestDropTarget(QQuickItem *parent = 0) + : QQuickItem(parent) + , enterDropAction(Qt::CopyAction) + , moveDropAction(Qt::CopyAction) + , dropDropAction(Qt::CopyAction) + , enterAccept(true) + , moveAccept(true) + , dropAccept(true) + { + setFlags(ItemAcceptsDrops); + } + + void reset() + { + enterDropAction = Qt::CopyAction; + moveDropAction = Qt::CopyAction; + dropDropAction = Qt::CopyAction; + enterAccept = true; + moveAccept = true; + dropAccept = true; + } + + void dragEnterEvent(QDragEnterEvent *event) + { + event->setAccepted(enterAccept); + event->setDropAction(enterDropAction); + } + + void dragMoveEvent(QDragMoveEvent *event) + { + event->setAccepted(moveAccept); + event->setDropAction(moveDropAction); + } + + void dropEvent(QDropEvent *event) + { + event->setAccepted(dropAccept); + event->setDropAction(dropDropAction); + } + + Qt::DropAction enterDropAction; + Qt::DropAction moveDropAction; + Qt::DropAction dropDropAction; + bool enterAccept; + bool moveAccept; + bool dropAccept; +}; + +class DragEventTester { +public: + DragEventTester() + : pos(60, 60) + , actions(Qt::CopyAction | Qt::MoveAction | Qt::LinkAction) + , buttons(Qt::LeftButton) + , modifiers(Qt::NoModifier) + { + } + + ~DragEventTester() { + qDeleteAll(events); + events.clear(); + enterEvent = 0; + moveEvent = 0; + dropEvent = 0; + leaveEvent = 0; + } + + void addEnterEvent() + { + enterEvent = new QDragEnterEvent(pos, actions, &data, buttons, modifiers); + events.append(enterEvent); + } + + void addMoveEvent() + { + moveEvent = new QDragMoveEvent(pos, actions, &data, buttons, modifiers, QEvent::DragMove); + events.append(moveEvent); + } + + void addDropEvent() + { + dropEvent = new QDropEvent(pos, actions, &data, buttons, modifiers, QEvent::Drop); + events.append(dropEvent); + } + + void addLeaveEvent() + { + leaveEvent = new QDragLeaveEvent(); + events.append(leaveEvent); + } + + void sendDragEventSequence(QQuickWindow *window) const { + for (int i = 0; i < events.size(); ++i) { + QCoreApplication::sendEvent(window, events[i]); + } + } + + // Used for building events. + QMimeData data; + QPoint pos; + Qt::DropActions actions; + Qt::MouseButtons buttons; + Qt::KeyboardModifiers modifiers; + + // Owns events. + QList<QEvent *> events; + + // Non-owner pointers for easy acccess. + QDragEnterEvent *enterEvent; + QDragMoveEvent *moveEvent; + QDropEvent *dropEvent; + QDragLeaveEvent *leaveEvent; +}; + +void tst_qquickwindow::testDragEventPropertyPropagation() +{ + QQuickWindow window; + TestDropTarget dropTarget(window.contentItem()); + + // Setting the size is important because the QQuickWindow checks if the drag happened inside + // the drop target. + dropTarget.setSize(QSizeF(100, 100)); + + // Test enter events property propagation. + // For enter events, only isAccepted gets propagated. + { + DragEventTester builder; + dropTarget.enterAccept = false; + dropTarget.enterDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragEnterEvent* enterEvent = builder.enterEvent; + QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept); + } + { + DragEventTester builder; + dropTarget.enterAccept = false; + dropTarget.enterDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragEnterEvent* enterEvent = builder.enterEvent; + QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept); + } + { + DragEventTester builder; + dropTarget.enterAccept = true; + dropTarget.enterDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragEnterEvent* enterEvent = builder.enterEvent; + QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept); + } + { + DragEventTester builder; + dropTarget.enterAccept = true; + dropTarget.enterDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragEnterEvent* enterEvent = builder.enterEvent; + QCOMPARE(enterEvent->isAccepted(), dropTarget.enterAccept); + } + + // Test move events property propagation. + // For move events, both isAccepted and dropAction get propagated. + dropTarget.reset(); + { + DragEventTester builder; + dropTarget.moveAccept = false; + dropTarget.moveDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragMoveEvent* moveEvent = builder.moveEvent; + QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept); + QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction); + } + { + DragEventTester builder; + dropTarget.moveAccept = false; + dropTarget.moveDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragMoveEvent* moveEvent = builder.moveEvent; + QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept); + QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction); + } + { + DragEventTester builder; + dropTarget.moveAccept = true; + dropTarget.moveDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragMoveEvent* moveEvent = builder.moveEvent; + QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept); + QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction); + } + { + DragEventTester builder; + dropTarget.moveAccept = true; + dropTarget.moveDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addLeaveEvent(); + builder.sendDragEventSequence(&window); + QDragMoveEvent* moveEvent = builder.moveEvent; + QCOMPARE(moveEvent->isAccepted(), dropTarget.moveAccept); + QCOMPARE(moveEvent->dropAction(), dropTarget.moveDropAction); + } + + // Test drop events property propagation. + // For drop events, both isAccepted and dropAction get propagated. + dropTarget.reset(); + { + DragEventTester builder; + dropTarget.dropAccept = false; + dropTarget.dropDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent(); + builder.sendDragEventSequence(&window); + QDropEvent* dropEvent = builder.dropEvent; + QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept); + QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction); + } + { + DragEventTester builder; + dropTarget.dropAccept = false; + dropTarget.dropDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent(); + builder.sendDragEventSequence(&window); + QDropEvent* dropEvent = builder.dropEvent; + QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept); + QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction); + } + { + DragEventTester builder; + dropTarget.dropAccept = true; + dropTarget.dropDropAction = Qt::IgnoreAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent(); + builder.sendDragEventSequence(&window); + QDropEvent* dropEvent = builder.dropEvent; + QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept); + QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction); + } + { + DragEventTester builder; + dropTarget.dropAccept = true; + dropTarget.dropDropAction = Qt::CopyAction; + builder.addEnterEvent(); builder.addMoveEvent(); builder.addDropEvent(); + builder.sendDragEventSequence(&window); + QDropEvent* dropEvent = builder.dropEvent; + QCOMPARE(dropEvent->isAccepted(), dropTarget.dropAccept); + QCOMPARE(dropEvent->dropAction(), dropTarget.dropDropAction); + } +} + QTEST_MAIN(tst_qquickwindow) #include "tst_qquickwindow.moc" diff --git a/tests/auto/quick/touchmouse/tst_touchmouse.cpp b/tests/auto/quick/touchmouse/tst_touchmouse.cpp index b02d60d0c5..6e7fce6189 100644 --- a/tests/auto/quick/touchmouse/tst_touchmouse.cpp +++ b/tests/auto/quick/touchmouse/tst_touchmouse.cpp @@ -71,7 +71,7 @@ Q_SIGNALS: public: EventItem(QQuickItem *parent = 0) - : QQuickItem(parent), acceptMouse(false), acceptTouch(false), filterTouch(false), point0(-1) + : QQuickItem(parent), touchUngrabCount(0), acceptMouse(false), acceptTouch(false), filterTouch(false), point0(-1) { setAcceptedMouseButtons(Qt::LeftButton); } @@ -111,11 +111,17 @@ public: eventList.append(Event(QEvent::UngrabMouse, QPoint(0,0), QPoint(0,0))); } + void touchUngrabEvent() + { + ++touchUngrabCount; + } + bool event(QEvent *event) { return QQuickItem::event(event); } QList<Event> eventList; + int touchUngrabCount; bool acceptMouse; bool acceptTouch; bool filterTouch; // when used as event filter @@ -158,6 +164,7 @@ private slots: void mouseOverTouch(); void buttonOnFlickable(); + void touchButtonOnFlickable(); void buttonOnDelayedPressFlickable_data(); void buttonOnDelayedPressFlickable(); void buttonOnTouch(); @@ -568,9 +575,10 @@ void tst_TouchMouse::buttonOnFlickable() QCOMPARE(pointerEvent->point(0)->exclusiveGrabber(), eventItem1); QCOMPARE(window->mouseGrabberItem(), eventItem1); - p1 += QPoint(0, -10); - QPoint p2 = p1 + QPoint(0, -10); - QPoint p3 = p2 + QPoint(0, -10); + int dragDelta = -qApp->styleHints()->startDragDistance(); + p1 += QPoint(0, dragDelta); + QPoint p2 = p1 + QPoint(0, dragDelta); + QPoint p3 = p2 + QPoint(0, dragDelta); QQuickTouchUtils::flush(window.data()); QTest::touchEvent(window.data(), device).move(0, p1, window.data()); QQuickTouchUtils::flush(window.data()); @@ -593,6 +601,66 @@ void tst_TouchMouse::buttonOnFlickable() QQuickTouchUtils::flush(window.data()); } +void tst_TouchMouse::touchButtonOnFlickable() +{ + // flickable - height 500 / 1000 + // - eventItem1 y: 100, height 100 + // - eventItem2 y: 300, height 100 + + QScopedPointer<QQuickView> window(createView()); + window->setSource(testFileUrl("buttononflickable.qml")); + window->show(); + QQuickViewTestUtil::centerOnScreen(window.data()); + QVERIFY(QTest::qWaitForWindowActive(window.data())); + QVERIFY(window->rootObject() != 0); + + QQuickFlickable *flickable = window->rootObject()->findChild<QQuickFlickable*>("flickable"); + QVERIFY(flickable); + + EventItem *eventItem2 = window->rootObject()->findChild<EventItem*>("eventItem2"); + QVERIFY(eventItem2); + QCOMPARE(eventItem2->eventList.size(), 0); + eventItem2->acceptTouch = true; + + // press via touch, then drag: check that flickable moves and that the button gets ungrabbed + QCOMPARE(eventItem2->eventList.size(), 0); + QPoint p1 = QPoint(10, 310); + QTest::touchEvent(window.data(), device).press(0, p1, window.data()); + QQuickTouchUtils::flush(window.data()); + QCOMPARE(eventItem2->eventList.size(), 1); + QCOMPARE(eventItem2->eventList.at(0).type, QEvent::TouchBegin); + + QQuickWindowPrivate *windowPriv = QQuickWindowPrivate::get(window.data()); + QVERIFY(windowPriv->touchMouseId == -1); + auto pointerEvent = QQuickPointerDevice::touchDevices().at(0)->pointerEvent(); + QCOMPARE(pointerEvent->point(0)->grabberItem(), eventItem2); + QCOMPARE(window->mouseGrabberItem(), nullptr); + + int dragDelta = qApp->styleHints()->startDragDistance() * -0.7; + p1 += QPoint(0, dragDelta); + QPoint p2 = p1 + QPoint(0, dragDelta); + QPoint p3 = p2 + QPoint(0, dragDelta); + + QQuickTouchUtils::flush(window.data()); + QTest::touchEvent(window.data(), device).move(0, p1, window.data()); + QQuickTouchUtils::flush(window.data()); + QTest::touchEvent(window.data(), device).move(0, p2, window.data()); + QQuickTouchUtils::flush(window.data()); + QTest::touchEvent(window.data(), device).move(0, p3, window.data()); + QQuickTouchUtils::flush(window.data()); + + QVERIFY(eventItem2->eventList.size() > 2); + QCOMPARE(eventItem2->eventList.at(1).type, QEvent::TouchUpdate); + QCOMPARE(eventItem2->touchUngrabCount, 1); + QCOMPARE(window->mouseGrabberItem(), flickable); + QVERIFY(windowPriv->touchMouseId != -1); + QCOMPARE(pointerEvent->point(0)->grabberItem(), flickable); + QVERIFY(flickable->isMovingVertically()); + + QTest::touchEvent(window.data(), device).release(0, p3, window.data()); + QQuickTouchUtils::flush(window.data()); +} + void tst_TouchMouse::buttonOnDelayedPressFlickable_data() { QTest::addColumn<bool>("scrollBeforeDelayIsOver"); diff --git a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp index 5e8f762e23..bd051ec990 100644 --- a/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp +++ b/tests/auto/quickwidgets/qquickwidget/tst_qquickwidget.cpp @@ -58,6 +58,7 @@ private slots: void grabBeforeShow(); void reparentToNewWindow(); void nullEngine(); + void keyEvents(); }; @@ -337,6 +338,33 @@ void tst_qquickwidget::nullEngine() QVERIFY(widget.engine()); } +class KeyHandlingWidget : public QQuickWidget +{ +public: + void keyPressEvent(QKeyEvent *e) override { + if (e->key() == Qt::Key_A) + ok = true; + } + + bool ok = false; +}; + +void tst_qquickwidget::keyEvents() +{ + // A QQuickWidget should behave like a normal widget when it comes to event handling. + // Verify that key events actually reach the widget. (QTBUG-45757) + KeyHandlingWidget widget; + widget.setSource(testFileUrl("rectangle.qml")); + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(widget.window(), 5000)); + + // Note: send the event to the QWindow, not the QWidget, in order + // to simulate the full event processing chain. + QTest::keyClick(widget.window()->windowHandle(), Qt::Key_A); + + QTRY_VERIFY(widget.ok); +} + QTEST_MAIN(tst_qquickwidget) #include "tst_qquickwidget.moc" diff --git a/tests/auto/toolsupport/tst_toolsupport.cpp b/tests/auto/toolsupport/tst_toolsupport.cpp index 9a11a67e65..6ee1db805a 100644 --- a/tests/auto/toolsupport/tst_toolsupport.cpp +++ b/tests/auto/toolsupport/tst_toolsupport.cpp @@ -101,7 +101,7 @@ void tst_toolsupport::offsets_data() = QTest::newRow("CompiledData::CompilationUnit::data") << pmm_to_offsetof(&QV4::CompiledData::CompilationUnit::data); - data << 8 << 16; + data << 12 << 24; } { @@ -109,7 +109,7 @@ void tst_toolsupport::offsets_data() = QTest::newRow("CompiledData::CompilationUnit::runtimeStrings") << pmm_to_offsetof(&QV4::CompiledData::CompilationUnit::runtimeStrings); - data << 16 << 32; + data << 0 << 0; } { diff --git a/tests/benchmarks/qml/holistic/testtypes.h b/tests/benchmarks/qml/holistic/testtypes.h index 0736982373..a752a8585b 100644 --- a/tests/benchmarks/qml/holistic/testtypes.h +++ b/tests/benchmarks/qml/holistic/testtypes.h @@ -43,6 +43,7 @@ #include <QtQml/qjsvalue.h> #include <QtQml/qqmlscriptstring.h> #include <QtQml/qqmlcomponent.h> +#include <QtCore/qregexp.h> class MyQmlAttachedObject : public QObject { diff --git a/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp b/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp index 886cfc6599..6da0799bbc 100644 --- a/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp +++ b/tests/manual/scenegraph_lancelot/scenegrabber/main.cpp @@ -29,6 +29,7 @@ #include <QtCore/QTimer> #include <QtCore/QDebug> #include <QtCore/QFileInfo> +#include <QtCore/QHashFunctions> #include <QtGui/QGuiApplication> #include <QtGui/QImage> @@ -134,11 +135,9 @@ private: }; -Q_CORE_EXPORT extern QBasicAtomicInt qt_qhash_seed; - int main(int argc, char *argv[]) { - qt_qhash_seed = 0; + qSetGlobalQHashSeed(0); QGuiApplication a(argc, argv); diff --git a/tests/manual/v4/test262 b/tests/manual/v4/test262 deleted file mode 160000 -Subproject 9741ac4655808ac46c127e3d1d8ba3d27ada618 diff --git a/tests/manual/v4/tests.pro b/tests/manual/v4/tests.pro deleted file mode 100644 index ce4a34f7a0..0000000000 --- a/tests/manual/v4/tests.pro +++ /dev/null @@ -1,15 +0,0 @@ -TEMPLATE = aux - -TESTSCRIPT=$$PWD/test262.py -isEmpty(V4CMD): V4CMD = qmljs - -checktarget.target = check -checktarget.commands = python $$TESTSCRIPT --command=$$V4CMD --parallel --with-test-expectations --update-expectations -checktarget.depends = all -QMAKE_EXTRA_TARGETS += checktarget - -checkmothtarget.target = check-interpreter -checkmothtarget.commands = python $$TESTSCRIPT --command=\"$$V4CMD --interpret\" --parallel --with-test-expectations -checkmothtarget.depends = all -QMAKE_EXTRA_TARGETS += checkmothtarget - diff --git a/tools/qml/main.cpp b/tools/qml/main.cpp index 1699d3d81b..3b20fba5d4 100644 --- a/tools/qml/main.cpp +++ b/tools/qml/main.cpp @@ -55,7 +55,9 @@ #include <QLibraryInfo> #include <qqml.h> #include <qqmldebug.h> +#if QT_CONFIG(animation) #include <private/qabstractanimation_p.h> +#endif #include <cstdio> #include <cstring> @@ -476,10 +478,12 @@ int main(int argc, char *argv[]) break; else if (arg == QLatin1String("-verbose")) verboseMode = true; +#if QT_CONFIG(animation) else if (arg == QLatin1String("-slow-animations")) QUnifiedTimer::instance()->setSlowModeEnabled(true); else if (arg == QLatin1String("-fixed-animations")) QUnifiedTimer::instance()->setConsistentTiming(true); +#endif else if (arg == QLatin1String("-I")) { if (i+1 == argList.count()) continue;//Invalid usage, but just ignore it @@ -567,7 +571,7 @@ int main(int argc, char *argv[]) loadDummyDataFiles(e, dummyDir); for (const QString &path : qAsConst(files)) { - QUrl url = QUrl::fromUserInput(path, QDir::currentPath()); + QUrl url = QUrl::fromUserInput(path, QDir::currentPath(), QUrl::AssumeLocalFile); if (verboseMode) printf("qml: loading %s\n", qPrintable(url.toString())); QByteArray strippedFile; diff --git a/tools/qmlcachegen/qmlcache.prf b/tools/qmlcachegen/qmlcache.prf index fed9f0d2f3..4470db0d09 100644 --- a/tools/qmlcachegen/qmlcache.prf +++ b/tools/qmlcachegen/qmlcache.prf @@ -1,12 +1,55 @@ -qtPrepareTool(QML_CACHEGEN, qmlcachegen) +static { + message("QML cache generation ahead of time is not supported in static builds") + return() +} + +qtPrepareTool(QML_CACHEGEN, qmlcachegen, _ARCH_CHECK) + +isEmpty(TARGETPATH): error("Must set TARGETPATH (QML import name) for ahead-of-time QML cache generation") !isEmpty(QT_TARGET_ARCH):QML_CACHEGEN_ARCH=$$QT_TARGET_ARCH else:QML_CACHEGEN_ARCH=$$QT_ARCH -qmlcachegen.input = QML_FILES -qmlcachegen.output = ${QMAKE_FILE_IN}c -qmlcachegen.commands = $$QML_CACHEGEN --target-architecture=$$QML_CACHEGEN_ARCH ${QMAKE_FILE_IN} +!isEmpty(QT_TARGET_BUILDABI):QML_CACHEGEN_ABI=$$QT_TARGET_BUILDABI +else:QML_CACHEGEN_ABI=$$QT_BUILDABI + +QML_CACHEGEN_ARGS=--target-architecture=$$QML_CACHEGEN_ARCH --target-abi=$$QML_CACHEGEN_ABI + +!system($$QML_CACHEGEN_ARCH_CHECK $$QML_CACHEGEN_ARGS --check-if-supported) { + message("QML cache generation requested but target architecture $$QML_CACHEGEN_ARCH is not supported.") + return() +} + +load(qt_build_paths) + +prefix_build: QMLCACHE_DESTDIR = $$MODULE_BASE_OUTDIR/qml/$$TARGETPATH +else: QMLCACHE_DESTDIR = $$[QT_INSTALL_QML]/$$TARGETPATH + +CACHEGEN_FILES= +qmlcacheinst.files = +for(qmlf, QML_FILES) { + contains(qmlf,.*\\.js$)|contains(qmlf,.*\\.qml$) { + CACHEGEN_FILES += $$absolute_path($$qmlf, $$_PRO_FILE_PWD_) + qmlcacheinst.files += $$QMLCACHE_DESTDIR/$$relative_path($$qmlf, $$_PRO_FILE_PWD_)c + } +} + +defineReplace(qmlCacheOutputFileName) { + return($$relative_path($$QMLCACHE_DESTDIR/$$relative_path($$1, $$_PRO_FILE_PWD_)c, $$OUT_PWD)) +} + +qmlcacheinst.base = $$QMLCACHE_DESTDIR +qmlcacheinst.path = $$[QT_INSTALL_QML]/$$TARGETPATH +qmlcacheinst.CONFIG = no_check_exist + +qmlcachegen.input = CACHEGEN_FILES +qmlcachegen.output = ${QMAKE_FUNC_FILE_IN_qmlCacheOutputFileName} +qmlcachegen.CONFIG = no_link target_predeps +qmlcachegen.commands = $$QML_CACHEGEN $$QML_CACHEGEN_ARGS -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN} qmlcachegen.name = Generate QML Cache ${QMAKE_FILE_IN} -qmlcachegen.variable_out = AUX_QML_FILES +qmlcachegen.variable_out = GENERATED_FILES -QMAKE_EXTRA_COMPILERS += qmlcachegen +!debug_and_release|!build_all|CONFIG(release, debug|release) { + QMAKE_EXTRA_COMPILERS += qmlcachegen + INSTALLS += qmlcacheinst +} diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp index 977c5b6ff1..b201176d5e 100644 --- a/tools/qmlcachegen/qmlcachegen.cpp +++ b/tools/qmlcachegen/qmlcachegen.cpp @@ -32,13 +32,14 @@ #include <QFile> #include <QFileInfo> #include <QDateTime> +#include <QHashFunctions> #include <private/qqmlirbuilder_p.h> #include <private/qv4isel_moth_p.h> #include <private/qqmljsparser_p.h> +#include <private/qv4jssimplifier_p.h> QT_BEGIN_NAMESPACE -extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed; namespace QV4 { namespace JIT { Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture); @@ -80,9 +81,43 @@ QString diagnosticErrorMessage(const QString &fileName, const QQmlJS::Diagnostic return message; } -static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *iselFactory, Error *error) +// Ensure that ListElement objects keep all property assignments in their string form +static void annotateListElements(QmlIR::Document *document) +{ + QStringList listElementNames; + + foreach (const QV4::CompiledData::Import *import, document->imports) { + const QString uri = document->stringAt(import->uriIndex); + if (uri != QStringLiteral("QtQml.Models") && uri != QStringLiteral("QtQuick")) + continue; + + QString listElementName = QStringLiteral("ListElement"); + const QString qualifier = document->stringAt(import->qualifierIndex); + if (!qualifier.isEmpty()) { + listElementName.prepend(QLatin1Char('.')); + listElementName.prepend(qualifier); + } + listElementNames.append(listElementName); + } + + if (listElementNames.isEmpty()) + return; + + foreach (QmlIR::Object *object, document->objects) { + if (!listElementNames.contains(document->stringAt(object->inheritedTypeNameIndex))) + continue; + for (QmlIR::Binding *binding = object->firstBinding(); binding; binding = binding->next) { + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + binding->stringIndex = document->registerString(object->bindingAsString(document, binding->value.compiledScriptIndex)); + } + } +} + +static bool compileQmlFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, const QString &targetABI, Error *error) { QmlIR::Document irDocument(/*debugMode*/false); + irDocument.jsModule.targetABI = targetABI; QString sourceCode; { @@ -96,7 +131,6 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i error->message = QLatin1String("Error reading from ") + inputFileName + QLatin1Char(':') + f.errorString(); return false; } - irDocument.jsModule.sourceTimeStamp = QFileInfo(f).lastModified().toMSecsSinceEpoch(); } { @@ -112,8 +146,10 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i } } + annotateListElements(&irDocument); + { - QmlIR::JSCodeGen v4CodeGen(inputFileName, irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable); + QmlIR::JSCodeGen v4CodeGen(/*empty input file name*/QString(), irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable); for (QmlIR::Object *object: qAsConst(irDocument.objects)) { if (object->functionsAndExpressions->count == 0) continue; @@ -139,21 +175,22 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i QmlIR::QmlUnitGenerator generator; - // ### translation binding simplification + { + QQmlJavaScriptBindingExpressionSimplificationPass pass(irDocument.objects, &irDocument.jsModule, &irDocument.jsGenerator); + pass.reduceTranslationBindings(); + } QV4::ExecutableAllocator allocator; QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator)); // Disable lookups in non-standalone (aka QML) mode isel->setUseFastLookups(false); irDocument.javaScriptCompilationUnit = isel->compile(/*generate unit*/false); - // ### - QV4::CompiledData::ResolvedTypeReferenceMap dummyDependencies; - QV4::CompiledData::Unit *unit = generator.generate(irDocument, /*engine*/nullptr, dummyDependencies); + QV4::CompiledData::Unit *unit = generator.generate(irDocument); unit->flags |= QV4::CompiledData::Unit::StaticData; unit->flags |= QV4::CompiledData::Unit::PendingTypeCompilation; irDocument.javaScriptCompilationUnit->data = unit; - if (!irDocument.javaScriptCompilationUnit->saveToDisk(inputFileName, &error->message)) + if (!irDocument.javaScriptCompilationUnit->saveToDisk(outputFileName, &error->message)) return false; free(unit); @@ -161,9 +198,10 @@ static bool compileQmlFile(const QString &inputFileName, QV4::EvalISelFactory *i return true; } -static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *iselFactory, Error *error) +static bool compileJSFile(const QString &inputFileName, const QString &outputFileName, QV4::EvalISelFactory *iselFactory, const QString &targetABI, Error *error) { QmlIR::Document irDocument(/*debugMode*/false); + irDocument.jsModule.targetABI = targetABI; QString sourceCode; { @@ -177,7 +215,6 @@ static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *is error->message = QLatin1String("Error reading from ") + inputFileName + QLatin1Char(':') + f.errorString(); return false; } - irDocument.jsModule.sourceTimeStamp = QFileInfo(f).lastModified().toMSecsSinceEpoch(); } QQmlJS::Engine *engine = &irDocument.jsParserEngine; @@ -217,7 +254,7 @@ static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *is { QmlIR::JSCodeGen v4CodeGen(inputFileName, irDocument.code, &irDocument.jsModule, &irDocument.jsParserEngine, irDocument.program, /*import cache*/0, &irDocument.jsGenerator.stringTable); - v4CodeGen.generateFromProgram(inputFileName, sourceCode, program, &irDocument.jsModule, QQmlJS::Codegen::GlobalCode); + v4CodeGen.generateFromProgram(/*empty input file name*/QString(), sourceCode, program, &irDocument.jsModule, QQmlJS::Codegen::GlobalCode); QList<QQmlJS::DiagnosticMessage> jsErrors = v4CodeGen.errors(); if (!jsErrors.isEmpty()) { for (const QQmlJS::DiagnosticMessage &e: qAsConst(jsErrors)) { @@ -231,19 +268,16 @@ static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *is QmlIR::QmlUnitGenerator generator; - // ### translation binding simplification - - QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, /*executable allocator*/nullptr, &irDocument.jsModule, &irDocument.jsGenerator)); + QV4::ExecutableAllocator allocator; + QScopedPointer<QV4::EvalInstructionSelection> isel(iselFactory->create(/*engine*/nullptr, &allocator, &irDocument.jsModule, &irDocument.jsGenerator)); // Disable lookups in non-standalone (aka QML) mode isel->setUseFastLookups(false); irDocument.javaScriptCompilationUnit = isel->compile(/*generate unit*/false); - // ### - QV4::CompiledData::ResolvedTypeReferenceMap dummyDependencies; - QV4::CompiledData::Unit *unit = generator.generate(irDocument, /*engine*/nullptr, dummyDependencies); + QV4::CompiledData::Unit *unit = generator.generate(irDocument); unit->flags |= QV4::CompiledData::Unit::StaticData; irDocument.javaScriptCompilationUnit->data = unit; - if (!irDocument.javaScriptCompilationUnit->saveToDisk(inputFileName, &error->message)) { + if (!irDocument.javaScriptCompilationUnit->saveToDisk(outputFileName, &error->message)) { engine->setDirectives(oldDirs); return false; } @@ -257,7 +291,7 @@ static bool compileJSFile(const QString &inputFileName, QV4::EvalISelFactory *is int main(int argc, char **argv) { // Produce reliably the same output for the same input by disabling QHash's random seeding. - qt_qhash_seed.testAndSetRelaxed(-1, 0); + qSetGlobalQHashSeed(0); QCoreApplication app(argc, argv); QCoreApplication::setApplicationName(QStringLiteral("qmlcachegen")); @@ -270,37 +304,65 @@ int main(int argc, char **argv) QCommandLineOption targetArchitectureOption(QStringLiteral("target-architecture"), QCoreApplication::translate("main", "Target architecture"), QCoreApplication::translate("main", "architecture")); parser.addOption(targetArchitectureOption); + QCommandLineOption targetABIOption(QStringLiteral("target-abi"), QCoreApplication::translate("main", "Target architecture binary interface"), QCoreApplication::translate("main", "abi")); + parser.addOption(targetABIOption); + + QCommandLineOption outputFileOption(QStringLiteral("o"), QCoreApplication::translate("main", "Output file name"), QCoreApplication::translate("main", "file name")); + parser.addOption(outputFileOption); + + QCommandLineOption checkIfSupportedOption(QStringLiteral("check-if-supported"), QCoreApplication::translate("main", "Check if cache generate is supported on the specified target architecture")); + parser.addOption(checkIfSupportedOption); + parser.addPositionalArgument(QStringLiteral("[qml file]"), QStringLiteral("QML source file to generate cache for.")); parser.process(app); - const QStringList sources = parser.positionalArguments(); - if (sources.isEmpty()){ + if (!parser.isSet(targetArchitectureOption)) { + fprintf(stderr, "Target architecture not specified. Please specify with --target-architecture=<arch>\n"); parser.showHelp(); - } else if (sources.count() > 1) { - fprintf(stderr, "%s\n", qPrintable(QStringLiteral("Too many input files specified: '") + sources.join(QStringLiteral("' '")) + QLatin1Char('\''))); return EXIT_FAILURE; } - const QString inputFile = sources.first(); QScopedPointer<QV4::EvalISelFactory> isel; const QString targetArchitecture = parser.value(targetArchitectureOption); isel.reset(QV4::JIT::createISelForArchitecture(targetArchitecture)); + if (parser.isSet(checkIfSupportedOption)) { + if (isel.isNull()) + return EXIT_FAILURE; + else + return EXIT_SUCCESS; + } + + const QStringList sources = parser.positionalArguments(); + if (sources.isEmpty()){ + parser.showHelp(); + } else if (sources.count() > 1) { + fprintf(stderr, "%s\n", qPrintable(QStringLiteral("Too many input files specified: '") + sources.join(QStringLiteral("' '")) + QLatin1Char('\''))); + return EXIT_FAILURE; + } + const QString inputFile = sources.first(); + if (!isel) isel.reset(new QV4::Moth::ISelFactory); Error error; + QString outputFileName = inputFile + QLatin1Char('c'); + if (parser.isSet(outputFileOption)) + outputFileName = parser.value(outputFileOption); + + const QString targetABI = parser.value(targetABIOption); + if (inputFile.endsWith(QLatin1String(".qml"))) { - if (!compileQmlFile(inputFile, isel.data(), &error)) { + if (!compileQmlFile(inputFile, outputFileName, isel.data(), targetABI, &error)) { error.augment(QLatin1String("Error compiling qml file: ")).print(); return EXIT_FAILURE; } } else if (inputFile.endsWith(QLatin1String(".js"))) { - if (!compileJSFile(inputFile, isel.data(), &error)) { + if (!compileJSFile(inputFile, outputFileName, isel.data(), targetABI, &error)) { error.augment(QLatin1String("Error compiling qml file: ")).print(); return EXIT_FAILURE; } diff --git a/tools/qmlcachegen/qmlcachegen.pro b/tools/qmlcachegen/qmlcachegen.pro index 81783d0396..25afc2860d 100644 --- a/tools/qmlcachegen/qmlcachegen.pro +++ b/tools/qmlcachegen/qmlcachegen.pro @@ -6,19 +6,9 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII SOURCES = qmlcachegen.cpp TARGET = qmlcachegen -BUILD_INTEGRATION = qmlcache.prf -!force_independent { - qmake_integration.input = BUILD_INTEGRATION - qmake_integration.output = $$[QT_HOST_DATA]/mkspecs/features/${QMAKE_FILE_BASE}.prf - qmake_integration.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} - qmake_integration.name = COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT} - qmake_integration.CONFIG = no_clean no_link - !contains(TEMPLATE, vc.*): qmake_integration.variable_out = GENERATED_FILES - QMAKE_EXTRA_COMPILERS += qmake_integration -} - -qmake_integration_installs.files = $$BUILD_INTEGRATION -qmake_integration_installs.path = $$[QT_HOST_DATA]/mkspecs/features -INSTALLS += qmake_integration_installs +build_integration.files = qmlcache.prf +build_integration.path = $$[QT_HOST_DATA]/mkspecs/features +prefix_build: INSTALLS += build_integration +else: COPIES += build_integration load(qt_tool) diff --git a/tools/qmljs/qmljs.cpp b/tools/qmljs/qmljs.cpp index 081e0660d5..182547490d 100644 --- a/tools/qmljs/qmljs.cpp +++ b/tools/qmljs/qmljs.cpp @@ -157,7 +157,7 @@ int main(int argc, char *argv[]) if (cache && QFile::exists(fn + QLatin1Char('c'))) { QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit = iSelFactory->createUnitForLoading(); QString error; - if (unit->loadFromDisk(QUrl::fromLocalFile(fn), iSelFactory, &error)) { + if (unit->loadFromDisk(QUrl::fromLocalFile(fn), QFileInfo(fn).lastModified(), iSelFactory, &error)) { script.reset(new QV4::Script(&vm, nullptr, unit)); } else { std::cout << "Error loading" << qPrintable(fn) << "from disk cache:" << qPrintable(error) << std::endl; diff --git a/tools/qmlprofiler/qmlprofilerdata.cpp b/tools/qmlprofiler/qmlprofilerdata.cpp index 668cb3ce2d..bcda0bb7fe 100644 --- a/tools/qmlprofiler/qmlprofilerdata.cpp +++ b/tools/qmlprofiler/qmlprofilerdata.cpp @@ -33,6 +33,7 @@ #include <QHash> #include <QFile> #include <QXmlStreamReader> +#include <QRegularExpression> #include <limits> @@ -231,10 +232,10 @@ void QmlProfilerData::addQmlEvent(QQmlProfilerDefinitions::RangeType type, if (!data.isEmpty()) { details = data.join(QLatin1Char(' ')).replace( QLatin1Char('\n'), QLatin1Char(' ')).simplified(); - QRegExp rewrite(QStringLiteral("\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)")); - bool match = rewrite.exactMatch(details); - if (match) { - details = rewrite.cap(1) +QLatin1String(": ") + rewrite.cap(3); + QRegularExpression rewrite(QStringLiteral("^\\(function \\$(\\w+)\\(\\) \\{ (return |)(.+) \\}\\)$")); + QRegularExpressionMatch match = rewrite.match(details); + if (match.hasMatch()) { + details = match.captured(1) +QLatin1String(": ") + match.captured(3); } if (details.startsWith(QLatin1String("file://"))) details = details.mid(details.lastIndexOf(QLatin1Char('/')) + 1); diff --git a/tools/qmlscene/main.cpp b/tools/qmlscene/main.cpp index 1e8d91a95b..6ce676456c 100644 --- a/tools/qmlscene/main.cpp +++ b/tools/qmlscene/main.cpp @@ -33,6 +33,7 @@ #include <QtCore/qpointer.h> #include <QtCore/qscopedpointer.h> #include <QtCore/qtextstream.h> +#include <QtCore/qregularexpression.h> #include <QtGui/QGuiApplication> #include <QtGui/QOpenGLFunctions> @@ -132,6 +133,17 @@ void RenderStatistics::printTotalStats() struct Options { + enum QmlApplicationType + { + QmlApplicationTypeGui, + QmlApplicationTypeWidget, +#ifdef QT_WIDGETS_LIB + DefaultQmlApplicationType = QmlApplicationTypeWidget +#else + DefaultQmlApplicationType = QmlApplicationTypeGui +#endif + }; + Options() : originalQml(false) , originalQmlRaster(false) @@ -145,6 +157,7 @@ struct Options , resizeViewToRootItem(false) , multisample(false) , verbose(false) + , applicationType(DefaultQmlApplicationType) { // QtWebEngine needs a shared context in order for the GPU thread to // upload textures. @@ -166,6 +179,7 @@ struct Options bool verbose; QVector<Qt::ApplicationAttribute> applicationAttributes; QString translationFile; + QmlApplicationType applicationType; }; #if defined(QMLSCENE_BUNDLE) @@ -258,8 +272,8 @@ static bool checkVersion(const QUrl &url) return false; } - QRegExp quick1("^\\s*import +QtQuick +1\\.\\w*"); - QRegExp qt47("^\\s*import +Qt +4\\.7"); + QRegularExpression quick1("^\\s*import +QtQuick +1\\.\\w*"); + QRegularExpression qt47("^\\s*import +Qt +4\\.7"); QTextStream stream(&f); bool codeFound= false; @@ -269,10 +283,11 @@ static bool checkVersion(const QUrl &url) codeFound = true; } else { QString import; - if (quick1.indexIn(line) >= 0) - import = quick1.cap(0).trimmed(); - else if (qt47.indexIn(line) >= 0) - import = qt47.cap(0).trimmed(); + QRegularExpressionMatch match = quick1.match(line); + if (match.hasMatch()) + import = match.captured(0).trimmed(); + else if ((match = qt47.match(line)).hasMatch()) + import = match.captured(0).trimmed(); if (!import.isNull()) { fprintf(stderr, "qmlscene: '%s' is no longer supported.\n" @@ -290,15 +305,17 @@ static bool checkVersion(const QUrl &url) static void displayFileDialog(Options *options) { #if defined(QT_WIDGETS_LIB) && QT_CONFIG(filedialog) - QString fileName = QFileDialog::getOpenFileName(0, "Open QML file", QString(), "QML Files (*.qml)"); - if (!fileName.isEmpty()) { - QFileInfo fi(fileName); - options->url = QUrl::fromLocalFile(fi.canonicalFilePath()); + if (options->applicationType == Options::QmlApplicationTypeWidget) { + QString fileName = QFileDialog::getOpenFileName(0, "Open QML file", QString(), "QML Files (*.qml)"); + if (!fileName.isEmpty()) { + QFileInfo fi(fileName); + options->url = QUrl::fromLocalFile(fi.canonicalFilePath()); + } + return; } -#else +#endif // QT_WIDGETS_LIB && QT_CONFIG(filedialog) Q_UNUSED(options); puts("No filename specified..."); -#endif } #if QT_CONFIG(translation) @@ -354,6 +371,9 @@ static void usage() puts(" --scaling..........................Enable High DPI scaling (AA_EnableHighDpiScaling)"); puts(" --no-scaling.......................Disable High DPI scaling (AA_DisableHighDpiScaling)"); puts(" --verbose..........................Print version and graphical diagnostics for the run-time"); +#ifdef QT_WIDGETS_LIB + puts(" --apptype [gui|widgets] ...........Select which application class to use. Default is widgets."); +#endif puts(" -I <path> ........................ Add <path> to the list of import paths"); puts(" -P <path> ........................ Add <path> to the list of plugin paths"); puts(" -translation <translationfile> ... Set the language to run in"); @@ -443,30 +463,38 @@ int main(int argc, char ** argv) // Parse arguments for application attributes to be applied before Q[Gui]Application creation. for (int i = 1; i < argc; ++i) { const char *arg = argv[i]; - if (!qstrcmp(arg, "--disable-context-sharing")) + if (!qstrcmp(arg, "--disable-context-sharing")) { options.applicationAttributes.removeAll(Qt::AA_ShareOpenGLContexts); - else if (!qstrcmp(arg, "--gles")) + } else if (!qstrcmp(arg, "--gles")) { options.applicationAttributes.append(Qt::AA_UseOpenGLES); - else if (!qstrcmp(arg, "--software")) + } else if (!qstrcmp(arg, "--software")) { options.applicationAttributes.append(Qt::AA_UseSoftwareOpenGL); - else if (!qstrcmp(arg, "--desktop")) + } else if (!qstrcmp(arg, "--desktop")) { options.applicationAttributes.append(Qt::AA_UseDesktopOpenGL); - else if (!qstrcmp(arg, "--scaling")) + } else if (!qstrcmp(arg, "--scaling")) { options.applicationAttributes.append(Qt::AA_EnableHighDpiScaling); - else if (!qstrcmp(arg, "--no-scaling")) + } else if (!qstrcmp(arg, "--no-scaling")) { options.applicationAttributes.append(Qt::AA_DisableHighDpiScaling); + } else if (!qstrcmp(arg, "--apptype")) { + if (++i >= argc) + usage(); + if (!qstrcmp(argv[i], "gui")) + options.applicationType = Options::QmlApplicationTypeGui; + } } for (Qt::ApplicationAttribute a : qAsConst(options.applicationAttributes)) QCoreApplication::setAttribute(a); + QScopedPointer<QGuiApplication> app; #ifdef QT_WIDGETS_LIB - QApplication app(argc, argv); -#else - QGuiApplication app(argc, argv); + if (options.applicationType == Options::QmlApplicationTypeWidget) + app.reset(new QApplication(argc, argv)); #endif - app.setApplicationName("QtQmlViewer"); - app.setOrganizationName("QtProject"); - app.setOrganizationDomain("qt-project.org"); + if (app.isNull()) + app.reset(new QGuiApplication(argc, argv)); + QCoreApplication::setApplicationName(QStringLiteral("QtQmlViewer")); + QCoreApplication::setOrganizationName(QStringLiteral("QtProject")); + QCoreApplication::setOrganizationDomain(QStringLiteral("qt-project.org")); QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); const QStringList arguments = QCoreApplication::arguments(); @@ -501,6 +529,8 @@ int main(int argc, char ** argv) imports.append(arguments.at(++i)); else if (lowerArgument == QLatin1String("-p") && i + 1 < size) pluginPaths.append(arguments.at(++i)); + else if (lowerArgument == QLatin1String("--apptype")) + ++i; // Consume previously parsed argument else if (lowerArgument == QLatin1String("--help") || lowerArgument == QLatin1String("-help") || lowerArgument == QLatin1String("--h") @@ -514,14 +544,14 @@ int main(int argc, char ** argv) QTranslator qtTranslator; QString sysLocale = QLocale::system().name(); if (qtTranslator.load(QLatin1String("qt_") + sysLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) - app.installTranslator(&qtTranslator); + app->installTranslator(&qtTranslator); if (translator.load(QLatin1String("qmlscene_") + sysLocale, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) - app.installTranslator(&translator); + app->installTranslator(&translator); QTranslator qmlTranslator; if (!options.translationFile.isEmpty()) { if (qmlTranslator.load(options.translationFile)) { - app.installTranslator(&qmlTranslator); + app->installTranslator(&qmlTranslator); } else { fprintf(stderr, "Could not load the translation file \"%s\"\n", qPrintable(options.translationFile)); @@ -629,7 +659,7 @@ int main(int argc, char ** argv) // Now would be a good time to inform the debug service to start listening. - exitCode = app.exec(); + exitCode = app->exec(); #ifdef QML_RUNTIME_TESTING RenderStatistics::printTotalStats(); diff --git a/tools/tools.pro b/tools/tools.pro index bf39a649df..20a3600fb8 100644 --- a/tools/tools.pro +++ b/tools/tools.pro @@ -2,15 +2,16 @@ TEMPLATE = subdirs QT_FOR_CONFIG += qml-private SUBDIRS += \ qmlmin \ - qmlimportscanner \ - qmlcachegen + qmlimportscanner + +qtConfig(commandlineparser): SUBDIRS += qmlcachegen !android|android_app { SUBDIRS += \ qml \ qmllint - qtConfig(qml-network):!contains(QT_CONFIG, no-qml-debug): SUBDIRS += qmlprofiler + qtConfig(qml-profiler): SUBDIRS += qmlprofiler qtHaveModule(quick) { !static: { @@ -18,7 +19,7 @@ SUBDIRS += \ qmlscene \ qmltime - qtConfig(regularexpression) { + qtConfig(regularexpression):qtConfig(process) { SUBDIRS += \ qmlplugindump } |