diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2017-03-01 11:54:30 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2017-03-01 11:54:35 +0100 |
commit | 87aebe39ae80c0752695b21b5b7508d220035b85 (patch) | |
tree | a60bd61dd81b5e79532762ae014cb7612d619da2 /src | |
parent | a48244d5aa1d14c286b7cd39afebcfff9c9dcb60 (diff) | |
parent | afec9016d0fd51345ea93a1bbadb99b5c3fdf629 (diff) |
Merge remote-tracking branch 'origin/dev' into wip/scenegraphng
Change-Id: Iba540adaeffb0098fc4e1923050eb611bf47287b
Diffstat (limited to 'src')
216 files changed, 7643 insertions, 3935 deletions
diff --git a/src/3rdparty/masm/assembler/ARM64Assembler.h b/src/3rdparty/masm/assembler/ARM64Assembler.h index ad5acdbb85..7390997af1 100644 --- a/src/3rdparty/masm/assembler/ARM64Assembler.h +++ b/src/3rdparty/masm/assembler/ARM64Assembler.h @@ -26,9 +26,10 @@ #ifndef ARM64Assembler_h #define ARM64Assembler_h -#if ENABLE(ASSEMBLER) && CPU(ARM64) +#if ENABLE(ASSEMBLER) && (CPU(ARM64) || defined(V4_BOOTSTRAP)) #include "AssemblerBuffer.h" +#include "AbstractMacroAssembler.h" #include <limits.h> #include <wtf/Assertions.h> #include <wtf/Vector.h> @@ -520,8 +521,8 @@ typedef enum { #undef DECLARE_REGISTER } FPRegisterID; -static constexpr bool isSp(RegisterID reg) { return reg == sp; } -static constexpr bool isZr(RegisterID reg) { return reg == zr; } +static Q_DECL_CONSTEXPR bool isSp(RegisterID reg) { return reg == sp; } +static Q_DECL_CONSTEXPR bool isZr(RegisterID reg) { return reg == zr; } } // namespace ARM64Registers @@ -530,15 +531,15 @@ public: typedef ARM64Registers::RegisterID RegisterID; typedef ARM64Registers::FPRegisterID FPRegisterID; - static constexpr RegisterID firstRegister() { return ARM64Registers::x0; } - static constexpr RegisterID lastRegister() { return ARM64Registers::sp; } + static Q_DECL_CONSTEXPR RegisterID firstRegister() { return ARM64Registers::x0; } + static Q_DECL_CONSTEXPR RegisterID lastRegister() { return ARM64Registers::sp; } - static constexpr FPRegisterID firstFPRegister() { return ARM64Registers::q0; } - static constexpr FPRegisterID lastFPRegister() { return ARM64Registers::q31; } + static Q_DECL_CONSTEXPR FPRegisterID firstFPRegister() { return ARM64Registers::q0; } + static Q_DECL_CONSTEXPR FPRegisterID lastFPRegister() { return ARM64Registers::q31; } private: - static constexpr bool isSp(RegisterID reg) { return ARM64Registers::isSp(reg); } - static constexpr bool isZr(RegisterID reg) { return ARM64Registers::isZr(reg); } + static Q_DECL_CONSTEXPR bool isSp(RegisterID reg) { return ARM64Registers::isSp(reg); } + static Q_DECL_CONSTEXPR bool isZr(RegisterID reg) { return ARM64Registers::isZr(reg); } public: ARM64Assembler() @@ -546,7 +547,7 @@ public: , m_indexOfTailOfLastWatchpoint(INT_MIN) { } - + AssemblerBuffer& buffer() { return m_buffer; } // (HS, LO, HI, LS) -> (AE, B, A, BE) @@ -653,9 +654,7 @@ public: } void operator=(const LinkRecord& other) { - data.copyTypes.content[0] = other.data.copyTypes.content[0]; - data.copyTypes.content[1] = other.data.copyTypes.content[1]; - data.copyTypes.content[2] = other.data.copyTypes.content[2]; + data.realTypes = other.data.realTypes; } intptr_t from() const { return data.realTypes.m_from; } void setFrom(intptr_t from) { data.realTypes.m_from = from; } @@ -671,8 +670,8 @@ public: private: union { struct RealTypes { - intptr_t m_from : 48; - intptr_t m_to : 48; + int64_t m_from : 48; + int64_t m_to : 48; JumpType m_type : 8; JumpLinkType m_linkType : 8; Condition m_condition : 4; @@ -680,10 +679,6 @@ public: RegisterID m_compareRegister : 6; bool m_is64Bit : 1; } realTypes; - struct CopyTypes { - uint64_t content[3]; - } copyTypes; - COMPILE_ASSERT(sizeof(RealTypes) == sizeof(CopyTypes), LinkRecordCopyStructSizeEqualsRealStruct); } data; }; @@ -743,6 +738,89 @@ public: return isValidSignedImm9(offset); } + + // Jump: + // + // A jump object is a reference to a jump instruction that has been planted + // into the code buffer - it is typically used to link the jump, setting the + // relative offset such that when executed it will jump to the desired + // destination. + template <typename LabelType> + class Jump { + template<class TemplateAssemblerType> + friend class AbstractMacroAssembler; + friend class Call; + template <typename, template <typename> class> friend class LinkBufferBase; + public: + Jump() + { + } + + Jump(AssemblerLabel jmp, ARM64Assembler::JumpType type = ARM64Assembler::JumpNoCondition, ARM64Assembler::Condition condition = ARM64Assembler::ConditionInvalid) + : m_label(jmp) + , m_type(type) + , m_condition(condition) + { + } + + Jump(AssemblerLabel jmp, ARM64Assembler::JumpType type, ARM64Assembler::Condition condition, bool is64Bit, ARM64Assembler::RegisterID compareRegister) + : m_label(jmp) + , m_type(type) + , m_condition(condition) + , m_is64Bit(is64Bit) + , m_compareRegister(compareRegister) + { + ASSERT((type == ARM64Assembler::JumpCompareAndBranch) || (type == ARM64Assembler::JumpCompareAndBranchFixedSize)); + } + + Jump(AssemblerLabel jmp, ARM64Assembler::JumpType type, ARM64Assembler::Condition condition, unsigned bitNumber, ARM64Assembler::RegisterID compareRegister) + : m_label(jmp) + , m_type(type) + , m_condition(condition) + , m_bitNumber(bitNumber) + , m_compareRegister(compareRegister) + { + ASSERT((type == ARM64Assembler::JumpTestBit) || (type == ARM64Assembler::JumpTestBitFixedSize)); + } + + LabelType label() const + { + LabelType result; + result.m_label = m_label; + return result; + } + + void link(AbstractMacroAssembler<ARM64Assembler>* masm) const + { + if ((m_type == ARM64Assembler::JumpCompareAndBranch) || (m_type == ARM64Assembler::JumpCompareAndBranchFixedSize)) + masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition, m_is64Bit, m_compareRegister); + else if ((m_type == ARM64Assembler::JumpTestBit) || (m_type == ARM64Assembler::JumpTestBitFixedSize)) + masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition, m_bitNumber, m_compareRegister); + else + masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition); + } + + void linkTo(LabelType label, AbstractMacroAssembler<ARM64Assembler>* masm) const + { + if ((m_type == ARM64Assembler::JumpCompareAndBranch) || (m_type == ARM64Assembler::JumpCompareAndBranchFixedSize)) + masm->m_assembler.linkJump(m_label, label.label(), m_type, m_condition, m_is64Bit, m_compareRegister); + else if ((m_type == ARM64Assembler::JumpTestBit) || (m_type == ARM64Assembler::JumpTestBitFixedSize)) + masm->m_assembler.linkJump(m_label, label.label(), m_type, m_condition, m_bitNumber, m_compareRegister); + else + masm->m_assembler.linkJump(m_label, label.label(), m_type, m_condition); + } + + bool isSet() const { return m_label.isSet(); } + + private: + AssemblerLabel m_label; + ARM64Assembler::JumpType m_type; + ARM64Assembler::Condition m_condition; + bool m_is64Bit; + unsigned m_bitNumber; + ARM64Assembler::RegisterID m_compareRegister; + }; + private: int encodeFPImm(double d) { @@ -2857,11 +2935,11 @@ public: expected = disassembleMoveWideImediate(address + 1, sf, opc, hw, imm16, rd); ASSERT_UNUSED(expected, expected && sf && opc == MoveWideOp_K && hw == 1 && rd == rdFirst); - result |= static_cast<uintptr_t>(imm16) << 16; + result |= static_cast<uint64_t>(imm16) << 16; expected = disassembleMoveWideImediate(address + 2, sf, opc, hw, imm16, rd); ASSERT_UNUSED(expected, expected && sf && opc == MoveWideOp_K && hw == 2 && rd == rdFirst); - result |= static_cast<uintptr_t>(imm16) << 32; + result |= static_cast<uint64_t>(imm16) << 32; return reinterpret_cast<void*>(result); } @@ -2932,7 +3010,10 @@ public: static void cacheFlush(void* code, size_t size) { -#if OS(IOS) +#if defined(V4_BOOTSTRAP) + UNUSED_PARAM(code) + UNUSED_PARAM(size) +#elif OS(IOS) sys_cache_control(kCacheFunctionPrepareForExecution, code, size); #elif OS(LINUX) size_t page = pageSize(); @@ -2989,7 +3070,7 @@ public: case JumpCondition: { ASSERT(!(reinterpret_cast<intptr_t>(from) & 0x3)); ASSERT(!(reinterpret_cast<intptr_t>(to) & 0x3)); - intptr_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from)); + int64_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from)); if (((relative << 43) >> 43) == relative) return LinkJumpConditionDirect; @@ -2999,7 +3080,7 @@ public: case JumpCompareAndBranch: { ASSERT(!(reinterpret_cast<intptr_t>(from) & 0x3)); ASSERT(!(reinterpret_cast<intptr_t>(to) & 0x3)); - intptr_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from)); + int64_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from)); if (((relative << 43) >> 43) == relative) return LinkJumpCompareAndBranchDirect; @@ -3009,7 +3090,7 @@ public: case JumpTestBit: { ASSERT(!(reinterpret_cast<intptr_t>(from) & 0x3)); ASSERT(!(reinterpret_cast<intptr_t>(to) & 0x3)); - intptr_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from)); + int64_t relative = reinterpret_cast<intptr_t>(to) - (reinterpret_cast<intptr_t>(from)); if (((relative << 50) >> 50) == relative) return LinkJumpTestBitDirect; @@ -3121,7 +3202,7 @@ private: { ASSERT(!(reinterpret_cast<intptr_t>(from) & 3)); ASSERT(!(reinterpret_cast<intptr_t>(to) & 3)); - intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2; + int64_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2; ASSERT(((offset << 38) >> 38) == offset); bool useDirect = ((offset << 45) >> 45) == offset; // Fits in 19 bits @@ -3142,7 +3223,7 @@ private: { ASSERT(!(reinterpret_cast<intptr_t>(from) & 3)); ASSERT(!(reinterpret_cast<intptr_t>(to) & 3)); - intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2; + int64_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2; ASSERT(((offset << 38) >> 38) == offset); bool useDirect = ((offset << 45) >> 45) == offset; // Fits in 19 bits @@ -3163,7 +3244,7 @@ private: { ASSERT(!(reinterpret_cast<intptr_t>(from) & 3)); ASSERT(!(reinterpret_cast<intptr_t>(to) & 3)); - intptr_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2; + int64_t offset = (reinterpret_cast<intptr_t>(to) - reinterpret_cast<intptr_t>(from)) >> 2; ASSERT(static_cast<int>(offset) == offset); ASSERT(((offset << 38) >> 38) == offset); @@ -3766,6 +3847,8 @@ private: #undef DATASIZE #undef MEMOPSIZE #undef CHECK_FP_MEMOP_DATASIZE +#undef JUMP_ENUM_WITH_SIZE +#undef JUMP_ENUM_SIZE #endif // ENABLE(ASSEMBLER) && CPU(ARM64) diff --git a/src/3rdparty/masm/assembler/ARMv7Assembler.h b/src/3rdparty/masm/assembler/ARMv7Assembler.h index f0fa07a1bf..615c72fc15 100644 --- a/src/3rdparty/masm/assembler/ARMv7Assembler.h +++ b/src/3rdparty/masm/assembler/ARMv7Assembler.h @@ -27,10 +27,11 @@ #ifndef ARMAssembler_h #define ARMAssembler_h -#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) +#if ENABLE(ASSEMBLER) && (CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)) #include "AssemblerBuffer.h" #include "MacroAssemblerCodeRef.h" +#include "AbstractMacroAssembler.h" #include <wtf/Assertions.h> #include <wtf/Vector.h> #include <stdint.h> @@ -491,8 +492,8 @@ public: private: union { struct RealTypes { - intptr_t m_from : 31; - intptr_t m_to : 31; + int32_t m_from : 31; + int32_t m_to : 31; JumpType m_type : 8; JumpLinkType m_linkType : 8; Condition m_condition : 16; @@ -510,6 +511,56 @@ public: { } + + // Jump: + // + // A jump object is a reference to a jump instruction that has been planted + // into the code buffer - it is typically used to link the jump, setting the + // relative offset such that when executed it will jump to the desired + // destination. + template <typename LabelType> + class Jump { + template<class TemplateAssemblerType> friend class AbstractMacroAssembler; + friend class Call; + template <typename, template <typename> class> friend class LinkBufferBase;; + public: + Jump() + { + } + + // Fixme: this information should be stored in the instruction stream, not in the Jump object. + Jump(AssemblerLabel jmp, ARMv7Assembler::JumpType type = ARMv7Assembler::JumpNoCondition, ARMv7Assembler::Condition condition = ARMv7Assembler::ConditionInvalid) + : m_label(jmp) + , m_type(type) + , m_condition(condition) + { + } + + LabelType label() const + { + LabelType result; + result.m_label = m_label; + return result; + } + + void link(AbstractMacroAssembler<ARMv7Assembler>* masm) const + { + masm->m_assembler.linkJump(m_label, masm->m_assembler.label(), m_type, m_condition); + } + + void linkTo(LabelType label, AbstractMacroAssembler<ARMv7Assembler>* masm) const + { + masm->m_assembler.linkJump(m_label, label.label(), m_type, m_condition); + } + + bool isSet() const { return m_label.isSet(); } + + private: + AssemblerLabel m_label; + ARMv7Assembler::JumpType m_type; + ARMv7Assembler::Condition m_condition; + }; + private: // ARMv7, Appx-A.6.3 @@ -2115,6 +2166,7 @@ public: linkJumpAbsolute(location, to); } +#if !defined(V4_BOOTSTRAP) static void linkCall(void* code, AssemblerLabel from, void* to) { ASSERT(!(reinterpret_cast<intptr_t>(code) & 1)); @@ -2123,12 +2175,14 @@ public: setPointer(reinterpret_cast<uint16_t*>(reinterpret_cast<intptr_t>(code) + from.m_offset) - 1, to, false); } +#endif static void linkPointer(void* code, AssemblerLabel where, void* value) { setPointer(reinterpret_cast<char*>(code) + where.m_offset, value, false); } +#if !defined(V4_BOOTSTRAP) static void relinkJump(void* from, void* to) { ASSERT(!(reinterpret_cast<intptr_t>(from) & 1)); @@ -2146,11 +2200,12 @@ public: setPointer(reinterpret_cast<uint16_t*>(from) - 1, to, true); } - + static void* readCallTarget(void* from) { return readPointer(reinterpret_cast<uint16_t*>(from) - 1); } +#endif static void repatchInt32(void* where, int32_t value) { @@ -2179,6 +2234,7 @@ public: cacheFlush(location, sizeof(uint16_t) * 2); } +#if !defined(V4_BOOTSTRAP) static void repatchPointer(void* where, void* value) { ASSERT(!(reinterpret_cast<intptr_t>(where) & 1)); @@ -2190,7 +2246,8 @@ public: { return reinterpret_cast<void*>(readInt32(where)); } - +#endif + static void replaceWithJump(void* instructionStart, void* to) { ASSERT(!(bitwise_cast<uintptr_t>(instructionStart) & 1)); @@ -2264,7 +2321,7 @@ public: unsigned debugOffset() { return m_formatter.debugOffset(); } -#if OS(LINUX) +#if OS(LINUX) && !defined(V4_BOOTSTRAP) static inline void linuxPageFlush(uintptr_t begin, uintptr_t end) { asm volatile( @@ -2284,7 +2341,10 @@ public: static void cacheFlush(void* code, size_t size) { -#if OS(IOS) +#if defined(V4_BOOTSTRAP) + UNUSED_PARAM(code) + UNUSED_PARAM(size) +#elif OS(IOS) sys_cache_control(kCacheFunctionPrepareForExecution, code, size); #elif OS(LINUX) size_t page = pageSize(); @@ -2430,7 +2490,9 @@ private: static void setPointer(void* code, void* value, bool flush) { - setInt32(code, reinterpret_cast<uint32_t>(value), flush); + // ### Deliberate "loss" of precision here. On 64-bit hosts void* is wider + // than uint32_t, but the target is 32-bit ARM anyway. + setInt32(code, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(value)), flush); } static bool isB(void* address) @@ -2594,6 +2656,11 @@ private: static void linkBX(uint16_t* instruction, void* target) { +#if defined(V4_BOOTSTRAP) + UNUSED_PARAM(instruction); + UNUSED_PARAM(target); + RELEASE_ASSERT_NOT_REACHED(); +#else // FIMXE: this should be up in the MacroAssembler layer. :-( ASSERT(!(reinterpret_cast<intptr_t>(instruction) & 1)); ASSERT(!(reinterpret_cast<intptr_t>(target) & 1)); @@ -2606,6 +2673,7 @@ private: instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); +#endif } void linkConditionalBX(Condition cond, uint16_t* instruction, void* target) @@ -2638,6 +2706,9 @@ private: instruction[-3] = OP_NOP_T2b; linkJumpT4(instruction, target); } else { +#if defined(V4_BOOTSTRAP) + RELEASE_ASSERT_NOT_REACHED(); +#else const uint16_t JUMP_TEMPORARY_REGISTER = ARMRegisters::ip; ARMThumbImmediate lo16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) + 1)); ARMThumbImmediate hi16 = ARMThumbImmediate::makeUInt16(static_cast<uint16_t>(reinterpret_cast<uint32_t>(target) >> 16)); @@ -2646,6 +2717,7 @@ private: instruction[-3] = twoWordOp5i6Imm4Reg4EncodedImmFirst(OP_MOVT, hi16); instruction[-2] = twoWordOp5i6Imm4Reg4EncodedImmSecond(JUMP_TEMPORARY_REGISTER, hi16); instruction[-1] = OP_BX | (JUMP_TEMPORARY_REGISTER << 3); +#endif } } @@ -2795,6 +2867,9 @@ private: int m_indexOfTailOfLastWatchpoint; }; +#undef JUMP_ENUM_WITH_SIZE +#undef JUMP_ENUM_SIZE + } // namespace JSC #endif // ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) diff --git a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h index e90dd235c6..6fac27fdf1 100644 --- a/src/3rdparty/masm/assembler/AbstractMacroAssembler.h +++ b/src/3rdparty/masm/assembler/AbstractMacroAssembler.h @@ -47,7 +47,10 @@ namespace JSC { class JumpReplacementWatchpoint; -class LinkBuffer; +template <typename, template <typename> class> +class LinkBufferBase; +template <typename> +class BranchCompactingLinkBuffer; class RepatchBuffer; class Watchpoint; namespace DFG { @@ -63,7 +66,9 @@ public: typedef MacroAssemblerCodePtr CodePtr; typedef MacroAssemblerCodeRef CodeRef; +#if !CPU(ARM_THUMB2) && !CPU(ARM64) && !defined(V4_BOOTSTRAP) class Jump; +#endif typedef typename AssemblerType::RegisterID RegisterID; typedef typename AssemblerType::FPRegisterID FPRegisterID; @@ -325,7 +330,7 @@ public: friend class Jump; friend class JumpReplacementWatchpoint; friend class MacroAssemblerCodeRef; - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; friend class Watchpoint; public: @@ -339,6 +344,8 @@ public: } bool isSet() const { return m_label.isSet(); } + + const AssemblerLabel &label() const { return m_label; } private: AssemblerLabel m_label; }; @@ -356,7 +363,7 @@ public: class ConvertibleLoadLabel { template<class TemplateAssemblerType> friend class AbstractMacroAssembler; - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; public: ConvertibleLoadLabel() @@ -380,7 +387,7 @@ public: class DataLabelPtr { template<class TemplateAssemblerType> friend class AbstractMacroAssembler; - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; public: DataLabelPtr() { @@ -404,7 +411,7 @@ public: class DataLabel32 { template<class TemplateAssemblerType> friend class AbstractMacroAssembler; - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; public: DataLabel32() { @@ -428,7 +435,7 @@ public: class DataLabelCompact { template<class TemplateAssemblerType> friend class AbstractMacroAssembler; - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; public: DataLabelCompact() { @@ -448,6 +455,11 @@ public: AssemblerLabel m_label; }; +#if CPU(ARM_THUMB2) || CPU(ARM64) || defined(V4_BOOTSTRAP) + using Jump = typename AssemblerType::template Jump<Label>; + friend Jump; +#endif + // Call: // // A Call object is a reference to a call instruction that has been planted @@ -498,18 +510,19 @@ public: // into the code buffer - it is typically used to link the jump, setting the // relative offset such that when executed it will jump to the desired // destination. +#if !CPU(ARM_THUMB2) && !CPU(ARM64) && !defined(V4_BOOTSTRAP) class Jump { template<class TemplateAssemblerType> friend class AbstractMacroAssembler; friend class Call; friend struct DFG::OSRExit; - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; public: Jump() { } -#if CPU(ARM_THUMB2) +#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) // Fixme: this information should be stored in the instruction stream, not in the Jump object. Jump(AssemblerLabel jmp, ARMv7Assembler::JumpType type = ARMv7Assembler::JumpNoCondition, ARMv7Assembler::Condition condition = ARMv7Assembler::ConditionInvalid) : m_label(jmp) @@ -610,10 +623,11 @@ public: private: AssemblerLabel m_label; -#if CPU(ARM_THUMB2) +#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) ARMv7Assembler::JumpType m_type; ARMv7Assembler::Condition m_condition; -#elif CPU(ARM64) +#endif +#if CPU(ARM64) ARM64Assembler::JumpType m_type; ARM64Assembler::Condition m_condition; bool m_is64Bit; @@ -624,6 +638,7 @@ public: SH4Assembler::JumpType m_type; #endif }; +#endif struct PatchableJump { PatchableJump() @@ -645,7 +660,7 @@ public: // A JumpList is a set of Jump objects. // All jumps in the set will be linked to the same destination. class JumpList { - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; public: typedef Vector<Jump, 2> JumpVector; @@ -819,7 +834,8 @@ protected: static bool shouldBlindForSpecificArch(uint64_t) { return true; } #endif - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; + template <typename> friend class BranchCompactingLinkBuffer; friend class RepatchBuffer; static void linkJump(void* code, Jump jump, CodeLocationLabel target) @@ -867,10 +883,12 @@ protected: AssemblerType::repatchPointer(dataLabelPtr.dataLocation(), value); } +#if !defined(V4_BOOTSTRAP) static void* readPointer(CodeLocationDataLabelPtr dataLabelPtr) { return AssemblerType::readPointer(dataLabelPtr.dataLocation()); } +#endif static void replaceWithLoad(CodeLocationConvertibleLoad label) { diff --git a/src/3rdparty/masm/assembler/LinkBuffer.cpp b/src/3rdparty/masm/assembler/LinkBuffer.cpp index 432a7ee227..74c278135b 100644 --- a/src/3rdparty/masm/assembler/LinkBuffer.cpp +++ b/src/3rdparty/masm/assembler/LinkBuffer.cpp @@ -32,140 +32,6 @@ namespace JSC { -LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithoutDisassembly() -{ - performFinalization(); - - return CodeRef(m_executableMemory); -} - -LinkBuffer::CodeRef LinkBuffer::finalizeCodeWithDisassembly(const char* format, ...) -{ - ASSERT(Options::showDisassembly() || Options::showDFGDisassembly()); - - CodeRef result = finalizeCodeWithoutDisassembly(); - - dataLogF("Generated JIT code for "); - va_list argList; - va_start(argList, format); - WTF::dataLogFV(format, argList); - va_end(argList); - dataLogF(":\n"); - - dataLogF( -#if OS(WINDOWS) - " Code at [0x%p, 0x%p):\n", -#else - " Code at [%p, %p):\n", -#endif - result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size()); - disassemble(result.code(), m_size, " ", WTF::dataFile()); - - return result; -} - -void LinkBuffer::linkCode(void* ownerUID, JITCompilationEffort effort) -{ - ASSERT(!m_code); -#if !ENABLE(BRANCH_COMPACTION) - m_executableMemory = m_assembler->m_assembler.executableCopy(*m_globalData, ownerUID, effort); - if (!m_executableMemory) - return; - m_code = m_executableMemory->start(); - m_size = m_assembler->m_assembler.codeSize(); - ASSERT(m_code); -#else - m_initialSize = m_assembler->m_assembler.codeSize(); - m_executableMemory = m_globalData->executableAllocator.allocate(*m_globalData, m_initialSize, ownerUID, effort); - if (!m_executableMemory) - return; - m_code = (uint8_t*)m_executableMemory->start(); - ASSERT(m_code); - ExecutableAllocator::makeWritable(m_code, m_initialSize); - uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); - uint8_t* outData = reinterpret_cast<uint8_t*>(m_code); - int readPtr = 0; - int writePtr = 0; - Vector<LinkRecord, 0, UnsafeVectorOverflow>& jumpsToLink = m_assembler->jumpsToLink(); - unsigned jumpCount = jumpsToLink.size(); - for (unsigned i = 0; i < jumpCount; ++i) { - int offset = readPtr - writePtr; - ASSERT(!(offset & 1)); - - // Copy the instructions from the last jump to the current one. - size_t regionSize = jumpsToLink[i].from() - readPtr; - uint16_t* copySource = reinterpret_cast_ptr<uint16_t*>(inData + readPtr); - uint16_t* copyEnd = reinterpret_cast_ptr<uint16_t*>(inData + readPtr + regionSize); - uint16_t* copyDst = reinterpret_cast_ptr<uint16_t*>(outData + writePtr); - ASSERT(!(regionSize % 2)); - ASSERT(!(readPtr % 2)); - ASSERT(!(writePtr % 2)); - while (copySource != copyEnd) - *copyDst++ = *copySource++; - m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); - readPtr += regionSize; - writePtr += regionSize; - - // Calculate absolute address of the jump target, in the case of backwards - // branches we need to be precise, forward branches we are pessimistic - const uint8_t* target; - if (jumpsToLink[i].to() >= jumpsToLink[i].from()) - target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far - else - target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); - - JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target); - // Compact branch if we can... - if (m_assembler->canCompact(jumpsToLink[i].type())) { - // Step back in the write stream - int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); - if (delta) { - writePtr -= delta; - m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); - } - } - jumpsToLink[i].setFrom(writePtr); - } - // Copy everything after the last jump - memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr); - m_assembler->recordLinkOffsets(readPtr, m_initialSize, readPtr - writePtr); - - for (unsigned i = 0; i < jumpCount; ++i) { - uint8_t* location = outData + jumpsToLink[i].from(); - uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); - m_assembler->link(jumpsToLink[i], location, target); - } - - jumpsToLink.clear(); - m_size = writePtr + m_initialSize - readPtr; - m_executableMemory->shrink(m_size); - -#if DUMP_LINK_STATISTICS - dumpLinkStatistics(m_code, m_initialSize, m_size); -#endif -#if DUMP_CODE - dumpCode(m_code, m_size); -#endif -#endif -} - -void LinkBuffer::performFinalization() -{ -#ifndef NDEBUG - ASSERT(!m_completed); - ASSERT(isValid()); - m_completed = true; -#endif - -#if ENABLE(BRANCH_COMPACTION) - ExecutableAllocator::makeExecutable(code(), m_initialSize); -#else - ASSERT(m_size <= INT_MAX); - ExecutableAllocator::makeExecutable(code(), static_cast<int>(m_size)); -#endif - MacroAssembler::cacheFlush(code(), m_size); -} - #if DUMP_LINK_STATISTICS void LinkBuffer::dumpLinkStatistics(void* code, size_t initializeSize, size_t finalSize) { diff --git a/src/3rdparty/masm/assembler/LinkBuffer.h b/src/3rdparty/masm/assembler/LinkBuffer.h index e1882433c1..bfd0e402ca 100644 --- a/src/3rdparty/masm/assembler/LinkBuffer.h +++ b/src/3rdparty/masm/assembler/LinkBuffer.h @@ -36,6 +36,7 @@ #include "JITCompilationEffort.h" #include "MacroAssembler.h" +#include "Options.h" #include <wtf/DataLog.h> #include <wtf/Noncopyable.h> @@ -43,6 +44,12 @@ namespace JSC { class JSGlobalData; +template <typename T> +struct DefaultExecutableOffsetCalculator { + template <typename Assembler> + static T applyOffset(Assembler *, T src) { return src; } +}; + // LinkBuffer: // // This class assists in linking code generated by the macro assembler, once code generation @@ -57,30 +64,24 @@ class JSGlobalData; // * The address of a Label pointing into the code may be resolved. // * The value referenced by a DataLabel may be set. // -class LinkBuffer { - WTF_MAKE_NONCOPYABLE(LinkBuffer); +template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator> +class LinkBufferBase { + WTF_MAKE_NONCOPYABLE(LinkBufferBase); typedef MacroAssemblerCodeRef CodeRef; typedef MacroAssemblerCodePtr CodePtr; - typedef MacroAssembler::Label Label; - typedef MacroAssembler::Jump Jump; - typedef MacroAssembler::PatchableJump PatchableJump; - typedef MacroAssembler::JumpList JumpList; - typedef MacroAssembler::Call Call; - typedef MacroAssembler::DataLabelCompact DataLabelCompact; - typedef MacroAssembler::DataLabel32 DataLabel32; - typedef MacroAssembler::DataLabelPtr DataLabelPtr; - typedef MacroAssembler::ConvertibleLoadLabel ConvertibleLoadLabel; -#if ENABLE(BRANCH_COMPACTION) - typedef MacroAssembler::LinkRecord LinkRecord; - typedef MacroAssembler::JumpLinkType JumpLinkType; -#endif + typedef typename MacroAssembler::Label Label; + typedef typename MacroAssembler::Jump Jump; + typedef typename MacroAssembler::PatchableJump PatchableJump; + typedef typename MacroAssembler::JumpList JumpList; + typedef typename MacroAssembler::Call Call; + typedef typename MacroAssembler::DataLabelCompact DataLabelCompact; + typedef typename MacroAssembler::DataLabel32 DataLabel32; + typedef typename MacroAssembler::DataLabelPtr DataLabelPtr; + typedef typename MacroAssembler::ConvertibleLoadLabel ConvertibleLoadLabel; public: - LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) + LinkBufferBase(JSGlobalData& globalData, MacroAssembler* masm, JITCompilationEffort effort = JITCompilationMustSucceed) : m_size(0) -#if ENABLE(BRANCH_COMPACTION) - , m_initialSize(0) -#endif , m_code(0) , m_assembler(masm) , m_globalData(&globalData) @@ -89,10 +90,13 @@ public: , m_effort(effort) #endif { - linkCode(ownerUID, effort); +#ifdef NDEBUG + UNUSED_PARAM(effort) +#endif + // Simon: Moved this to the sub-classes linkCode(ownerUID, effort); } - ~LinkBuffer() + ~LinkBufferBase() { ASSERT(m_completed || (!m_executableMemory && m_effort == JITCompilationCanFail)); } @@ -204,8 +208,8 @@ public: // finalizeCodeWithoutDisassembly() directly if you have your own way of // displaying disassembly. - CodeRef finalizeCodeWithoutDisassembly(); - CodeRef finalizeCodeWithDisassembly(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); + inline CodeRef finalizeCodeWithoutDisassembly(); + inline CodeRef finalizeCodeWithDisassembly(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3); CodePtr trampolineAt(Label label) { @@ -225,21 +229,19 @@ public: private: template <typename T> T applyOffset(T src) { -#if ENABLE(BRANCH_COMPACTION) - src.m_offset -= m_assembler->executableOffsetFor(src.m_offset); -#endif - return src; + return ExecutableOffsetCalculator<T>::applyOffset(m_assembler, src); } +protected: // Keep this private! - the underlying code should only be obtained externally via finalizeCode(). void* code() { return m_code; } - void linkCode(void* ownerUID, JITCompilationEffort); + inline void linkCode(void* ownerUID, JITCompilationEffort); - void performFinalization(); + inline void performFinalization(); #if DUMP_LINK_STATISTICS static void dumpLinkStatistics(void* code, size_t initialSize, size_t finalSize); @@ -251,12 +253,10 @@ private: RefPtr<ExecutableMemoryHandle> m_executableMemory; size_t m_size; -#if ENABLE(BRANCH_COMPACTION) - size_t m_initialSize; -#endif void* m_code; MacroAssembler* m_assembler; JSGlobalData* m_globalData; +protected: #ifndef NDEBUG bool m_completed; JITCompilationEffort m_effort; @@ -290,6 +290,234 @@ private: #define FINALIZE_DFG_CODE(linkBufferReference, dataLogFArgumentsForHeading) \ FINALIZE_CODE_IF((Options::showDisassembly() || Options::showDFGDisassembly()), linkBufferReference, dataLogFArgumentsForHeading) + +template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator> +inline typename LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::CodeRef LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::finalizeCodeWithoutDisassembly() +{ + performFinalization(); + + return CodeRef(m_executableMemory); +} + +template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator> +inline typename LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::CodeRef LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::finalizeCodeWithDisassembly(const char* format, ...) +{ + ASSERT(Options::showDisassembly() || Options::showDFGDisassembly()); + + CodeRef result = finalizeCodeWithoutDisassembly(); + + dataLogF("Generated JIT code for "); + va_list argList; + va_start(argList, format); + WTF::dataLogFV(format, argList); + va_end(argList); + dataLogF(":\n"); + + dataLogF( +#if OS(WINDOWS) + " Code at [0x%p, 0x%p):\n", +#else + " Code at [%p, %p):\n", +#endif + result.code().executableAddress(), static_cast<char*>(result.code().executableAddress()) + result.size()); + disassemble(result.code(), m_size, " ", WTF::dataFile()); + + return result; +} + +template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator> +inline void LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::linkCode(void* ownerUID, JITCompilationEffort effort) +{ + UNUSED_PARAM(ownerUID) + UNUSED_PARAM(effort) + ASSERT(!m_code); + m_executableMemory = m_assembler->m_assembler.executableCopy(*m_globalData, ownerUID, effort); + if (!m_executableMemory) + return; + m_code = m_executableMemory->start(); + m_size = m_assembler->m_assembler.codeSize(); + ASSERT(m_code); +} + +template <typename MacroAssembler, template <typename T> class ExecutableOffsetCalculator> +inline void LinkBufferBase<MacroAssembler, ExecutableOffsetCalculator>::performFinalization() +{ + // NOTE: This function is specialized in LinkBuffer<MacroAssemblerARMv7> +#ifndef NDEBUG + ASSERT(!m_completed); + ASSERT(isValid()); + m_completed = true; +#endif + + ASSERT(m_size <= INT_MAX); + ExecutableAllocator::makeExecutable(code(), static_cast<int>(m_size)); + MacroAssembler::cacheFlush(code(), m_size); +} + +template <typename MacroAssembler> +class LinkBuffer : public LinkBufferBase<MacroAssembler, DefaultExecutableOffsetCalculator> +{ +public: + LinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) + : LinkBufferBase<MacroAssembler, DefaultExecutableOffsetCalculator>(globalData, masm, effort) + { + this->linkCode(ownerUID, effort); + } +}; + +#if CPU(ARM_THUMB2) || CPU(ARM64) || defined(V4_BOOTSTRAP) + +template <typename T> +struct BranchCompactingExecutableOffsetCalculator { + template <typename Assembler> + static T applyOffset(Assembler *as, T src) { + src.m_offset -= as->executableOffsetFor(src.m_offset); + return src; + } +}; + +template <typename MacroAssembler> +class BranchCompactingLinkBuffer : public LinkBufferBase<MacroAssembler, BranchCompactingExecutableOffsetCalculator> +{ +public: + BranchCompactingLinkBuffer(JSGlobalData& globalData, MacroAssembler* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) + : LinkBufferBase<MacroAssembler, BranchCompactingExecutableOffsetCalculator>(globalData, masm, effort) + { + linkCode(ownerUID, effort); + } + + inline void performFinalization(); + + inline void linkCode(void* ownerUID, JITCompilationEffort); + +private: + using Base = LinkBufferBase<MacroAssembler, BranchCompactingExecutableOffsetCalculator>; +#ifndef NDEBUG + using Base::m_completed; +#endif + using Base::isValid; + using Base::code; + using Base::m_code; + using Base::m_size; + using Base::m_assembler; + using Base::m_executableMemory; + using Base::m_globalData; + + using LinkRecord = typename MacroAssembler::LinkRecord; + using JumpLinkType = typename MacroAssembler::JumpLinkType; + + size_t m_initialSize = 0; +}; + +template <typename MacroAssembler> +inline void BranchCompactingLinkBuffer<MacroAssembler>::performFinalization() +{ +#ifndef NDEBUG + ASSERT(!m_completed); + ASSERT(isValid()); + this->m_completed = true; +#endif + + ExecutableAllocator::makeExecutable(code(), m_initialSize); + MacroAssembler::cacheFlush(code(), m_size); +} + +template <typename MacroAssembler> +inline void BranchCompactingLinkBuffer<MacroAssembler>::linkCode(void* ownerUID, JITCompilationEffort effort) +{ + UNUSED_PARAM(ownerUID) + UNUSED_PARAM(effort) + ASSERT(!m_code); + m_initialSize = m_assembler->m_assembler.codeSize(); + m_executableMemory = m_globalData->executableAllocator.allocate(*m_globalData, m_initialSize, ownerUID, effort); + if (!m_executableMemory) + return; + m_code = (uint8_t*)m_executableMemory->start(); + ASSERT(m_code); + ExecutableAllocator::makeWritable(m_code, m_initialSize); + uint8_t* inData = (uint8_t*)m_assembler->unlinkedCode(); + uint8_t* outData = reinterpret_cast<uint8_t*>(m_code); + int readPtr = 0; + int writePtr = 0; + Vector<LinkRecord, 0, UnsafeVectorOverflow>& jumpsToLink = m_assembler->jumpsToLink(); + unsigned jumpCount = unsigned(jumpsToLink.size()); + for (unsigned i = 0; i < jumpCount; ++i) { + int offset = readPtr - writePtr; + ASSERT(!(offset & 1)); + + // Copy the instructions from the last jump to the current one. + unsigned regionSize = unsigned(jumpsToLink[i].from() - readPtr); + uint16_t* copySource = reinterpret_cast_ptr<uint16_t*>(inData + readPtr); + uint16_t* copyEnd = reinterpret_cast_ptr<uint16_t*>(inData + readPtr + regionSize); + uint16_t* copyDst = reinterpret_cast_ptr<uint16_t*>(outData + writePtr); + ASSERT(!(regionSize % 2)); + ASSERT(!(readPtr % 2)); + ASSERT(!(writePtr % 2)); + while (copySource != copyEnd) + *copyDst++ = *copySource++; + m_assembler->recordLinkOffsets(readPtr, jumpsToLink[i].from(), offset); + readPtr += regionSize; + writePtr += regionSize; + + // Calculate absolute address of the jump target, in the case of backwards + // branches we need to be precise, forward branches we are pessimistic + const uint8_t* target; + if (jumpsToLink[i].to() >= jumpsToLink[i].from()) + target = outData + jumpsToLink[i].to() - offset; // Compensate for what we have collapsed so far + else + target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); + + JumpLinkType jumpLinkType = m_assembler->computeJumpType(jumpsToLink[i], outData + writePtr, target); + // Compact branch if we can... + if (m_assembler->canCompact(jumpsToLink[i].type())) { + // Step back in the write stream + int32_t delta = m_assembler->jumpSizeDelta(jumpsToLink[i].type(), jumpLinkType); + if (delta) { + writePtr -= delta; + m_assembler->recordLinkOffsets(jumpsToLink[i].from() - delta, readPtr, readPtr - writePtr); + } + } + jumpsToLink[i].setFrom(writePtr); + } + // Copy everything after the last jump + memcpy(outData + writePtr, inData + readPtr, m_initialSize - readPtr); + m_assembler->recordLinkOffsets(readPtr, unsigned(m_initialSize), readPtr - writePtr); + + for (unsigned i = 0; i < jumpCount; ++i) { + uint8_t* location = outData + jumpsToLink[i].from(); + uint8_t* target = outData + jumpsToLink[i].to() - m_assembler->executableOffsetFor(jumpsToLink[i].to()); + m_assembler->link(jumpsToLink[i], location, target); + } + + jumpsToLink.clear(); + m_size = writePtr + m_initialSize - readPtr; + m_executableMemory->shrink(m_size); +} + +#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) +template <> +class LinkBuffer<JSC::MacroAssembler<MacroAssemblerARMv7>> : public BranchCompactingLinkBuffer<JSC::MacroAssembler<MacroAssemblerARMv7>> +{ +public: + LinkBuffer(JSGlobalData& globalData, JSC::MacroAssembler<MacroAssemblerARMv7>* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) + : BranchCompactingLinkBuffer<JSC::MacroAssembler<MacroAssemblerARMv7>>(globalData, masm, ownerUID, effort) + {} +}; +#endif + +#if CPU(ARM64) || defined(V4_BOOTSTRAP) +template <> +class LinkBuffer<JSC::MacroAssembler<MacroAssemblerARM64>> : public BranchCompactingLinkBuffer<JSC::MacroAssembler<MacroAssemblerARM64>> +{ +public: + LinkBuffer(JSGlobalData& globalData, JSC::MacroAssembler<MacroAssemblerARM64>* masm, void* ownerUID, JITCompilationEffort effort = JITCompilationMustSucceed) + : BranchCompactingLinkBuffer<JSC::MacroAssembler<JSC::MacroAssemblerARM64>>(globalData, masm, ownerUID, effort) + {} +}; +#endif + +#endif + } // namespace JSC #endif // ENABLE(ASSEMBLER) diff --git a/src/3rdparty/masm/assembler/MacroAssembler.h b/src/3rdparty/masm/assembler/MacroAssembler.h index e122e2f3ae..7d9f156c8c 100644 --- a/src/3rdparty/masm/assembler/MacroAssembler.h +++ b/src/3rdparty/masm/assembler/MacroAssembler.h @@ -30,12 +30,13 @@ #if ENABLE(ASSEMBLER) -#if CPU(ARM_THUMB2) #include "MacroAssemblerARMv7.h" +#include "MacroAssemblerARM64.h" + +#if CPU(ARM_THUMB2) namespace JSC { typedef MacroAssemblerARMv7 MacroAssemblerBase; }; #elif CPU(ARM64) -#include "MacroAssemblerARM64.h" namespace JSC { typedef MacroAssemblerARM64 MacroAssemblerBase; }; #elif CPU(ARM_TRADITIONAL) @@ -68,13 +69,53 @@ typedef MacroAssemblerSH4 MacroAssemblerBase; namespace JSC { +template <typename MacroAssemblerBase> class MacroAssembler : public MacroAssemblerBase { public: + using DoubleCondition = typename MacroAssemblerBase::DoubleCondition; + using ResultCondition = typename MacroAssemblerBase::ResultCondition; + using RelationalCondition = typename MacroAssemblerBase::RelationalCondition; + using RegisterID = typename MacroAssemblerBase::RegisterID; + using Address = typename MacroAssemblerBase::Address; + using ExtendedAddress = typename MacroAssemblerBase::ExtendedAddress; + using BaseIndex = typename MacroAssemblerBase::BaseIndex; + using ImplicitAddress = typename MacroAssemblerBase::ImplicitAddress; + using AbsoluteAddress = typename MacroAssemblerBase::AbsoluteAddress; + using TrustedImm32 = typename MacroAssemblerBase::TrustedImm32; + using TrustedImm64 = typename MacroAssemblerBase::TrustedImm64; + using TrustedImmPtr = typename MacroAssemblerBase::TrustedImmPtr; + using Imm32 = typename MacroAssemblerBase::Imm32; + using Imm64 = typename MacroAssemblerBase::Imm64; + using ImmPtr = typename MacroAssemblerBase::ImmPtr; + using Label = typename MacroAssemblerBase::Label; + using DataLabelPtr = typename MacroAssemblerBase::DataLabelPtr; + using DataLabel32 = typename MacroAssemblerBase::DataLabel32; + using DataLabelCompact = typename MacroAssemblerBase::DataLabelCompact; + using Jump = typename MacroAssemblerBase::Jump; + using PatchableJump = typename MacroAssemblerBase::PatchableJump; + using MacroAssemblerBase::pop; using MacroAssemblerBase::jump; using MacroAssemblerBase::branch32; using MacroAssemblerBase::move; + using MacroAssemblerBase::store32; + using MacroAssemblerBase::add32; + using MacroAssemblerBase::xor32; + using MacroAssemblerBase::sub32; + using MacroAssemblerBase::load32; + + +#if defined(V4_BOOTSTRAP) + using MacroAssemblerBase::loadPtr; + using MacroAssemblerBase::storePtr; +#elif CPU(X86_64) || CPU(ARM64) + using MacroAssemblerBase::add64; + using MacroAssemblerBase::sub64; + using MacroAssemblerBase::xor64; + using MacroAssemblerBase::load64; + using MacroAssemblerBase::store64; +#endif #if ENABLE(JIT_CONSTANT_BLINDING) using MacroAssemblerBase::add32; @@ -100,41 +141,41 @@ public: static DoubleCondition invert(DoubleCondition cond) { switch (cond) { - case DoubleEqual: - return DoubleNotEqualOrUnordered; - case DoubleNotEqual: - return DoubleEqualOrUnordered; - case DoubleGreaterThan: - return DoubleLessThanOrEqualOrUnordered; - case DoubleGreaterThanOrEqual: - return DoubleLessThanOrUnordered; - case DoubleLessThan: - return DoubleGreaterThanOrEqualOrUnordered; - case DoubleLessThanOrEqual: - return DoubleGreaterThanOrUnordered; - case DoubleEqualOrUnordered: - return DoubleNotEqual; - case DoubleNotEqualOrUnordered: - return DoubleEqual; - case DoubleGreaterThanOrUnordered: - return DoubleLessThanOrEqual; - case DoubleGreaterThanOrEqualOrUnordered: - return DoubleLessThan; - case DoubleLessThanOrUnordered: - return DoubleGreaterThanOrEqual; - case DoubleLessThanOrEqualOrUnordered: - return DoubleGreaterThan; + case DoubleCondition::DoubleEqual: + return DoubleCondition::DoubleNotEqualOrUnordered; + case DoubleCondition::DoubleNotEqual: + return DoubleCondition::DoubleEqualOrUnordered; + case DoubleCondition::DoubleGreaterThan: + return DoubleCondition::DoubleLessThanOrEqualOrUnordered; + case DoubleCondition::DoubleGreaterThanOrEqual: + return DoubleCondition::DoubleLessThanOrUnordered; + case DoubleCondition::DoubleLessThan: + return DoubleCondition::DoubleGreaterThanOrEqualOrUnordered; + case DoubleCondition::DoubleLessThanOrEqual: + return DoubleCondition::DoubleGreaterThanOrUnordered; + case DoubleCondition::DoubleEqualOrUnordered: + return DoubleCondition::DoubleNotEqual; + case DoubleCondition::DoubleNotEqualOrUnordered: + return DoubleCondition::DoubleEqual; + case DoubleCondition::DoubleGreaterThanOrUnordered: + return DoubleCondition::DoubleLessThanOrEqual; + case DoubleCondition::DoubleGreaterThanOrEqualOrUnordered: + return DoubleCondition::DoubleLessThan; + case DoubleCondition::DoubleLessThanOrUnordered: + return DoubleCondition::DoubleGreaterThanOrEqual; + case DoubleCondition::DoubleLessThanOrEqualOrUnordered: + return DoubleCondition::DoubleGreaterThan; default: RELEASE_ASSERT_NOT_REACHED(); - return DoubleEqual; // make compiler happy + return DoubleCondition::DoubleEqual; // make compiler happy } } static bool isInvertible(ResultCondition cond) { switch (cond) { - case Zero: - case NonZero: + case ResultCondition::Zero: + case ResultCondition::NonZero: return true; default: return false; @@ -144,13 +185,13 @@ public: static ResultCondition invert(ResultCondition cond) { switch (cond) { - case Zero: - return NonZero; - case NonZero: - return Zero; + case ResultCondition::Zero: + return ResultCondition::NonZero; + case ResultCondition::NonZero: + return ResultCondition::Zero; default: RELEASE_ASSERT_NOT_REACHED(); - return Zero; // Make compiler happy for release builds. + return ResultCondition::Zero; // Make compiler happy for release builds. } } #endif @@ -159,17 +200,17 @@ public: // described in terms of other macro assembly methods. void pop() { - addPtr(TrustedImm32(sizeof(void*)), stackPointerRegister); + addPtr(TrustedImm32(sizeof(void*)), MacroAssemblerBase::stackPointerRegister); } void peek(RegisterID dest, int index = 0) { - loadPtr(Address(stackPointerRegister, (index * sizeof(void*))), dest); + loadPtr(Address(MacroAssemblerBase::stackPointerRegister, (index * sizeof(void*))), dest); } Address addressForPoke(int index) { - return Address(stackPointerRegister, (index * sizeof(void*))); + return Address(MacroAssemblerBase::stackPointerRegister, (index * sizeof(void*))); } void poke(RegisterID src, int index = 0) @@ -187,10 +228,10 @@ public: storePtr(imm, addressForPoke(index)); } -#if CPU(X86_64) || CPU(ARM64) +#if (CPU(X86_64) || CPU(ARM64)) && !defined(V4_BOOTSTRAP) void peek64(RegisterID dest, int index = 0) { - load64(Address(stackPointerRegister, (index * sizeof(void*))), dest); + load64(Address(MacroAssemblerBase::stackPointerRegister, (index * sizeof(void*))), dest); } void poke(TrustedImm64 value, int index = 0) @@ -296,36 +337,37 @@ public: static RelationalCondition commute(RelationalCondition condition) { switch (condition) { - case Above: - return Below; - case AboveOrEqual: - return BelowOrEqual; - case Below: - return Above; - case BelowOrEqual: - return AboveOrEqual; - case GreaterThan: - return LessThan; - case GreaterThanOrEqual: - return LessThanOrEqual; - case LessThan: - return GreaterThan; - case LessThanOrEqual: - return GreaterThanOrEqual; + case RelationalCondition::Above: + return RelationalCondition::Below; + case RelationalCondition::AboveOrEqual: + return RelationalCondition::BelowOrEqual; + case RelationalCondition::Below: + return RelationalCondition::Above; + case RelationalCondition::BelowOrEqual: + return RelationalCondition::AboveOrEqual; + case RelationalCondition::GreaterThan: + return RelationalCondition::LessThan; + case RelationalCondition::GreaterThanOrEqual: + return RelationalCondition::LessThanOrEqual; + case RelationalCondition::LessThan: + return RelationalCondition::GreaterThan; + case RelationalCondition::LessThanOrEqual: + return RelationalCondition::GreaterThanOrEqual; default: break; } - ASSERT(condition == Equal || condition == NotEqual); + ASSERT(condition == RelationalCondition::Equal || condition == RelationalCondition::NotEqual); return condition; } static const unsigned BlindingModulus = 64; bool shouldConsiderBlinding() { - return !(random() & (BlindingModulus - 1)); + return !(this->random() & (BlindingModulus - 1)); } +#if !defined(V4_BOOTSTRAP) // Ptr methods // On 32-bit platforms (i.e. x86), these methods directly map onto their 32-bit equivalents. // FIXME: should this use a test for 32-bitness instead of this specific exception? @@ -850,6 +892,7 @@ public: { return branchSub64(cond, src1, src2, dest); } +#endif // !defined(V4_BOOTSTRAP) #if ENABLE(JIT_CONSTANT_BLINDING) using MacroAssemblerBase::and64; @@ -1447,6 +1490,22 @@ public: #endif }; +#if CPU(ARM_THUMB2) +typedef MacroAssembler<MacroAssemblerARMv7> DefaultMacroAssembler; +#elif CPU(ARM64) +typedef MacroAssembler<MacroAssemblerARM64> DefaultMacroAssembler; +#elif CPU(ARM_TRADITIONAL) +typedef MacroAssembler<MacroAssemblerARM> DefaultMacroAssembler; +#elif CPU(MIPS) +typedef MacroAssembler<MacroAssemblerMIPS> DefaultMacroAssembler; +#elif CPU(X86) +typedef MacroAssembler<MacroAssemblerX86> DefaultMacroAssembler; +#elif CPU(X86_64) +typedef MacroAssembler<MacroAssemblerX86_64> DefaultMacroAssembler; +#elif CPU(SH4) +typedef JSC::MacroAssemblerSH4 DefaultMacroAssembler; +#endif + } // namespace JSC #else // ENABLE(ASSEMBLER) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM.h b/src/3rdparty/masm/assembler/MacroAssemblerARM.h index 01e34c97cd..268fe5fe73 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerARM.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerARM.h @@ -1349,7 +1349,7 @@ protected: } private: - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; friend class RepatchBuffer; void internalCompare32(RegisterID left, TrustedImm32 right) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h index bd85b6b2c1..a11637f7ca 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerARM64.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerARM64.h @@ -26,7 +26,7 @@ #ifndef MacroAssemblerARM64_h #define MacroAssemblerARM64_h -#if ENABLE(ASSEMBLER) +#if ENABLE(ASSEMBLER) && (CPU(ARM64) || defined(V4_BOOTSTRAP)) #include "ARM64Assembler.h" #include "AbstractMacroAssembler.h" @@ -42,7 +42,7 @@ class MacroAssemblerARM64 : public AbstractMacroAssembler<ARM64Assembler> { friend class DataLabelPtr; friend class DataLabel32; friend class DataLabelCompact; - friend class Jump; +// template <typename> friend class Jump; friend class Label; public: @@ -119,9 +119,9 @@ public: private: static const ARM64Registers::FPRegisterID fpTempRegister = ARM64Registers::q31; static const ARM64Assembler::SetFlags S = ARM64Assembler::S; - static const intptr_t maskHalfWord0 = 0xffffl; - static const intptr_t maskHalfWord1 = 0xffff0000l; - static const intptr_t maskUpperWord = 0xffffffff00000000l; + static const int64_t maskHalfWord0 = 0xffffl; + static const int64_t maskHalfWord1 = 0xffff0000l; + static const int64_t maskUpperWord = 0xffffffff00000000l; // 4 instructions - 3 to load the function pointer, + blr. static const ptrdiff_t REPATCH_OFFSET_CALL_TO_POINTER = -16; @@ -209,6 +209,33 @@ public: static bool shouldBlindForSpecificArch(uint32_t value) { return value >= 0x00ffffff; } static bool shouldBlindForSpecificArch(uint64_t value) { return value >= 0x00ffffff; } +#if defined(V4_BOOTSTRAP) + void loadPtr(ImplicitAddress address, RegisterID dest) + { + load32(address, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + sub32(imm, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + add32(imm, src, dest); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + store32(src, address); + } +#endif + // Integer operations: void add32(RegisterID a, RegisterID b, RegisterID dest) @@ -2757,6 +2784,7 @@ public: return branch32(cond, left, dataTempRegister); } +#if !defined(V4_BOOTSTRAP) PatchableJump patchableBranchPtr(RelationalCondition cond, Address left, TrustedImmPtr right) { m_makeJumpPatchable = true; @@ -2764,6 +2792,7 @@ public: m_makeJumpPatchable = false; return PatchableJump(result); } +#endif PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) { @@ -3000,7 +3029,7 @@ private: return m_cachedMemoryTempRegister.registerIDInvalidate(); } - ALWAYS_INLINE bool isInIntRange(intptr_t value) + ALWAYS_INLINE bool isInIntRange(int64_t value) { return value == ((value << 32) >> 32); } @@ -3353,7 +3382,9 @@ private: return makeBranch(cond); } - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; + template <typename> friend class BranchCompactingLinkBuffer; + template <typename> friend struct BranchCompactingExecutableOffsetCalculator; void recordLinkOffsets(int32_t regionStart, int32_t regionEnd, int32_t offset) {return m_assembler.recordLinkOffsets(regionStart, regionEnd, offset); } int executableOffsetFor(int location) { return m_assembler.executableOffsetFor(location); } diff --git a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h index 0938383513..806f2e13b6 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerARMv7.h @@ -27,7 +27,7 @@ #ifndef MacroAssemblerARMv7_h #define MacroAssemblerARMv7_h -#if ENABLE(ASSEMBLER) && CPU(ARM_THUMB2) +#if ENABLE(ASSEMBLER) && (CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP)) #include "ARMv7Assembler.h" #include "AbstractMacroAssembler.h" @@ -160,12 +160,41 @@ public: { add32(imm, dest, dest); } + +#if defined(V4_BOOTSTRAP) + void loadPtr(ImplicitAddress address, RegisterID dest) + { + load32(address, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + sub32(imm, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + add32(imm, src, dest); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + store32(src, address); + } +#endif +#if !defined(V4_BOOTSTRAP) void add32(AbsoluteAddress src, RegisterID dest) { load32(src.m_ptr, dataTempRegister); add32(dataTempRegister, dest); } +#endif void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) { @@ -206,6 +235,7 @@ public: add32(dataTempRegister, dest); } +#if !defined(V4_BOOTSTRAP) void add32(TrustedImm32 imm, AbsoluteAddress address) { load32(address.m_ptr, dataTempRegister); @@ -242,6 +272,7 @@ public: m_assembler.adc(dataTempRegister, dataTempRegister, ARMThumbImmediate::makeEncodedImm(imm.m_value >> 31)); m_assembler.str(dataTempRegister, addressTempRegister, ARMThumbImmediate::makeUInt12(4)); } +#endif void and32(RegisterID op1, RegisterID op2, RegisterID dest) { @@ -343,6 +374,7 @@ public: or32(dataTempRegister, dest); } +#if !defined(V4_BOOTSTRAP) void or32(RegisterID src, AbsoluteAddress dest) { move(TrustedImmPtr(dest.m_ptr), addressTempRegister); @@ -350,6 +382,7 @@ public: or32(src, dataTempRegister); store32(dataTempRegister, addressTempRegister); } +#endif void or32(TrustedImm32 imm, RegisterID dest) { @@ -461,6 +494,7 @@ public: sub32(dataTempRegister, dest); } +#if !defined(V4_BOOTSTRAP) void sub32(TrustedImm32 imm, AbsoluteAddress address) { load32(address.m_ptr, dataTempRegister); @@ -477,6 +511,7 @@ public: store32(dataTempRegister, address.m_ptr); } +#endif void xor32(Address src, RegisterID dest) { @@ -526,7 +561,8 @@ public: // operand objects to loads and store will be implicitly constructed if a // register is passed. -private: + // internal function, but public because of "using load32;" in template sub-classes to pull + // in the other public overloads. void load32(ArmAddress address, RegisterID dest) { if (address.type == ArmAddress::HasIndex) @@ -541,6 +577,7 @@ private: } } +private: void load16(ArmAddress address, RegisterID dest) { if (address.type == ArmAddress::HasIndex) @@ -646,11 +683,13 @@ public: load16(setupArmAddress(address), dest); } +#if !defined(V4_BOOTSTRAP) void load32(const void* address, RegisterID dest) { move(TrustedImmPtr(address), addressTempRegister); m_assembler.ldr(dest, addressTempRegister, ARMThumbImmediate::makeUInt16(0)); } +#endif ConvertibleLoadLabel convertibleLoadPtr(Address address, RegisterID dest) { @@ -755,6 +794,7 @@ public: store32(dataTempRegister, setupArmAddress(address)); } +#if !defined(V4_BOOTSTRAP) void store32(RegisterID src, const void* address) { move(TrustedImmPtr(address), addressTempRegister); @@ -766,12 +806,14 @@ public: move(imm, dataTempRegister); store32(dataTempRegister, address); } +#endif void store8(RegisterID src, BaseIndex address) { store8(src, setupArmAddress(address)); } +#if !defined(V4_BOOTSTRAP) void store8(RegisterID src, void* address) { move(TrustedImmPtr(address), addressTempRegister); @@ -783,6 +825,7 @@ public: move(imm, dataTempRegister); store8(dataTempRegister, address); } +#endif void store16(RegisterID src, BaseIndex address) { @@ -880,11 +923,13 @@ public: m_assembler.vmov(dest, src); } +#if !defined(V4_BOOTSTRAP) void loadDouble(const void* address, FPRegisterID dest) { move(TrustedImmPtr(address), addressTempRegister); m_assembler.vldr(dest, addressTempRegister, 0); } +#endif void storeDouble(FPRegisterID src, ImplicitAddress address) { @@ -916,11 +961,13 @@ public: m_assembler.fsts(ARMRegisters::asSingle(src), base, offset); } +#if !defined(V4_BOOTSTRAP) void storeDouble(FPRegisterID src, const void* address) { move(TrustedImmPtr(address), addressTempRegister); storeDouble(src, addressTempRegister); } +#endif void storeDouble(FPRegisterID src, BaseIndex address) { @@ -954,11 +1001,13 @@ public: m_assembler.vadd(dest, op1, op2); } +#if !defined(V4_BOOTSTRAP) void addDouble(AbsoluteAddress address, FPRegisterID dest) { loadDouble(address.m_ptr, fpTempRegister); m_assembler.vadd(dest, dest, fpTempRegister); } +#endif void divDouble(FPRegisterID src, FPRegisterID dest) { @@ -1037,6 +1086,7 @@ public: m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); } +#if !defined(V4_BOOTSTRAP) void convertInt32ToDouble(AbsoluteAddress address, FPRegisterID dest) { // Fixme: load directly into the fpr! @@ -1044,6 +1094,7 @@ public: m_assembler.vmov(fpTempRegister, dataTempRegister, dataTempRegister); m_assembler.vcvt_signedToFloatingPoint(dest, fpTempRegisterAsSingle()); } +#endif void convertUInt32ToDouble(RegisterID src, FPRegisterID dest, RegisterID /*scratch*/) { @@ -1197,7 +1248,7 @@ public: void push(RegisterID src) { // store preindexed with writeback - m_assembler.str(src, ARMRegisters::sp, -sizeof(void*), true, true); + m_assembler.str(src, ARMRegisters::sp, -4 /*sizeof(void*)*/, true, true); } void push(Address address) @@ -1239,10 +1290,12 @@ public: m_assembler.mov(dest, src); } +#if !defined(V4_BOOTSTRAP) void move(TrustedImmPtr imm, RegisterID dest) { move(TrustedImm32(imm), dest); } +#endif void swap(RegisterID reg1, RegisterID reg2) { @@ -1383,6 +1436,7 @@ public: return branch32(cond, addressTempRegister, right); } +#if !defined(V4_BOOTSTRAP) Jump branch32(RelationalCondition cond, AbsoluteAddress left, RegisterID right) { load32(left.m_ptr, dataTempRegister); @@ -1395,6 +1449,7 @@ public: load32(left.m_ptr, addressTempRegister); return branch32(cond, addressTempRegister, right); } +#endif Jump branch8(RelationalCondition cond, RegisterID left, TrustedImm32 right) { @@ -1451,6 +1506,7 @@ public: return branchTest32(cond, addressTempRegister, mask); } +#if !defined(V4_BOOTSTRAP) Jump branchTest8(ResultCondition cond, AbsoluteAddress address, TrustedImm32 mask = TrustedImm32(-1)) { // use addressTempRegister incase the branchTest8 we call uses dataTempRegister. :-/ @@ -1458,6 +1514,7 @@ public: load8(Address(addressTempRegister), addressTempRegister); return branchTest32(cond, addressTempRegister, mask); } +#endif void jump(RegisterID target) { @@ -1471,12 +1528,14 @@ public: m_assembler.bx(dataTempRegister); } +#if !defined(V4_BOOTSTRAP) void jump(AbsoluteAddress address) { move(TrustedImmPtr(address.m_ptr), dataTempRegister); load32(Address(dataTempRegister), dataTempRegister); m_assembler.bx(dataTempRegister); } +#endif // Arithmetic control flow operations: @@ -1517,6 +1576,7 @@ public: return branchAdd32(cond, dest, imm, dest); } +#if !defined(V4_BOOTSTRAP) Jump branchAdd32(ResultCondition cond, TrustedImm32 imm, AbsoluteAddress dest) { // Move the high bits of the address into addressTempRegister, @@ -1542,6 +1602,7 @@ public: return Jump(makeBranch(cond)); } +#endif Jump branchMul32(ResultCondition cond, RegisterID src1, RegisterID src2, RegisterID dest) { @@ -1712,6 +1773,7 @@ public: return DataLabel32(this); } +#if !defined(V4_BOOTSTRAP) ALWAYS_INLINE DataLabelPtr moveWithPatch(TrustedImmPtr imm, RegisterID dst) { padBeforePatch(); @@ -1739,7 +1801,8 @@ public: m_makeJumpPatchable = false; return PatchableJump(result); } - +#endif + PatchableJump patchableBranchTest32(ResultCondition cond, RegisterID reg, TrustedImm32 mask = TrustedImm32(-1)) { m_makeJumpPatchable = true; @@ -1756,6 +1819,7 @@ public: return PatchableJump(result); } +#if !defined(V4_BOOTSTRAP) PatchableJump patchableBranchPtrWithPatch(RelationalCondition cond, Address left, DataLabelPtr& dataLabel, TrustedImmPtr initialRightValue = TrustedImmPtr(0)) { m_makeJumpPatchable = true; @@ -1763,6 +1827,7 @@ public: m_makeJumpPatchable = false; return PatchableJump(result); } +#endif PatchableJump patchableJump() { @@ -1773,6 +1838,7 @@ public: return PatchableJump(result); } +#if !defined(V4_BOOTSTRAP) ALWAYS_INLINE DataLabelPtr storePtrWithPatch(TrustedImmPtr initialValue, ImplicitAddress address) { DataLabelPtr label = moveWithPatch(initialValue, dataTempRegister); @@ -1780,7 +1846,7 @@ public: return label; } ALWAYS_INLINE DataLabelPtr storePtrWithPatch(ImplicitAddress address) { return storePtrWithPatch(TrustedImmPtr(0), address); } - +#endif ALWAYS_INLINE Call tailRecursiveCall() { @@ -1801,6 +1867,7 @@ public: return m_assembler.executableOffsetFor(location); } +#if !defined(V4_BOOTSTRAP) static FunctionPtr readCallTarget(CodeLocationCall call) { return FunctionPtr(reinterpret_cast<void(*)()>(ARMv7Assembler::readCallTarget(call.dataLocation()))); @@ -1813,7 +1880,8 @@ public: const unsigned twoWordOpSize = 4; return label.labelAtOffset(-twoWordOpSize * 2); } - +#endif + static void revertJumpReplacementToBranchPtrWithPatch(CodeLocationLabel instructionStart, RegisterID rd, void* initialValue) { #if OS(LINUX) || OS(QNX) @@ -1927,9 +1995,10 @@ protected: } private: - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; friend class RepatchBuffer; +#if !defined(V4_BOOTSTRAP) static void linkCall(void* code, Call call, FunctionPtr function) { ARMv7Assembler::linkCall(code, call.m_label, function.value()); @@ -1944,6 +2013,7 @@ private: { ARMv7Assembler::relinkCall(call.dataLocation(), destination.executableAddress()); } +#endif bool m_makeJumpPatchable; }; diff --git a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h index 734e779c70..68584527fc 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerMIPS.h @@ -2802,7 +2802,7 @@ private: // Otherwise, we can emit any number of instructions. bool m_fixedWidth; - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; friend class RepatchBuffer; static void linkCall(void* code, Call call, FunctionPtr function) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerSH4.h b/src/3rdparty/masm/assembler/MacroAssemblerSH4.h index 56fb74d45b..1e5a3113bb 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerSH4.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerSH4.h @@ -2278,7 +2278,7 @@ protected: return static_cast<SH4Assembler::Condition>(cond); } private: - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; friend class RepatchBuffer; static void linkCall(void*, Call, FunctionPtr); diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86.h b/src/3rdparty/masm/assembler/MacroAssemblerX86.h index 9a33fe870e..742a4b48f7 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerX86.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerX86.h @@ -54,6 +54,38 @@ public: using MacroAssemblerX86Common::convertInt32ToDouble; using MacroAssemblerX86Common::branchTest8; +#if defined(V4_BOOTSTRAP) + void loadPtr(ImplicitAddress address, RegisterID dest) + { + load32(address, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + sub32(imm, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID dest) + { + add32(imm, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + add32(imm, src, dest); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + store32(src, address); + } + + Jump branchTest8(ResultCondition cond, ExtendedAddress address, TrustedImm32 mask = TrustedImm32(-1)) + { + return branchTest8(cond, Address(address.base, address.offset), mask); + } +#endif + void add32(TrustedImm32 imm, RegisterID src, RegisterID dest) { m_assembler.leal_mr(imm.m_value, src, dest); @@ -306,7 +338,7 @@ public: } private: - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; friend class RepatchBuffer; static void linkCall(void* code, Call call, FunctionPtr function) diff --git a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h index 9e74f1c29f..3566702413 100644 --- a/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h +++ b/src/3rdparty/masm/assembler/MacroAssemblerX86_64.h @@ -52,6 +52,33 @@ public: using MacroAssemblerX86Common::loadDouble; using MacroAssemblerX86Common::convertInt32ToDouble; +#if defined(V4_BOOTSTRAP) + void loadPtr(ImplicitAddress address, RegisterID dest) + { + load64(address, dest); + } + + void subPtr(TrustedImm32 imm, RegisterID dest) + { + sub64(imm, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID dest) + { + add64(imm, dest); + } + + void addPtr(TrustedImm32 imm, RegisterID src, RegisterID dest) + { + add64(imm, src, dest); + } + + void storePtr(RegisterID src, ImplicitAddress address) + { + store64(src, address); + } +#endif + void add32(TrustedImm32 imm, AbsoluteAddress address) { move(TrustedImmPtr(address.m_ptr), scratchRegister); @@ -634,7 +661,7 @@ public: } private: - friend class LinkBuffer; + template <typename, template <typename> class> friend class LinkBufferBase; friend class RepatchBuffer; static void linkCall(void* code, Call call, FunctionPtr function) diff --git a/src/3rdparty/masm/assembler/X86Assembler.h b/src/3rdparty/masm/assembler/X86Assembler.h index 1875ebaff0..24462ef38f 100644 --- a/src/3rdparty/masm/assembler/X86Assembler.h +++ b/src/3rdparty/masm/assembler/X86Assembler.h @@ -29,6 +29,7 @@ #if ENABLE(ASSEMBLER) && (CPU(X86) || CPU(X86_64)) #include "AssemblerBuffer.h" +#include "AbstractMacroAssembler.h" #include "JITCompilationEffort.h" #include <stdint.h> #include <wtf/Assertions.h> @@ -252,6 +253,45 @@ public: { } + template <typename LabelType> + class Jump { + template<class TemplateAssemblerType> + friend class AbstractMacroAssembler; + friend class Call; + template <typename, template <typename> class> friend class LinkBufferBase; + public: + Jump() + { + } + + Jump(AssemblerLabel jmp) + : m_label(jmp) + { + } + + LabelType label() const + { + LabelType result; + result.m_label = m_label; + return result; + } + + void link(AbstractMacroAssembler<X86Assembler>* masm) const + { + masm->m_assembler.linkJump(m_label, masm->m_assembler.label()); + } + + void linkTo(LabelType label, AbstractMacroAssembler<X86Assembler>* masm) const + { + masm->m_assembler.linkJump(m_label, label.label()); + } + + bool isSet() const { return m_label.isSet(); } + + private: + AssemblerLabel m_label; + }; + // Stack operations: void push_r(RegisterID reg) diff --git a/src/3rdparty/masm/masm.pri b/src/3rdparty/masm/masm.pri index afa1438974..6c301fea38 100644 --- a/src/3rdparty/masm/masm.pri +++ b/src/3rdparty/masm/masm.pri @@ -13,6 +13,7 @@ HEADERS += $$PWD/wtf/RawPointer.h winrt: SOURCES += $$PWD/wtf/OSAllocatorWinRT.cpp else:win32: SOURCES += $$PWD/wtf/OSAllocatorWin.cpp +else:integrity: SOURCES += $$PWD/wtf/OSAllocatorIntegrity.cpp else: SOURCES += $$PWD/wtf/OSAllocatorPosix.cpp HEADERS += $$PWD/wtf/OSAllocator.h @@ -30,7 +31,17 @@ HEADERS += $$PWD/stubs/WTFStubs.h SOURCES += $$PWD/stubs/Options.cpp -HEADERS += $$PWD/stubs/wtf/*.h +HEADERS += $$PWD/stubs/wtf/FastAllocBase.h \ + $$PWD/stubs/wtf/FastMalloc.h \ + $$PWD/stubs/wtf/Noncopyable.h \ + $$PWD/stubs/wtf/OwnPtr.h \ + $$PWD/stubs/wtf/PassOwnPtr.h \ + $$PWD/stubs/wtf/PassRefPtr.h \ + $$PWD/stubs/wtf/RefCounted.h \ + $$PWD/stubs/wtf/RefPtr.h \ + $$PWD/stubs/wtf/TypeTraits.h \ + $$PWD/stubs/wtf/UnusedParam.h \ + $$PWD/stubs/wtf/Vector.h SOURCES += $$PWD/disassembler/Disassembler.cpp SOURCES += $$PWD/disassembler/UDis86Disassembler.cpp @@ -66,8 +77,21 @@ SOURCES += $$PWD/disassembler/ARM64Disassembler.cpp SOURCES += $$PWD/disassembler/ARM64/A64DOpcode.cpp HEADERS += $$PWD/disassembler/ARM64/A64DOpcode.h -SOURCES += $$PWD/yarr/*.cpp -HEADERS += $$PWD/yarr/*.h +!qmldevtools_build { +SOURCES += $$PWD/yarr/YarrCanonicalizeUCS2.cpp \ + $$PWD/yarr/YarrInterpreter.cpp \ + $$PWD/yarr/YarrJIT.cpp \ + $$PWD/yarr/YarrPattern.cpp \ + $$PWD/yarr/YarrSyntaxChecker.cpp + +HEADERS += $$PWD/yarr/Yarr.h \ + $$PWD/yarr/YarrCanonicalizeUCS2.h \ + $$PWD/yarr/YarrInterpreter.h \ + $$PWD/yarr/YarrJIT.h \ + $$PWD/yarr/YarrParser.h \ + $$PWD/yarr/YarrPattern.h \ + $$PWD/yarr/YarrSyntaxChecker.h +} # # Generate RegExpJitTables.h diff --git a/src/3rdparty/masm/stubs/ExecutableAllocator.h b/src/3rdparty/masm/stubs/ExecutableAllocator.h index 8617229b06..3b84b5c986 100644 --- a/src/3rdparty/masm/stubs/ExecutableAllocator.h +++ b/src/3rdparty/masm/stubs/ExecutableAllocator.h @@ -61,7 +61,7 @@ namespace JSC { class JSGlobalData; struct ExecutableMemoryHandle : public RefCounted<ExecutableMemoryHandle> { - ExecutableMemoryHandle(QV4::ExecutableAllocator *allocator, int size) + ExecutableMemoryHandle(QV4::ExecutableAllocator *allocator, size_t size) : m_allocator(allocator) , m_size(size) { @@ -79,14 +79,14 @@ struct ExecutableMemoryHandle : public RefCounted<ExecutableMemoryHandle> { inline bool isManaged() const { return true; } void* start() { return m_allocation->start(); } - int sizeInBytes() { return m_size; } + size_t sizeInBytes() { return m_size; } QV4::ExecutableAllocator::ChunkOfPages *chunk() const { return m_allocator->chunkForAllocation(m_allocation); } QV4::ExecutableAllocator *m_allocator; QV4::ExecutableAllocator::Allocation *m_allocation; - int m_size; + size_t m_size; }; struct ExecutableAllocator { @@ -94,7 +94,7 @@ struct ExecutableAllocator { : realAllocator(alloc) {} - PassRefPtr<ExecutableMemoryHandle> allocate(JSGlobalData&, int size, void*, int) + PassRefPtr<ExecutableMemoryHandle> allocate(JSGlobalData&, size_t size, void*, int) { return adoptRef(new ExecutableMemoryHandle(realAllocator, size)); } @@ -107,7 +107,7 @@ struct ExecutableAllocator { size = size + (iaddr - roundAddr); addr = reinterpret_cast<void*>(roundAddr); -#if ENABLE(ASSEMBLER_WX_EXCLUSIVE) +#if ENABLE(ASSEMBLER_WX_EXCLUSIVE) && !defined(V4_BOOTSTRAP) # if OS(WINDOWS) DWORD oldProtect; # if !OS(WINRT) @@ -140,6 +140,7 @@ struct ExecutableAllocator { size = size + (iaddr - roundAddr); addr = reinterpret_cast<void*>(roundAddr); +#if !defined(V4_BOOTSTRAP) #if ENABLE(ASSEMBLER_WX_EXCLUSIVE) # if OS(WINDOWS) DWORD oldProtect; @@ -161,6 +162,10 @@ struct ExecutableAllocator { #else # error "Only W^X is supported" #endif +#else + (void)addr; // suppress unused parameter warning + (void)size; // suppress unused parameter warning +#endif } QV4::ExecutableAllocator *realAllocator; diff --git a/src/3rdparty/masm/wtf/MathExtras.h b/src/3rdparty/masm/wtf/MathExtras.h index 75e3670367..3740d54beb 100644 --- a/src/3rdparty/masm/wtf/MathExtras.h +++ b/src/3rdparty/masm/wtf/MathExtras.h @@ -88,7 +88,7 @@ inline double wtf_ceil(double x) { return copysign(ceil(x), x); } #endif -#if OS(SOLARIS) +#if OS(SOLARIS) && __cplusplus < 201103L namespace std { diff --git a/src/3rdparty/masm/wtf/OSAllocatorIntegrity.cpp b/src/3rdparty/masm/wtf/OSAllocatorIntegrity.cpp new file mode 100644 index 0000000000..451ca147d1 --- /dev/null +++ b/src/3rdparty/masm/wtf/OSAllocatorIntegrity.cpp @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) Rolland Dudemaine All rights reserved. +** 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$ +** +****************************************************************************/ + +#include "config.h" +#include "OSAllocator.h" + +#include <INTEGRITY.h> +#include <memory_region.h> +#include <set> +#include <wtf/Assertions.h> +#include <wtf/UnusedParam.h> + +#define ASP_PAGESIZE 0x1000 + +namespace WTF { +struct MRPair { + mutable MemoryRegion pmr; + mutable MemoryRegion vmr; + + mutable bool mapped; + + Address start; + + MRPair(Address _start = 0) : + pmr(0), + vmr(0), + mapped(false), + start(_start) + {} + + bool operator<(const MRPair& rhs) const + { + return this->start < rhs.start; + } +}; + +class MRContainer +{ +private: + std::set<MRPair> mrset; + LocalMutex iteratorGuard; +public: + MRContainer() { + CheckSuccess(CreateLocalMutex(&iteratorGuard)); + } + const MRPair* getMRPair(Address start) { + WaitForLocalMutex(iteratorGuard); + auto pairIterator = mrset.find(MRPair(start)); + const MRPair* result = ((pairIterator == mrset.end()) ? NULL : &(*pairIterator)); + ReleaseLocalMutex(iteratorGuard); + return result; + } + Error deleteMRPair(const MRPair* pair) { + int erased = 0; + WaitForLocalMutex(iteratorGuard); + erased = mrset.erase(*pair); + ReleaseLocalMutex(iteratorGuard); + if(erased == 1) + return Success; + else + return ArgumentError; /* An exception could be thrown in this case */ + } + Error insertMRPair(MRPair* pair) { + WaitForLocalMutex(iteratorGuard); + auto inserted = mrset.insert(*pair); + ReleaseLocalMutex(iteratorGuard); + if(inserted.second == true) + return Success; + else + return Failure; /* An exception could be thrown in this case */ + } + ~MRContainer() { + CheckSuccess(CloseLocalMutex(iteratorGuard)); + } +}; + +static MRContainer memoryRegionsContainer; + +Error setAttributes(MemoryRegion mr, bool writable, bool executable) +{ + Value attributes = MEMORY_READ; + if(writable) + attributes |= MEMORY_WRITE; + if(executable) + attributes |= MEMORY_EXEC; + return SetMemoryRegionAttributes(mr, attributes); +} + +void* OSAllocator::reserveUncommitted(size_t bytes, Usage usage, bool writable, bool executable) +{ + MemoryRegion VMR; + + Address virtualStart, length; + + CheckSuccess(AllocateAnyMemoryRegion(__ghs_VirtualMemoryRegionPool, bytes, &VMR)); + CheckSuccess(GetMemoryRegionAddresses(VMR, &virtualStart, &length)); + Address addressIterator = virtualStart; + for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) { + MRPair pair; + CheckSuccess(SplitMemoryRegion(VMR, ASP_PAGESIZE, &pair.vmr)); + CheckSuccess(setAttributes(pair.vmr, writable, executable)); + pair.start = addressIterator; + + memoryRegionsContainer.insertMRPair(&pair); + addressIterator += ASP_PAGESIZE; + } + + CheckSuccess(CloseMemoryRegion(VMR)); + return (void*)virtualStart; +} + +void* OSAllocator::reserveAndCommit(size_t bytes, Usage usage, bool writable, bool executable, bool includesGuardPages) +{ + MemoryRegion VMR; + + Address virtualStart, length; + + CheckSuccess(AllocateAnyMemoryRegion(__ghs_VirtualMemoryRegionPool, bytes, &VMR)); + CheckSuccess(GetMemoryRegionAddresses(VMR, &virtualStart, &length)); + + Address addressIterator = virtualStart; + for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) { + MRPair pair; + pair.start = addressIterator; + CheckSuccess(SplitMemoryRegion(VMR, ASP_PAGESIZE, &pair.vmr)); + CheckSuccess(setAttributes(pair.vmr, writable, executable)); + /* Do not map the first and the last pages if guard pages are required */ + if(!includesGuardPages || (i!=0 && i!= (bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE -1)) + { + CheckSuccess(GetPageFromAddressSpaceFreeList(GetCurrentAddressSpace(), &pair.pmr)); + CheckSuccess(MapMemoryRegion(pair.vmr, pair.pmr)); + pair.mapped = true; + } + + memoryRegionsContainer.insertMRPair(&pair); + addressIterator += ASP_PAGESIZE; + } + + CheckSuccess(CloseMemoryRegion(VMR)); + return (void*)virtualStart; +} + +void OSAllocator::commit(void* address, size_t bytes, bool writable, bool executable) +{ + for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) + { + const MRPair* pair = memoryRegionsContainer.getMRPair((Address)address); + if(pair == NULL) + return; + CheckSuccess(setAttributes(pair->vmr, writable, executable)); + CheckSuccess(GetPageFromAddressSpaceFreeList(GetCurrentAddressSpace(), &pair->pmr)); + CheckSuccess(MapMemoryRegion(pair->vmr, pair->pmr)); + pair->mapped = true; + address = (char*)address + ASP_PAGESIZE; + } +} + +void OSAllocator::decommit(void* address, size_t bytes) +{ + for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) + { + const MRPair* pair = memoryRegionsContainer.getMRPair((Address)address); + if(pair == NULL) + return; + if(pair->mapped == false) + continue; + + CheckSuccess(UnmapMemoryRegion(pair->vmr)); + CheckSuccess(PutPageOnAddressSpaceFreeList(GetCurrentAddressSpace(), pair->pmr)); + pair->mapped = false; + address = (char*)address + ASP_PAGESIZE; + } +} + +void OSAllocator::releaseDecommitted(void* address, size_t bytes) +{ + for(int i=0; i<(bytes + ASP_PAGESIZE -1)/ASP_PAGESIZE; i++) + { + const MRPair* pair = memoryRegionsContainer.getMRPair((Address)address); + if(pair == NULL) + return; + /* Check if the memory is still committed */ + if(pair->mapped == true) + { + CheckSuccess(UnmapMemoryRegion(pair->vmr)); + CheckSuccess(PutPageOnAddressSpaceFreeList(GetCurrentAddressSpace(), pair->pmr)); + pair->mapped = false; + } + CheckSuccess(AddToMemoryPool(__ghs_VirtualMemoryRegionPool, pair->vmr)); + address = (char*)address + ASP_PAGESIZE; + + memoryRegionsContainer.deleteMRPair(pair); + } +} +} // namespace WTF diff --git a/src/3rdparty/masm/wtf/Platform.h b/src/3rdparty/masm/wtf/Platform.h index bc62c381db..7f2023a68a 100644 --- a/src/3rdparty/masm/wtf/Platform.h +++ b/src/3rdparty/masm/wtf/Platform.h @@ -949,10 +949,6 @@ #define WTF_USE_ACCESSIBILITY_CONTEXT_MENUS 1 #endif -#if CPU(ARM_THUMB2) || CPU(ARM64) -#define ENABLE_BRANCH_COMPACTION 1 -#endif - #if !defined(ENABLE_THREADING_LIBDISPATCH) && HAVE(DISPATCH_H) #define ENABLE_THREADING_LIBDISPATCH 1 #elif !defined(ENABLE_THREADING_OPENMP) && defined(_OPENMP) diff --git a/src/3rdparty/masm/yarr/YarrJIT.cpp b/src/3rdparty/masm/yarr/YarrJIT.cpp index d8211ec4b2..e4f2d97759 100644 --- a/src/3rdparty/masm/yarr/YarrJIT.cpp +++ b/src/3rdparty/masm/yarr/YarrJIT.cpp @@ -39,7 +39,7 @@ using namespace WTF; namespace JSC { namespace Yarr { template<YarrJITCompileMode compileMode> -class YarrGenerator : private MacroAssembler { +class YarrGenerator : private DefaultMacroAssembler { friend void jitCompile(JSGlobalData*, YarrCodeBlock& jitObject, const String& pattern, unsigned& numSubpatterns, const char*& error, bool ignoreCase, bool multiline); #if CPU(ARM) @@ -599,7 +599,7 @@ class YarrGenerator : private MacroAssembler { } // Called at the end of code generation to link all return addresses. - void linkDataLabels(LinkBuffer& linkBuffer) + void linkDataLabels(LinkBuffer<JSC::DefaultMacroAssembler>& linkBuffer) { ASSERT(isEmpty()); for (unsigned i = 0; i < m_backtrackRecords.size(); ++i) @@ -2676,7 +2676,7 @@ public: backtrack(); // Link & finalize the code. - LinkBuffer linkBuffer(*globalData, this, REGEXP_CODE_ID); + LinkBuffer<JSC::DefaultMacroAssembler> linkBuffer(*globalData, this, REGEXP_CODE_ID); m_backtrackingState.linkDataLabels(linkBuffer); if (compileMode == MatchOnly) { diff --git a/src/imports/imports.pro b/src/imports/imports.pro index cad02a2980..20ade45fc8 100644 --- a/src/imports/imports.pro +++ b/src/imports/imports.pro @@ -5,16 +5,17 @@ SUBDIRS += \ qtqml \ folderlistmodel \ localstorage \ - models \ - statemachine + models qtConfig(settings): SUBDIRS += settings +qtConfig(statemachine): SUBDIRS += statemachine qtHaveModule(quick) { SUBDIRS += \ layouts \ qtquick2 \ window \ + sharedimage \ testlib qtConfig(opengl(es1|es2)?): \ diff --git a/src/imports/layouts/qquicklayout.cpp b/src/imports/layouts/qquicklayout.cpp index 55ee3b63c6..df64b593d9 100644 --- a/src/imports/layouts/qquicklayout.cpp +++ b/src/imports/layouts/qquicklayout.cpp @@ -696,13 +696,20 @@ QQuickLayout::QQuickLayout(QQuickLayoutPrivate &dd, QQuickItem *parent) { } +static QQuickItemPrivate::ChangeTypes changeTypes = + QQuickItemPrivate::SiblingOrder + | QQuickItemPrivate::ImplicitWidth + | QQuickItemPrivate::ImplicitHeight + | QQuickItemPrivate::Destroyed + | QQuickItemPrivate::Visibility; + QQuickLayout::~QQuickLayout() { d_func()->m_isReady = false; const auto childItems = d_func()->childItems; for (QQuickItem *child : childItems) - QQuickItemPrivate::get(child)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder); + QQuickItemPrivate::get(child)->removeItemChangeListener(this, changeTypes); } QQuickLayoutAttached *QQuickLayout::qmlAttachedProperties(QObject *object) @@ -766,14 +773,14 @@ void QQuickLayout::itemChange(ItemChange change, const ItemChangeData &value) Q_D(QQuickLayout); QQuickItem *item = value.item; qmlobject_connect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem())); - QQuickItemPrivate::get(item)->addItemChangeListener(this, QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | QQuickItemPrivate::Destroyed | QQuickItemPrivate::Visibility); + QQuickItemPrivate::get(item)->addItemChangeListener(this, changeTypes); d->m_hasItemChangeListeners = true; if (isReady()) updateLayoutItems(); } else if (change == ItemChildRemovedChange) { QQuickItem *item = value.item; qmlobject_disconnect(item, QQuickItem, SIGNAL(baselineOffsetChanged(qreal)), this, QQuickLayout, SLOT(invalidateSenderItem())); - QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | QQuickItemPrivate::Destroyed | QQuickItemPrivate::Visibility); + QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes); if (isReady()) updateLayoutItems(); } @@ -821,7 +828,7 @@ void QQuickLayout::deactivateRecur() // When deleting a layout with children, there is no reason for the children to inform the layout that their // e.g. visibility got changed. The layout already knows that all its children will eventually become invisible, so // we therefore remove its change listener. - QQuickItemPrivate::get(item)->removeItemChangeListener(this, QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::ImplicitWidth | QQuickItemPrivate::ImplicitHeight | QQuickItemPrivate::Destroyed | QQuickItemPrivate::Visibility); + QQuickItemPrivate::get(item)->removeItemChangeListener(this, changeTypes); if (QQuickLayout *layout = qobject_cast<QQuickLayout*>(item)) layout->deactivateRecur(); } diff --git a/src/imports/layouts/qquickstacklayout.cpp b/src/imports/layouts/qquickstacklayout.cpp index a223dd0374..0bfe63816d 100644 --- a/src/imports/layouts/qquickstacklayout.cpp +++ b/src/imports/layouts/qquickstacklayout.cpp @@ -307,7 +307,11 @@ void QQuickStackLayout::rearrange(const QSizeF &newSize) QQuickItem *item = itemAt(d->currentIndex); Q_ASSERT(item); item->setPosition(QPointF(0,0)); // ### respect alignment? - item->setSize(newSize.expandedTo(hints.min()).boundedTo(hints.max())); + const QSizeF oldSize(item->width(), item->height()); + const QSizeF effectiveNewSize = newSize.expandedTo(hints.min()).boundedTo(hints.max()); + item->setSize(effectiveNewSize); + if (effectiveNewSize == oldSize) + item->polish(); QQuickLayout::rearrange(newSize); } diff --git a/src/imports/localstorage/plugin.cpp b/src/imports/localstorage/plugin.cpp index d3ea93c80a..60b8dad5fb 100644 --- a/src/imports/localstorage/plugin.cpp +++ b/src/imports/localstorage/plugin.cpp @@ -219,24 +219,6 @@ QQmlSqlDatabaseData::~QQmlSqlDatabaseData() { } -static QString qmlsqldatabase_databasesPath(QV4::ExecutionEngine *engine) -{ - return engine->qmlEngine()->offlineStoragePath() + - QDir::separator() + QLatin1String("Databases"); -} - -static void qmlsqldatabase_initDatabasesPath(QV4::ExecutionEngine *engine) -{ - QString databasesPath = qmlsqldatabase_databasesPath(engine); - if (!QDir().mkpath(databasesPath)) - qWarning() << "LocalStorage: can't create path - " << databasesPath; -} - -static QString qmlsqldatabase_databaseFile(const QString& connectionName, QV4::ExecutionEngine *engine) -{ - return qmlsqldatabase_databasesPath(engine) + QDir::separator() + connectionName; -} - static ReturnedValue qmlsqldatabase_rows_index(const QQmlSqlDatabaseWrapper *r, ExecutionEngine *v4, quint32 index, bool *hasProperty = 0) { Scope scope(v4); @@ -450,7 +432,8 @@ static ReturnedValue qmlsqldatabase_changeVersion(CallContext *ctx) if (ok) { *w->d()->version = to_version; #if QT_CONFIG(settings) - QSettings ini(qmlsqldatabase_databaseFile(db.connectionName(), scope.engine) + QLatin1String(".ini"), QSettings::IniFormat); + const QQmlEnginePrivate *enginePrivate = QQmlEnginePrivate::get(scope.engine->qmlEngine()); + QSettings ini(enginePrivate->offlineStorageDatabaseDirectory() + db.connectionName() + QLatin1String(".ini"), QSettings::IniFormat); ini.setValue(QLatin1String("Version"), to_version); #endif } @@ -723,24 +706,23 @@ void QQuickLocalStorage::openDatabaseSync(QQmlV4Function *args) if (scope.engine->qmlEngine()->offlineStoragePath().isEmpty()) V4THROW_SQL2(SQLEXCEPTION_DATABASE_ERR, QQmlEngine::tr("SQL: can't create database, offline storage is disabled.")); - qmlsqldatabase_initDatabasesPath(scope.engine); - - QSqlDatabase database; - QV4::ScopedValue v(scope); QString dbname = (v = (*args)[0])->toQStringNoThrow(); QString dbversion = (v = (*args)[1])->toQStringNoThrow(); QString dbdescription = (v = (*args)[2])->toQStringNoThrow(); int dbestimatedsize = (v = (*args)[3])->toInt32(); FunctionObject *dbcreationCallback = (v = (*args)[4])->as<FunctionObject>(); - - QCryptographicHash md5(QCryptographicHash::Md5); - md5.addData(dbname.toUtf8()); - QString dbid(QLatin1String(md5.result().toHex())); - - QString basename = qmlsqldatabase_databaseFile(dbid, scope.engine); + QString basename = args->v4engine()->qmlEngine()->offlineStorageDatabaseFilePath(dbname); + QFileInfo dbFile(basename); + if (!QDir().mkpath(dbFile.dir().absolutePath())) { + const QString message = QQmlEngine::tr("LocalStorage: can't create path %1"). + arg(QDir::toNativeSeparators(dbFile.dir().absolutePath())); + V4THROW_SQL2(SQLEXCEPTION_DATABASE_ERR, message); + } + QString dbid = dbFile.fileName(); bool created = false; QString version = dbversion; + QSqlDatabase database; { QSettings ini(basename+QLatin1String(".ini"),QSettings::IniFormat); diff --git a/src/imports/sharedimage/plugin.cpp b/src/imports/sharedimage/plugin.cpp new file mode 100644 index 0000000000..f20edc641c --- /dev/null +++ b/src/imports/sharedimage/plugin.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 <qqmlextensionplugin.h> +#include <qqmlengine.h> +#include <sharedimageprovider.h> + + +/*! + \qmlmodule QtQuick.SharedImage 1 + \title Qt Quick Shared Image Provider + \ingroup qmlmodules + \brief Adds an image provider which utilizes shared CPU memory + + \section2 Summary + + This module provides functionality to save memory in use cases where + several Qt Quick applications use the same local image files. It does this + by placing the decoded QImage data in shared system memory, making it + accessible to all the processes (see QSharedMemory). + + This module only shares CPU memory. It does not provide sharing of GPU + memory or textures. + + \section2 Usage + + To use this module, import it like this: + \code + import QtQuick.SharedImage 1.0 + \endcode + + The sharing functionality is provided through a QQuickImageProvider. Use + the "image:" scheme for the URL source of the image, followed by the + identifier \e shared, followed by the image file path. For example: + + \code + Image { source: "image://shared/usr/share/wallpapers/mybackground.jpg" } + \endcode + + This will look for the file \e /usr/share/wallpapers/mybackground.jpg. + The first process that does this will read the image file + using normal Qt image loading. The decoded image data will then be placed + in shared memory, using the full file path as key. Later processes + requesting the same image will discover that the data is already available + in shared memory. They will then use that instead of loading the image file + again. + + The shared image data will be kept available until the last process has deleted + its last reference to the shared image, at which point it is automatically released. + + If system memory sharing is not available, the shared image provider falls + back to normal, unshared image loading. + + The file path must be absolute. To use a relative path, make it absolute + using \e Qt.resolvedUrl() and replace the URL scheme. For example: + + \code + ... + property string imagePrefix: Qt.resolvedUrl("../myimages/").replace("file://", "image://shared/") + Image { source: imagePrefix + "myimage.png" } + \endcode + + The shared image module does not provide any directly usable QML types. +*/ + +static void initResources() +{ +#ifdef QT_STATIC + Q_INIT_RESOURCE(qmake_QtQuick_SharedImage); +#endif +} + +QT_BEGIN_NAMESPACE + +class QtQuickSharedImagePlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid) +public: + QtQuickSharedImagePlugin(QObject *parent = 0) : QQmlExtensionPlugin(parent) { initResources(); } + + void registerTypes(const char *uri) Q_DECL_OVERRIDE + { + Q_ASSERT(uri == QStringLiteral("QtQuick.SharedImage")); + // Need to register *something* to let our version number be known: + qmlRegisterTypeNotAvailable(uri, 1, 0, "nosuchtype", QStringLiteral("Just a dummy type, do not use")); + } + + void initializeEngine(QQmlEngine *engine, const char *uri) override + { + Q_UNUSED(uri); + engine->addImageProvider("shared", new SharedImageProvider); + } +}; + +QT_END_NAMESPACE + +#include "plugin.moc" diff --git a/src/imports/sharedimage/qmldir b/src/imports/sharedimage/qmldir new file mode 100644 index 0000000000..64a5aa8ac1 --- /dev/null +++ b/src/imports/sharedimage/qmldir @@ -0,0 +1,3 @@ +module QtQuick.SharedImage +plugin sharedimageplugin +classname QtQuickSharedImagePlugin diff --git a/src/imports/sharedimage/qsharedimageloader.cpp b/src/imports/sharedimage/qsharedimageloader.cpp new file mode 100644 index 0000000000..65cbd92bb4 --- /dev/null +++ b/src/imports/sharedimage/qsharedimageloader.cpp @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 "qsharedimageloader_p.h" +#include <private/qobject_p.h> +#include <private/qimage_p.h> +#include <QSharedMemory> + + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcSharedImage, "qt.quick.sharedimage"); + +struct SharedImageHeader { + quint8 magic; + quint8 version; + quint16 offset; + qint32 width; + qint32 height; + qint32 bpl; + QImage::Format format; +}; +Q_STATIC_ASSERT(sizeof(SharedImageHeader) % 4 == 0); + +#ifndef QT_NO_SHAREDMEMORY +struct SharedImageInfo { + QString path; + QPointer<QSharedMemory> shmp; +}; + +void cleanupSharedImage(void *cleanupInfo) +{ + if (!cleanupInfo) + return; + SharedImageInfo *sii = static_cast<SharedImageInfo *>(cleanupInfo); + qCDebug(lcSharedImage) << "Cleanup called for" << sii->path; + if (sii->shmp.isNull()) { + qCDebug(lcSharedImage) << "shm is 0 for" << sii->path; + return; + } + QSharedMemory *shm = sii->shmp.data(); + sii->shmp.clear(); + delete shm; // destructor detaches + delete sii; +} +#else +void cleanupSharedImage(void *) {} +#endif + +class QSharedImageLoaderPrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QSharedImageLoader) + +public: + QSharedImageLoaderPrivate() + : QObjectPrivate() + {} + + QImage load(const QString &path, QSharedImageLoader::ImageParameters *params); + + void storeImageToMem(void *data, const QImage &img); + + bool verifyMem(const void *data, int size); + + QImage createImageFromMem(const void *data, void *cleanupInfo); + +}; + + +void QSharedImageLoaderPrivate::storeImageToMem(void *data, const QImage &img) +{ + Q_ASSERT(data && !img.isNull()); + + SharedImageHeader *h = static_cast<SharedImageHeader *>(data); + h->magic = 'Q'; + h->version = 1; + h->offset = sizeof(SharedImageHeader); + h->width = img.width(); + h->height = img.height(); + h->bpl = img.bytesPerLine(); + h->format = img.format(); + + uchar *p = static_cast<uchar *>(data) + sizeof(SharedImageHeader); + memcpy(p, img.constBits(), img.byteCount()); +} + + +bool QSharedImageLoaderPrivate::verifyMem(const void *data, int size) +{ + if (!data || size < int(sizeof(SharedImageHeader))) + return false; + + const SharedImageHeader *h = static_cast<const SharedImageHeader *>(data); + if ((h->magic != 'Q') + || (h->version < 1) + || (h->offset < sizeof(SharedImageHeader)) + || (h->width <= 0) + || (h->height <= 0) + || (h->bpl <= 0) + || (h->format <= QImage::Format_Invalid) + || (h->format >= QImage::NImageFormats)) { + return false; + } + + int availSize = size - h->offset; + if (h->height * h->bpl > availSize) + return false; + if ((qt_depthForFormat(h->format) * h->width * h->height) > (8 * availSize)) + return false; + + return true; +} + + +QImage QSharedImageLoaderPrivate::createImageFromMem(const void *data, void *cleanupInfo) +{ + const SharedImageHeader *h = static_cast<const SharedImageHeader *>(data); + const uchar *p = static_cast<const uchar *>(data) + h->offset; + + QImage img(p, h->width, h->height, h->bpl, h->format, cleanupSharedImage, cleanupInfo); + return img; +} + + +QImage QSharedImageLoaderPrivate::load(const QString &path, QSharedImageLoader::ImageParameters *params) +{ +#ifndef QT_NO_SHAREDMEMORY + Q_Q(QSharedImageLoader); + + QImage nil; + if (path.isEmpty()) + return nil; + + QScopedPointer<QSharedMemory> shm(new QSharedMemory(q->key(path, params))); + bool locked = false; + + if (!shm->attach(QSharedMemory::ReadOnly)) { + QImage img = q->loadFile(path, params); + if (img.isNull()) + return nil; + int size = sizeof(SharedImageHeader) + img.byteCount(); + if (shm->create(size)) { + qCDebug(lcSharedImage) << "Created new shm segment of size" << size << "for image" << path; + if (!shm->lock()) { + qCDebug(lcSharedImage) << "Lock1 failed!?" << shm->errorString(); + return nil; + } + locked = true; + storeImageToMem(shm->data(), img); + } else if (shm->error() == QSharedMemory::AlreadyExists) { + // race handling: other process may have created the share while + // we loaded the image, so try again to just attach + if (!shm->attach(QSharedMemory::ReadOnly)) { + qCDebug(lcSharedImage) << "Attach to existing failed?" << shm->errorString(); + return nil; + } + } else { + qCDebug(lcSharedImage) << "Create failed?" << shm->errorString(); + return nil; + } + } + + Q_ASSERT(shm->isAttached()); + + if (!locked) { + if (!shm->lock()) { + qCDebug(lcSharedImage) << "Lock2 failed!?" << shm->errorString(); + return nil; + } + locked = true; + } + + if (!verifyMem(shm->constData(), shm->size())) { + qCDebug(lcSharedImage) << "Verifymem failed!?"; + shm->unlock(); + return nil; + } + + QSharedMemory *shmp = shm.take(); + SharedImageInfo *sii = new SharedImageInfo; + sii->path = path; + sii->shmp = shmp; + QImage shImg = createImageFromMem(shmp->constData(), sii); + + if (!shmp->unlock()) { + qCDebug(lcSharedImage) << "UnLock failed!?"; + } + + return shImg; +#else + Q_UNUSED(path); + Q_UNUSED(params); + return QImage(); +#endif +} + + +QSharedImageLoader::QSharedImageLoader(QObject *parent) + : QObject(*new QSharedImageLoaderPrivate, parent) +{ +} + +QSharedImageLoader::~QSharedImageLoader() +{ +} + +QImage QSharedImageLoader::load(const QString &path, ImageParameters *params) +{ + Q_D(QSharedImageLoader); + + return d->load(path, params); +} + +QImage QSharedImageLoader::loadFile(const QString &path, ImageParameters *params) +{ + Q_UNUSED(params); + + return QImage(path); +} + +QString QSharedImageLoader::key(const QString &path, ImageParameters *params) +{ + Q_UNUSED(params); + + return path; +} + + +QT_END_NAMESPACE diff --git a/src/imports/sharedimage/qsharedimageloader_p.h b/src/imports/sharedimage/qsharedimageloader_p.h new file mode 100644 index 0000000000..afb50e5088 --- /dev/null +++ b/src/imports/sharedimage/qsharedimageloader_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 QSHAREDIMAGELOADER_H +#define QSHAREDIMAGELOADER_H + +#include <QImage> +#include <QVariant> +#include <QLoggingCategory> + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcSharedImage); + +class QSharedImageLoaderPrivate; + +class QSharedImageLoader : public QObject +{ + Q_OBJECT + Q_DECLARE_PRIVATE(QSharedImageLoader) + +public: + enum ImageParameter { + OriginalSize = 0, + RequestedSize, + NumImageParameters + }; + typedef QVector<QVariant> ImageParameters; + + QSharedImageLoader(QObject *parent = Q_NULLPTR); + ~QSharedImageLoader(); + + QImage load(const QString &path, ImageParameters *params = Q_NULLPTR); + +protected: + virtual QImage loadFile(const QString &path, ImageParameters *params); + virtual QString key(const QString &path, ImageParameters *params); + +private: + Q_DISABLE_COPY(QSharedImageLoader) +}; + +QT_END_NAMESPACE + +#endif // QSHAREDIMAGELOADER_H diff --git a/src/imports/sharedimage/sharedimage.pro b/src/imports/sharedimage/sharedimage.pro new file mode 100644 index 0000000000..523de66ac1 --- /dev/null +++ b/src/imports/sharedimage/sharedimage.pro @@ -0,0 +1,17 @@ +CXX_MODULE = qml +TARGET = sharedimageplugin +TARGETPATH = QtQuick/SharedImage +IMPORT_VERSION = 1.0 + +QT *= quick qml gui-private core-private + +SOURCES += \ + plugin.cpp \ + sharedimageprovider.cpp \ + qsharedimageloader.cpp + +HEADERS += \ + sharedimageprovider.h \ + qsharedimageloader_p.h + +load(qml_plugin) diff --git a/src/imports/sharedimage/sharedimageprovider.cpp b/src/imports/sharedimage/sharedimageprovider.cpp new file mode 100644 index 0000000000..2dd3a130e9 --- /dev/null +++ b/src/imports/sharedimage/sharedimageprovider.cpp @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 <sharedimageprovider.h> +#include <qsharedimageloader_p.h> +#include <qquickimageprovider.h> +#include <private/qimage_p.h> +#include <QImageReader> +#include <QFileInfo> +#include <QDir> + +class QuickSharedImageLoader : public QSharedImageLoader +{ + Q_OBJECT + friend class SharedImageProvider; + +public: + QuickSharedImageLoader(QObject *parent = Q_NULLPTR) + : QSharedImageLoader(parent) + { + } + +protected: + QImage loadFile(const QString &path, ImageParameters *params) override + { + QImageReader imgio(path); + QSize realSize = imgio.size(); + QSize requestSize = params ? params->value(RequestedSize).toSize() : QSize(); + + // Following qquickpixmapcache's readImage, from here... + const bool force_scale = imgio.format() == "svg" || imgio.format() == "svgz"; + + if (requestSize.width() > 0 || requestSize.height() > 0) { + QSize s = realSize; + qreal ratio = 0.0; + if (requestSize.width() && (force_scale || requestSize.width() < s.width())) { + ratio = qreal(requestSize.width())/s.width(); + } + if (requestSize.height() && (force_scale || requestSize.height() < s.height())) { + qreal hr = qreal(requestSize.height())/s.height(); + if (ratio == 0.0 || hr < ratio) + ratio = hr; + } + if (ratio > 0.0) { + s.setHeight(qRound(s.height() * ratio)); + s.setWidth(qRound(s.width() * ratio)); + imgio.setScaledSize(s); + } + } + // ... to here + + QImage image; + if (imgio.read(&image)) { + if (realSize.isEmpty()) + realSize = image.size(); + // Make sure we have acceptable format for texture uploader, or it will convert & lose sharing + // This mimics the testing & conversion normally done by the quick pixmapcache & texturefactory + if (image.format() != QImage::Format_RGB32 && image.format() != QImage::Format_ARGB32_Premultiplied) { + QImage::Format newFmt = QImage::Format_RGB32; + if (image.hasAlphaChannel() && image.data_ptr()->checkForAlphaPixels()) + newFmt = QImage::Format_ARGB32_Premultiplied; + qCDebug(lcSharedImage) << "Convert on load from format" << image.format() << "to" << newFmt; + image = image.convertToFormat(newFmt); + } + } + + if (params && params->count() > OriginalSize) + params->replace(OriginalSize, realSize); + + return image; + } + + QString key(const QString &path, ImageParameters *params) override + { + QSize reqSz = params->value(RequestedSize).toSize(); + if (!reqSz.isValid()) + return path; + + QString key = path + QStringLiteral("_%1x%2").arg(reqSz.width()).arg(reqSz.height()); + qCDebug(lcSharedImage) << "KEY:" << key; + return key; + } +}; + + +SharedImageProvider::SharedImageProvider() + : QQuickImageProvider(QQuickImageProvider::Image), loader(new QuickSharedImageLoader) +{ +} + +QImage SharedImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) +{ + QFileInfo fi(QDir::root(), id); + QString path = fi.canonicalFilePath(); + if (path.isEmpty()) + return QImage(); + + QSharedImageLoader::ImageParameters params(QSharedImageLoader::NumImageParameters); + params[QSharedImageLoader::RequestedSize].setValue(requestedSize); + + QImage img = loader->load(path, ¶ms); + if (img.isNull()) { + // May be sharing problem, fall back to normal local load + img = loader->loadFile(path, ¶ms); + if (!img.isNull()) + qCWarning(lcSharedImage) << "Sharing problem; loading" << id << "unshared"; + } + + //... QSize realSize = params.value(QSharedImageLoader::OriginalSize).toSize(); + // quickpixmapcache's readImage() reports back the original size, prior to requestedSize scaling, in the *size + // parameter. That value is currently ignored by quick however, which only cares about the present size of the + // returned image. So handling and sharing of info on pre-scaled size is currently not implemented. + if (size) { + *size = img.size(); + } + + return img; +} + +#include "sharedimageprovider.moc" diff --git a/src/imports/sharedimage/sharedimageprovider.h b/src/imports/sharedimage/sharedimageprovider.h new file mode 100644 index 0000000000..a2f6b6ef2f --- /dev/null +++ b/src/imports/sharedimage/sharedimageprovider.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the plugins 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 SHAREDIMAGEPROVIDER_H +#define SHAREDIMAGEPROVIDER_H + +#include <QQuickImageProvider> +#include <QScopedPointer> + +class QuickSharedImageLoader; + +class SharedImageProvider : public QQuickImageProvider +{ +public: + SharedImageProvider(); + + QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override; + +protected: + QScopedPointer<QuickSharedImageLoader> loader; +}; +#endif // SHAREDIMAGEPROVIDER_H diff --git a/src/particles/qquickcustomparticle_p.h b/src/particles/qquickcustomparticle_p.h index 1d48786a41..e2292cb33b 100644 --- a/src/particles/qquickcustomparticle_p.h +++ b/src/particles/qquickcustomparticle_p.h @@ -53,7 +53,6 @@ #include "qquickparticlepainter_p.h" #include <private/qquickopenglshadereffectnode_p.h> #include <private/qquickopenglshadereffect_p.h> -#include <QSignalMapper> QT_BEGIN_NAMESPACE diff --git a/src/particles/qquickimageparticle.cpp b/src/particles/qquickimageparticle.cpp index ccfebddb0e..fde491b1ef 100644 --- a/src/particles/qquickimageparticle.cpp +++ b/src/particles/qquickimageparticle.cpp @@ -118,15 +118,15 @@ public: Q_ASSERT(!m_fragment_code.isNull()); } - const char *vertexShader() const { return m_vertex_code.constData(); } - const char *fragmentShader() const { return m_fragment_code.constData(); } + const char *vertexShader() const override { return m_vertex_code.constData(); } + const char *fragmentShader() const override { return m_fragment_code.constData(); } - QList<QByteArray> attributes() const { + QList<QByteArray> attributes() const override { return QList<QByteArray>() << "vPosTex" << "vData" << "vVec" << "vColor" << "vDeformVec" << "vRotation"; }; - void initialize() { + void initialize() override { QSGSimpleMaterialShader<TabledMaterialData>::initialize(); program()->bind(); program()->setUniformValue("_qt_texture", 0); @@ -138,7 +138,7 @@ public: m_opacitytable_id = program()->uniformLocation("opacitytable"); } - void updateState(const TabledMaterialData* d, const TabledMaterialData*) { + void updateState(const TabledMaterialData* d, const TabledMaterialData*) override { glFuncs->glActiveTexture(GL_TEXTURE1); d->colorTable->bind(); @@ -192,15 +192,15 @@ public: Q_ASSERT(!m_fragment_code.isNull()); } - const char *vertexShader() const { return m_vertex_code.constData(); } - const char *fragmentShader() const { return m_fragment_code.constData(); } + const char *vertexShader() const override { return m_vertex_code.constData(); } + const char *fragmentShader() const override { return m_fragment_code.constData(); } - QList<QByteArray> attributes() const { + QList<QByteArray> attributes() const override { return QList<QByteArray>() << "vPosTex" << "vData" << "vVec" << "vColor" << "vDeformVec" << "vRotation"; }; - void initialize() { + void initialize() override { QSGSimpleMaterialShader<DeformableMaterialData>::initialize(); program()->bind(); program()->setUniformValue("_qt_texture", 0); @@ -209,7 +209,7 @@ public: m_entry_id = program()->uniformLocation("entry"); } - void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) { + void updateState(const DeformableMaterialData* d, const DeformableMaterialData*) override { d->texture->bind(); program()->setUniformValue(m_timestamp_id, (float) d->timestamp); @@ -259,15 +259,15 @@ public: Q_ASSERT(!m_fragment_code.isNull()); } - const char *vertexShader() const { return m_vertex_code.constData(); } - const char *fragmentShader() const { return m_fragment_code.constData(); } + const char *vertexShader() const override { return m_vertex_code.constData(); } + const char *fragmentShader() const override { return m_fragment_code.constData(); } - QList<QByteArray> attributes() const { + QList<QByteArray> attributes() const override { return QList<QByteArray>() << "vPosTex" << "vData" << "vVec" << "vColor" << "vDeformVec" << "vRotation" << "vAnimData" << "vAnimPos"; } - void initialize() { + void initialize() override { QSGSimpleMaterialShader<SpriteMaterialData>::initialize(); program()->bind(); program()->setUniformValue("_qt_texture", 0); @@ -280,7 +280,7 @@ public: m_opacitytable_id = program()->uniformLocation("opacitytable"); } - void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) { + void updateState(const SpriteMaterialData* d, const SpriteMaterialData*) override { glFuncs->glActiveTexture(GL_TEXTURE1); d->colorTable->bind(); @@ -333,10 +333,10 @@ public: Q_ASSERT(!m_fragment_code.isNull()); } - const char *vertexShader() const { return m_vertex_code.constData(); } - const char *fragmentShader() const { return m_fragment_code.constData(); } + const char *vertexShader() const override { return m_vertex_code.constData(); } + const char *fragmentShader() const override { return m_fragment_code.constData(); } - void activate() { + void activate() override { QSGSimpleMaterialShader<ColoredMaterialData>::activate(); #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN) glEnable(GL_POINT_SPRITE); @@ -344,7 +344,7 @@ public: #endif } - void deactivate() { + void deactivate() override { QSGSimpleMaterialShader<ColoredMaterialData>::deactivate(); #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN) glDisable(GL_POINT_SPRITE); @@ -352,11 +352,11 @@ public: #endif } - QList<QByteArray> attributes() const { + QList<QByteArray> attributes() const override { return QList<QByteArray>() << "vPos" << "vData" << "vVec" << "vColor"; } - void initialize() { + void initialize() override { QSGSimpleMaterialShader<ColoredMaterialData>::initialize(); program()->bind(); program()->setUniformValue("_qt_texture", 0); @@ -365,7 +365,7 @@ public: m_entry_id = program()->uniformLocation("entry"); } - void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) { + void updateState(const ColoredMaterialData* d, const ColoredMaterialData*) override { d->texture->bind(); program()->setUniformValue(m_timestamp_id, (float) d->timestamp); @@ -407,10 +407,10 @@ public: Q_ASSERT(!m_fragment_code.isNull()); } - const char *vertexShader() const { return m_vertex_code.constData(); } - const char *fragmentShader() const { return m_fragment_code.constData(); } + const char *vertexShader() const override { return m_vertex_code.constData(); } + const char *fragmentShader() const override { return m_fragment_code.constData(); } - void activate() { + void activate() override { QSGSimpleMaterialShader<SimpleMaterialData>::activate(); #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN) glEnable(GL_POINT_SPRITE); @@ -418,7 +418,7 @@ public: #endif } - void deactivate() { + void deactivate() override { QSGSimpleMaterialShader<SimpleMaterialData>::deactivate(); #if !defined(QT_OPENGL_ES_2) && !defined(Q_OS_WIN) glDisable(GL_POINT_SPRITE); @@ -426,11 +426,11 @@ public: #endif } - QList<QByteArray> attributes() const { + QList<QByteArray> attributes() const override { return QList<QByteArray>() << "vPos" << "vData" << "vVec"; } - void initialize() { + void initialize() override { QSGSimpleMaterialShader<SimpleMaterialData>::initialize(); program()->bind(); program()->setUniformValue("_qt_texture", 0); @@ -439,7 +439,7 @@ public: m_entry_id = program()->uniformLocation("entry"); } - void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) { + void updateState(const SimpleMaterialData* d, const SimpleMaterialData*) override { d->texture->bind(); program()->setUniformValue(m_timestamp_id, (float) d->timestamp); diff --git a/src/particles/qquickparticlesystem.cpp b/src/particles/qquickparticlesystem.cpp index b60180b2ed..99e278238b 100644 --- a/src/particles/qquickparticlesystem.cpp +++ b/src/particles/qquickparticlesystem.cpp @@ -575,9 +575,6 @@ QQuickParticleSystem::QQuickParticleSystem(QQuickItem *parent) : m_paused(false), m_empty(true) { - connect(&m_painterMapper, SIGNAL(mapped(QObject*)), - this, SLOT(loadPainter(QObject*))); - m_debugMode = qmlParticlesDebug(); } @@ -615,8 +612,8 @@ void QQuickParticleSystem::registerParticlePainter(QQuickParticlePainter* p) qDebug() << "Registering Painter" << p << "to" << this; //TODO: a way to Unregister emitters, painters and affectors m_painters << QPointer<QQuickParticlePainter>(p);//###Set or uniqueness checking? - connect(p, SIGNAL(groupsChanged(QStringList)), - &m_painterMapper, SLOT(map())); + + connect(p, &QQuickParticlePainter::groupsChanged, this, [this, p] { this->loadPainter(p); }, Qt::QueuedConnection); loadPainter(p); } @@ -802,13 +799,11 @@ void QQuickParticleSystem::reset() } -void QQuickParticleSystem::loadPainter(QObject *p) +void QQuickParticleSystem::loadPainter(QQuickParticlePainter *painter) { - if (!m_componentComplete || !p) + if (!m_componentComplete || !painter) return; - QQuickParticlePainter* painter = qobject_cast<QQuickParticlePainter*>(p); - Q_ASSERT(painter);//XXX for (QQuickParticleGroupData* sg : groupData) { sg->painters.removeOne(painter); } diff --git a/src/particles/qquickparticlesystem_p.h b/src/particles/qquickparticlesystem_p.h index de39b436e2..92dca40419 100644 --- a/src/particles/qquickparticlesystem_p.h +++ b/src/particles/qquickparticlesystem_p.h @@ -56,7 +56,6 @@ #include <QVector> #include <QHash> #include <QPointer> -#include <QSignalMapper> #include <private/qquicksprite_p.h> #include <QAbstractAnimation> #include <QtQml/qqml.h> @@ -393,7 +392,7 @@ protected: private Q_SLOTS: void emittersChanged(); - void loadPainter(QObject* p); + void loadPainter(QQuickParticlePainter *p); void createEngine(); //Not invoked by sprite engine, unlike Sprite uses void particleStateChange(int idx); @@ -461,8 +460,6 @@ private: QSet<int> m_reusableIndexes; bool m_componentComplete; - QSignalMapper m_painterMapper; - QSignalMapper m_emitterMapper; bool m_paused; bool m_allDead; bool m_empty; diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp index aed2759383..a4bad5618b 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4datacollector.cpp @@ -273,9 +273,11 @@ bool QV4DataCollector::collectScope(QJsonObject *dict, int frameNr, int scopeNr) QV4::ScopedObject scopeObject(scope, engine()->newObject()); Q_ASSERT(names.size() == m_collectedRefs.size()); - for (int i = 0, ei = m_collectedRefs.size(); i != ei; ++i) - scopeObject->put(engine(), names.at(i), - QV4::Value::fromReturnedValue(getValue(m_collectedRefs.at(i)))); + QV4::ScopedString propName(scope); + for (int i = 0, ei = m_collectedRefs.size(); i != ei; ++i) { + propName = engine()->newString(names.at(i)); + scopeObject->put(propName, QV4::Value::fromReturnedValue(getValue(m_collectedRefs.at(i)))); + } Ref scopeObjectRef = addRef(scopeObject); dict->insert(QStringLiteral("ref"), qint64(scopeObjectRef)); diff --git a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp index df316c1ae6..d5fadad50a 100644 --- a/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp +++ b/src/plugins/qmltooling/qmldbg_debugger/qv4debugjob.cpp @@ -90,12 +90,15 @@ void JavaScriptJob::run() QQmlContextPrivate *ctxtPriv = QQmlContextPrivate::get(qmlRootContext); QV4::ScopedObject withContext(scope, engine->newObject()); + QV4::ScopedString k(scope); + QV4::ScopedValue v(scope); for (int ii = 0; ii < ctxtPriv->instances.count(); ++ii) { QObject *object = ctxtPriv->instances.at(ii); if (QQmlContext *context = qmlContext(object)) { if (QQmlContextData *cdata = QQmlContextData::get(context)) { - QV4::ScopedValue v(scope, QV4::QObjectWrapper::wrap(engine, object)); - withContext->put(engine, cdata->findObjectId(object), v); + v = QV4::QObjectWrapper::wrap(engine, object); + k = engine->newString(cdata->findObjectId(object)); + withContext->put(k, v); } } } diff --git a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp index 6152853917..97e4b4e3e4 100644 --- a/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_local/qlocalclientconnection.cpp @@ -57,15 +57,15 @@ public: QLocalClientConnection(); ~QLocalClientConnection(); - void setServer(QQmlDebugServer *server); - bool setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress); - bool setFileName(const QString &filename, bool block); + void setServer(QQmlDebugServer *server) override; + bool setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress) override; + bool setFileName(const QString &filename, bool block) override; - bool isConnected() const; - void disconnect(); + bool isConnected() const override; + void disconnect() override; - void waitForConnection(); - void flush(); + void waitForConnection() override; + void flush() override; private: void connectionEstablished(); diff --git a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp index f6f48e43a4..bcfb3b8a75 100644 --- a/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp +++ b/src/plugins/qmltooling/qmldbg_server/qqmldebugserver.cpp @@ -114,7 +114,7 @@ public: return m_pluginName; } - void run(); + void run() override; private: QQmlDebugServerImpl *m_server; diff --git a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp index b305c3f535..af4f5292ba 100644 --- a/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp +++ b/src/plugins/qmltooling/qmldbg_tcp/qtcpserverconnection.cpp @@ -55,15 +55,15 @@ public: QTcpServerConnection(); ~QTcpServerConnection(); - void setServer(QQmlDebugServer *server); - bool setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress); - bool setFileName(const QString &fileName, bool block); + void setServer(QQmlDebugServer *server) override; + bool setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress) override; + bool setFileName(const QString &fileName, bool block) override; - bool isConnected() const; - void disconnect(); + bool isConnected() const override; + void disconnect() override; - void waitForConnection(); - void flush(); + void waitForConnection() override; + void flush() override; private: void newConnection(); diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h index f1ab580a84..f828843227 100644 --- a/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h +++ b/src/plugins/scenegraph/d3d12/qsgd3d12layer_p.h @@ -86,6 +86,7 @@ public: void setDevicePixelRatio(qreal ratio) override; void setMirrorHorizontal(bool mirror) override; void setMirrorVertical(bool mirror) override; + void setSamples(int) override { } public Q_SLOTS: void markDirtyTexture() override; diff --git a/src/plugins/scenegraph/openvg/qsgopenvgglyphnode.cpp b/src/plugins/scenegraph/openvg/qsgopenvgglyphnode.cpp index 8be2a97034..9cf4184c20 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvgglyphnode.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvgglyphnode.cpp @@ -43,6 +43,7 @@ #include "qsgopenvghelpers.h" #include "qsgopenvgfontglyphcache.h" #include "qopenvgoffscreensurface.h" +#include <cmath> QT_BEGIN_NAMESPACE @@ -143,7 +144,7 @@ void QSGOpenVGGlyphNode::render() vgLoadMatrix(transform().constData()); } else { vgLoadIdentity(); - offscreenSurface = new QOpenVGOffscreenSurface(QSize(ceil(m_bounding_rect.width()), ceil(m_bounding_rect.height()))); + offscreenSurface = new QOpenVGOffscreenSurface(QSize(std::ceil(m_bounding_rect.width()), std::ceil(m_bounding_rect.height()))); offscreenSurface->makeCurrent(); } diff --git a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp index be437303bc..0bd51cbf46 100644 --- a/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp +++ b/src/plugins/scenegraph/openvg/qsgopenvginternalrectanglenode.cpp @@ -39,7 +39,7 @@ #include "qsgopenvginternalrectanglenode.h" #include "qsgopenvghelpers.h" - +#include <cmath> #include <VG/vgu.h> QSGOpenVGInternalRectangleNode::QSGOpenVGInternalRectangleNode() @@ -207,9 +207,9 @@ void QSGOpenVGInternalRectangleNode::render() vgLoadIdentity(); if (m_radius > 0) { // Fallback to rendering to an image for rounded rects with perspective transforms - if (m_offscreenSurface == nullptr || m_offscreenSurface->size() != QSize(ceil(m_rect.width()), ceil(m_rect.height()))) { + if (m_offscreenSurface == nullptr || m_offscreenSurface->size() != QSize(std::ceil(m_rect.width()), std::ceil(m_rect.height()))) { delete m_offscreenSurface; - m_offscreenSurface = new QOpenVGOffscreenSurface(QSize(ceil(m_rect.width()), ceil(m_rect.height()))); + m_offscreenSurface = new QOpenVGOffscreenSurface(QSize(std::ceil(m_rect.width()), std::ceil(m_rect.height()))); } m_offscreenSurface->makeCurrent(); diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 1de5dfa6fa..fa66d3a6e3 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -39,7 +39,10 @@ SOURCES += \ unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp -qtConfig(qml-interpreter) { +qtConfig(private_tests): LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD +} + +qmldevtools_build|qtConfig(qml-interpreter) { HEADERS += \ $$PWD/qv4instr_moth_p.h \ $$PWD/qv4isel_moth_p.h @@ -48,6 +51,3 @@ qtConfig(qml-interpreter) { $$PWD/qv4isel_moth.cpp } - -qtConfig(private_tests): LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD -} diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 54d0cb4f46..030f485504 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1605,7 +1605,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil scan.leaveEnvironment(); scan.leaveEnvironment(); - _env = 0; + _variableEnvironment = 0; _function = _module->functions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0)); for (int i = 0; i < functions.count(); ++i) { @@ -2061,4 +2061,156 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis return 0; } +IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) + : unit(qmlData) + , output(output) +{ + pool = output->jsParserEngine.pool(); +} + +void IRLoader::load() +{ + output->jsGenerator.stringTable.clear(); + for (uint i = 0; i < unit->stringTableSize; ++i) + output->jsGenerator.stringTable.registerString(unit->stringAt(i)); + + for (quint32 i = 0; i < unit->nImports; ++i) + output->imports << unit->importAt(i); + + if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { + QmlIR::Pragma *p = New<QmlIR::Pragma>(); + p->location = QV4::CompiledData::Location(); + p->type = QmlIR::Pragma::PragmaSingleton; + output->pragmas << p; + } + + output->indexOfRootObject = unit->indexOfRootObject; + + for (uint i = 0; i < unit->nObjects; ++i) { + const QV4::CompiledData::Object *serializedObject = unit->objectAt(i); + QmlIR::Object *object = loadObject(serializedObject); + output->objects.append(object); + } +} + +struct FakeExpression : public QQmlJS::AST::NullExpression +{ + FakeExpression(int start, int length) + : location(start, length) + {} + + virtual QQmlJS::AST::SourceLocation firstSourceLocation() const + { return location; } + + virtual QQmlJS::AST::SourceLocation lastSourceLocation() const + { return location; } + +private: + QQmlJS::AST::SourceLocation location; +}; + +QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) +{ + QmlIR::Object *object = pool->New<QmlIR::Object>(); + object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); + + object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; + object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; + + object->location = serializedObject->location; + object->locationOfIdProperty = serializedObject->locationOfIdProperty; + + QVector<int> functionIndices; + functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); + + for (uint i = 0; i < serializedObject->nBindings; ++i) { + QmlIR::Binding *b = pool->New<QmlIR::Binding>(); + *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i]; + object->bindings->append(b); + if (b->type == QV4::CompiledData::Binding::Type_Script) { + functionIndices.append(b->value.compiledScriptIndex); + b->value.compiledScriptIndex = functionIndices.count() - 1; + + QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>(); + foe->disableAcceleratedLookups = true; + foe->nameIndex = 0; + + QQmlJS::AST::ExpressionNode *expr; + + if (b->stringIndex != quint32(0)) { + const int start = output->code.length(); + const QString script = output->stringAt(b->stringIndex); + const int length = script.length(); + output->code.append(script); + expr = new (pool) FakeExpression(start, length); + } else + expr = new (pool) QQmlJS::AST::NullExpression(); + foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy + object->functionsAndExpressions->append(foe); + } + } + + Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); + + for (uint i = 0; i < serializedObject->nSignals; ++i) { + const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); + QmlIR::Signal *s = pool->New<QmlIR::Signal>(); + s->nameIndex = serializedSignal->nameIndex; + s->location = serializedSignal->location; + s->parameters = pool->New<QmlIR::PoolList<QmlIR::SignalParameter> >(); + + for (uint i = 0; i < serializedSignal->nParameters; ++i) { + QmlIR::SignalParameter *p = pool->New<QmlIR::SignalParameter>(); + *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i); + s->parameters->append(p); + } + + object->qmlSignals->append(s); + } + + const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); + for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { + QmlIR::Property *p = pool->New<QmlIR::Property>(); + *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty; + object->properties->append(p); + } + + QQmlJS::Engine *jsParserEngine = &output->jsParserEngine; + + const QV4::CompiledData::LEUInt32 *functionIdx = serializedObject->functionOffsetTable(); + for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { + QmlIR::Function *f = pool->New<QmlIR::Function>(); + const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); + + functionIndices.append(*functionIdx); + f->index = functionIndices.count() - 1; + f->location = compiledFunction->location; + f->nameIndex = compiledFunction->nameIndex; + + QQmlJS::AST::FormalParameterList *paramList = 0; + const QV4::CompiledData::LEUInt32 *formalNameIdx = compiledFunction->formalsTable(); + for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) { + const QString formal = unit->stringAt(*formalNameIdx); + QStringRef paramNameRef = jsParserEngine->newStringRef(formal); + + if (paramList) + paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef); + else + paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef); + } + + if (paramList) + paramList = paramList->finish(); + + const QString name = unit->stringAt(compiledFunction->nameIndex); + f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/0); + + object->functions->append(f); + } + + object->runtimeFunctionIndices.allocate(pool, functionIndices); + + return object; +} + #endif // V4_BOOTSTRAP diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 95756845c3..2022112e07 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -622,6 +622,21 @@ private: int _importedScriptsTemp; }; +struct IRLoader { + IRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); + + void load(); + +private: + QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); + + template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } + + const QV4::CompiledData::Unit *unit; + QmlIR::Document *output; + QQmlJS::MemoryPool *pool; +}; + } // namespace QmlIR QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index ab2b0553a9..85267225be 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -57,12 +57,12 @@ QT_BEGIN_NAMESPACE QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, - QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &importCache, + QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) : resolvedTypes(resolvedTypeCache) , engine(engine) , typeData(typeData) - , importCache(importCache) + , typeNameCache(typeNameCache) , document(parsedQML) { } @@ -138,7 +138,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() sss.scan(); } - QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, importCache, &document->jsGenerator.stringTable); + QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return nullptr; @@ -164,7 +164,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit; compilationUnit = document->javaScriptCompilationUnit; - compilationUnit->importCache = importCache; + compilationUnit->typeNameCache = typeNameCache; compilationUnit->resolvedTypes = resolvedTypes; compilationUnit->propertyCaches = std::move(m_propertyCaches); Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->data->nObjects)); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index de6abb4ced..2b59e7e42f 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -89,7 +89,7 @@ struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); + QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); // --- interface used by QQmlPropertyCacheCreator typedef QmlIR::Object CompiledObject; @@ -139,7 +139,7 @@ private: QList<QQmlError> errors; QQmlEnginePrivate *engine; QQmlTypeData *typeData; - QQmlRefPointer<QQmlTypeNameCache> importCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QmlIR::Document *document; // index is string index of type name (use obj->inheritedTypeNameIndex) QHash<int, QQmlCustomParser*> customParsers; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 46c27fc735..0afc97e4bf 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -73,10 +73,29 @@ static inline void setLocation(IR::Stmt *s, const SourceLocation &loc) s->location = loc; } +static bool cjumpCanHandle(IR::AluOp op) +{ + switch (op) { + case IR::OpIn: + case IR::OpInstanceof: + case IR::OpEqual: + case IR::OpNotEqual: + case IR::OpGe: + case IR::OpGt: + case IR::OpLe: + case IR::OpLt: + case IR::OpStrictEqual: + case IR::OpStrictNotEqual: + return true; + default: + return false; + } +} + Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) : _cg(cg) , _sourceCode(sourceCode) - , _env(0) + , _variableEnvironment(0) , _allowFuncDecls(true) , defaultProgramMode(defaultProgramMode) { @@ -90,17 +109,17 @@ void Codegen::ScanFunctions::operator()(Node *node) void Codegen::ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) { - Environment *e = _cg->newEnvironment(node, _env, compilationMode); + Environment *e = _cg->newEnvironment(node, _variableEnvironment, compilationMode); if (!e->isStrict) e->isStrict = _cg->_strictMode; _envStack.append(e); - _env = e; + _variableEnvironment = e; } void Codegen::ScanFunctions::leaveEnvironment() { _envStack.pop(); - _env = _envStack.isEmpty() ? 0 : _envStack.top(); + _variableEnvironment = _envStack.isEmpty() ? 0 : _envStack.top(); } void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) @@ -116,7 +135,7 @@ void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) continue; QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); if (str == QLatin1String("use strict")) { - _env->isStrict = true; + _variableEnvironment->isStrict = true; } else { // TODO: give a warning. } @@ -131,7 +150,7 @@ void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) void Codegen::ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) { - if (_env->isStrict) { + if (_variableEnvironment->isStrict) { if (name == QLatin1String("implements") || name == QLatin1String("interface") || name == QLatin1String("let") @@ -149,7 +168,7 @@ void Codegen::ScanFunctions::checkForArguments(AST::FormalParameterList *paramet { while (parameters) { if (parameters->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; parameters = parameters->next; } } @@ -168,19 +187,19 @@ void Codegen::ScanFunctions::endVisit(Program *) bool Codegen::ScanFunctions::visit(CallExpression *ast) { - if (! _env->hasDirectEval) { + if (! _variableEnvironment->hasDirectEval) { if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { if (id->name == QLatin1String("eval")) { - if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) - _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; - _env->hasDirectEval = true; + if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown) + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed; + _variableEnvironment->hasDirectEval = true; } } } int argc = 0; for (ArgumentList *it = ast->arguments; it; it = it->next) ++argc; - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); return true; } @@ -189,7 +208,7 @@ bool Codegen::ScanFunctions::visit(NewMemberExpression *ast) int argc = 0; for (ArgumentList *it = ast->arguments; it; it = it->next) ++argc; - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); return true; } @@ -205,26 +224,38 @@ bool Codegen::ScanFunctions::visit(ArrayLiteral *ast) for (Elision *elision = ast->elision->next; elision; elision = elision->next) ++index; } - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, index); return true; } bool Codegen::ScanFunctions::visit(VariableDeclaration *ast) { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); checkName(ast->name, ast->identifierToken); if (ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); + return false; + } + QString name = ast->name.toString(); + const Environment::Member *m = 0; + if (_variableEnvironment->memberInfo(name, &m)) { + if (m->isLexicallyScoped() || ast->isLexicallyScoped()) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + return false; + } + } + _variableEnvironment->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration, ast->scope); return true; } bool Codegen::ScanFunctions::visit(IdentifierExpression *ast) { checkName(ast->name, ast->identifierToken); - if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed; return true; } @@ -256,7 +287,7 @@ bool Codegen::ScanFunctions::visit(FunctionExpression *ast) void Codegen::ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName, bool isExpression) { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); } @@ -277,7 +308,7 @@ bool Codegen::ScanFunctions::visit(ObjectLiteral *ast) if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) ++argc; } - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); Node::accept(ast->properties, this); @@ -309,7 +340,7 @@ void Codegen::ScanFunctions::endVisit(FunctionDeclaration *) bool Codegen::ScanFunctions::visit(WithStatement *ast) { - if (_env->isStrict) { + if (_variableEnvironment->isStrict) { _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); return false; } @@ -319,7 +350,7 @@ bool Codegen::ScanFunctions::visit(WithStatement *ast) bool Codegen::ScanFunctions::visit(DoWhileStatement *ast) { { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); } Node::accept(ast->expression, this); @@ -331,7 +362,7 @@ bool Codegen::ScanFunctions::visit(ForStatement *ast) { Node::accept(ast->condition, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -342,7 +373,7 @@ bool Codegen::ScanFunctions::visit(LocalForStatement *ast) { Node::accept(ast->condition, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -352,7 +383,7 @@ bool Codegen::ScanFunctions::visit(ForEachStatement *ast) { Node::accept(ast->initialiser, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -362,7 +393,7 @@ bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) { Node::accept(ast->declaration, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -370,12 +401,12 @@ bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) { bool Codegen::ScanFunctions::visit(ThisExpression *) { - _env->usesThis = true; + _variableEnvironment->usesThis = true; return false; } bool Codegen::ScanFunctions::visit(Block *ast) { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _env->isStrict ? false : _allowFuncDecls); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _variableEnvironment->isStrict ? false : _allowFuncDecls); Node::accept(ast->statements, this); return false; } @@ -383,26 +414,26 @@ bool Codegen::ScanFunctions::visit(Block *ast) { void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) { bool wasStrict = false; - if (_env) { - _env->hasNestedFunctions = true; + if (_variableEnvironment) { + _variableEnvironment->hasNestedFunctions = true; // The identifier of a function expression cannot be referenced from the enclosing environment. if (expr) - _env->enter(name, Environment::FunctionDefinition, expr); + _variableEnvironment->enter(name, Environment::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); if (name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - wasStrict = _env->isStrict; + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + wasStrict = _variableEnvironment->isStrict; } enterEnvironment(ast, FunctionCode); checkForArguments(formals); - _env->isNamedFunctionExpression = isExpression && !name.isEmpty(); - _env->formals = formals; + _variableEnvironment->isNamedFunctionExpression = isExpression && !name.isEmpty(); + _variableEnvironment->formals = formals; if (body) checkDirectivePrologue(body->elements); - if (wasStrict || _env->isStrict) { + if (wasStrict || _variableEnvironment->isStrict) { QStringList args; for (FormalParameterList *it = formals; it; it = it->next) { QString arg = it->name.toString(); @@ -426,7 +457,7 @@ Codegen::Codegen(bool strict) , _block(0) , _exitBlock(0) , _returnAddress(0) - , _env(0) + , _variableEnvironment(0) , _loop(0) , _labelledStatement(0) , _scopeAndFinally(0) @@ -446,7 +477,7 @@ void Codegen::generateFromProgram(const QString &fileName, Q_ASSERT(node); _module = module; - _env = 0; + _variableEnvironment = 0; _module->setFileName(fileName); @@ -465,7 +496,7 @@ void Codegen::generateFromFunctionExpression(const QString &fileName, { _module = module; _module->setFileName(fileName); - _env = 0; + _variableEnvironment = 0; ScanFunctions scan(this, sourceCode, GlobalCode); // fake a global environment @@ -482,14 +513,14 @@ void Codegen::generateFromFunctionExpression(const QString &fileName, void Codegen::enterEnvironment(Node *node) { - _env = _envMap.value(node); - Q_ASSERT(_env); + _variableEnvironment = _envMap.value(node); + Q_ASSERT(_variableEnvironment); } void Codegen::leaveEnvironment() { - Q_ASSERT(_env); - _env = _env->parent; + Q_ASSERT(_variableEnvironment); + _variableEnvironment = _variableEnvironment->parent; } void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) @@ -611,7 +642,7 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AS if (IR::Const *c1 = left->asConst()) { if (IR::Const *c2 = right->asConst()) { - if (c1->type == IR::NumberType && c2->type == IR::NumberType) { + if ((c1->type & IR::NumberType) && (c2->type & IR::NumberType)) { switch (op) { case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value); case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0); @@ -659,20 +690,20 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AS } } - if (!left->asTemp() && !left->asArgLocal()) { + if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), left), loc); left = _block->TEMP(t); } - if (!right->asTemp() && !right->asArgLocal()) { + if (!right->asTemp() && !right->asArgLocal() && !right->asConst()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), right), loc); right = _block->TEMP(t); } - Q_ASSERT(left->asTemp() || left->asArgLocal()); - Q_ASSERT(right->asTemp() || right->asArgLocal()); + Q_ASSERT(left->asTemp() || left->asArgLocal() || left->asConst()); + Q_ASSERT(right->asTemp() || right->asArgLocal() || right->asConst()); return _block->BINOP(op, left, right); } @@ -715,7 +746,7 @@ IR::Stmt *Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock if (hasError) return 0; - if (! (cond->asTemp() || cond->asBinop())) { + if (! (cond->asTemp() || (cond->asBinop() && cjumpCanHandle(cond->asBinop()->op)) )) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), cond); cond = _block->TEMP(t); @@ -842,9 +873,16 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) Q_ASSERT(expr.code); initializer = *expr; - int initialized = _block->newTemp(); - move(_block->TEMP(initialized), initializer); - move(identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), _block->TEMP(initialized)); + IR::Expr *lhs = identifier(ast->name.toString(), ast->identifierToken.startLine, + ast->identifierToken.startColumn); + + if (lhs->asArgLocal()) { + move(lhs, initializer); + } else { + int initialized = _block->newTemp(); + move(_block->TEMP(initialized), initializer); + move(lhs, _block->TEMP(initialized)); + } } void Codegen::variableDeclarationList(VariableDeclarationList *ast) @@ -1274,14 +1312,7 @@ bool Codegen::visit(BinaryExpression *ast) if (_expr.accept(cx)) { setLocation(cjump(binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken), _expr.iftrue, _expr.iffalse), ast->operatorToken); } else { - IR::Expr *e = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); - if (e->asConst() || e->asString()) - _expr.code = e; - else { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), e), ast->operatorToken); - _expr.code = _block->TEMP(t); - } + _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); } break; } @@ -1307,14 +1338,7 @@ bool Codegen::visit(BinaryExpression *ast) if (hasError) return false; - IR::Expr *e = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); - if (e->asConst() || e->asString()) - _expr.code = e; - else { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), e), ast->operatorToken); - _expr.code = _block->TEMP(t); - } + _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); break; } @@ -1389,7 +1413,7 @@ bool Codegen::visit(DeleteExpression *ast) return false; // Temporaries cannot be deleted IR::ArgLocal *al = expr->asArgLocal(); - if (al && al->index < static_cast<unsigned>(_env->members.size())) { + if (al && al->index < static_cast<unsigned>(_variableEnvironment->members.size())) { // Trying to delete a function argument might throw. if (_function->isStrict) { throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); @@ -1418,7 +1442,7 @@ bool Codegen::visit(DeleteExpression *ast) } if (expr->asTemp() || (expr->asArgLocal() && - expr->asArgLocal()->index >= static_cast<unsigned>(_env->members.size()))) { + expr->asArgLocal()->index >= static_cast<unsigned>(_variableEnvironment->members.size()))) { _expr.code = _block->CONST(IR::BoolType, 1); return false; } @@ -1469,7 +1493,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) return 0; uint scope = 0; - Environment *e = _env; + Environment *e = _variableEnvironment; IR::Function *f = _function; while (f && e->parent) { @@ -1500,7 +1524,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) if (IR::Expr *fallback = fallbackNameLookup(name, line, col)) return fallback; - if (!e->parent && (!f || !f->insideWithOrCatch) && _env->compilationMode != EvalCode && e->compilationMode != QmlBinding) + if (!e->parent && (!f || !f->insideWithOrCatch) && _variableEnvironment->compilationMode != EvalCode && e->compilationMode != QmlBinding) return _block->GLOBALNAME(name, line, col); // global context or with. Lookup by name @@ -1995,7 +2019,7 @@ bool Codegen::visit(FunctionDeclaration * ast) if (hasError) return false; - if (_env->compilationMode == QmlBinding) + if (_variableEnvironment->compilationMode == QmlBinding) move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); _expr.accept(nx); return false; @@ -2019,26 +2043,26 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, IR::BasicBlock *entryBlock = function->newBasicBlock(0); IR::BasicBlock *exitBlock = function->newBasicBlock(0, IR::Function::DontInsertBlock); - function->hasDirectEval = _env->hasDirectEval || _env->compilationMode == EvalCode + function->hasDirectEval = _variableEnvironment->hasDirectEval || _variableEnvironment->compilationMode == EvalCode || _module->debugMode; // Conditional breakpoints are like eval in the function - function->usesArgumentsObject = _env->parent && (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); - function->usesThis = _env->usesThis; - function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); - function->isStrict = _env->isStrict; - function->isNamedExpression = _env->isNamedFunctionExpression; - function->isQmlBinding = _env->compilationMode == QmlBinding; + function->usesArgumentsObject = _variableEnvironment->parent && (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUsed); + function->usesThis = _variableEnvironment->usesThis; + function->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); + function->isStrict = _variableEnvironment->isStrict; + function->isNamedExpression = _variableEnvironment->isNamedFunctionExpression; + function->isQmlBinding = _variableEnvironment->compilationMode == QmlBinding; AST::SourceLocation loc = ast->firstSourceLocation(); function->line = loc.startLine; function->column = loc.startColumn; if (function->usesArgumentsObject) - _env->enter(QStringLiteral("arguments"), Environment::VariableDeclaration); + _variableEnvironment->enter(QStringLiteral("arguments"), Environment::VariableDeclaration, AST::VariableDeclaration::FunctionScope); // variables in global code are properties of the global context object, not locals as with other functions. - if (_env->compilationMode == FunctionCode || _env->compilationMode == QmlBinding) { + if (_variableEnvironment->compilationMode == FunctionCode || _variableEnvironment->compilationMode == QmlBinding) { unsigned t = 0; - for (Environment::MemberMap::iterator it = _env->members.begin(), end = _env->members.end(); it != end; ++it) { + for (Environment::MemberMap::iterator it = _variableEnvironment->members.begin(), end = _variableEnvironment->members.end(); it != end; ++it) { const QString &local = it.key(); function->LOCAL(local); (*it).index = t; @@ -2046,18 +2070,19 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, ++t; } } else { - if (!_env->isStrict) { + if (!_variableEnvironment->isStrict) { for (const QString &inheritedLocal : qAsConst(inheritedLocals)) { function->LOCAL(inheritedLocal); unsigned tempIndex = entryBlock->newTemp(); Environment::Member member = { Environment::UndefinedMember, - static_cast<int>(tempIndex), 0 }; - _env->members.insert(inheritedLocal, member); + static_cast<int>(tempIndex), 0, + AST::VariableDeclaration::VariableScope::FunctionScope }; + _variableEnvironment->members.insert(inheritedLocal, member); } } IR::ExprList *args = 0; - for (Environment::MemberMap::const_iterator it = _env->members.constBegin(), cend = _env->members.constEnd(); it != cend; ++it) { + for (Environment::MemberMap::const_iterator it = _variableEnvironment->members.constBegin(), cend = _variableEnvironment->members.constEnd(); it != cend; ++it) { const QString &local = it.key(); IR::ExprList *next = function->New<IR::ExprList>(); next->expr = entryBlock->NAME(local, 0, 0); @@ -2089,11 +2114,11 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _function->RECEIVE(it->name.toString()); } - for (const Environment::Member &member : qAsConst(_env->members)) { + for (const Environment::Member &member : qAsConst(_variableEnvironment->members)) { if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body ? member.function->body->elements : 0); - if (! _env->parent) { + if (! _variableEnvironment->parent) { move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), _block->CLOSURE(function)); } else { @@ -2269,7 +2294,7 @@ bool Codegen::visit(ExpressionStatement *ast) if (hasError) return true; - if (_env->compilationMode == EvalCode || _env->compilationMode == QmlBinding) { + if (_variableEnvironment->compilationMode == EvalCode || _variableEnvironment->compilationMode == QmlBinding) { Result e = expression(ast->expression); if (*e) move(_block->TEMP(_returnAddress), *e); @@ -2505,7 +2530,7 @@ bool Codegen::visit(ReturnStatement *ast) if (hasError) return true; - if (_env->compilationMode != FunctionCode && _env->compilationMode != QmlBinding) { + if (_variableEnvironment->compilationMode != FunctionCode && _variableEnvironment->compilationMode != QmlBinding) { throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function")); return false; } @@ -2883,7 +2908,7 @@ bool Codegen::visit(UiSourceElement *) bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc) { - if (!_env->isStrict) + if (!_variableEnvironment->isStrict) return false; if (IR::Name *n = expr->asName()) { if (*n->id != QLatin1String("eval") && *n->id != QLatin1String("arguments")) diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 742ee79648..239ed5c4b9 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -141,10 +141,14 @@ protected: VariableDeclaration, FunctionDefinition }; + struct Member { MemberType type; int index; AST::FunctionExpression *function; + AST::VariableDeclaration::VariableScope scope; + + bool isLexicallyScoped() const { return this->scope != AST::VariableDeclaration::FunctionScope; } }; typedef QMap<QString, Member> MemberMap; @@ -191,6 +195,18 @@ protected: return (*it).index; } + bool memberInfo(const QString &name, const Member **m) const + { + Q_ASSERT(m); + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) { + *m = 0; + return false; + } + *m = &(*it); + return true; + } + bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) { Environment *it = this; @@ -206,7 +222,7 @@ protected: return false; } - void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0) + void enter(const QString &name, MemberType type, AST::VariableDeclaration::VariableScope scope, AST::FunctionExpression *function = 0) { if (! name.isEmpty()) { if (type != FunctionDefinition) { @@ -220,8 +236,10 @@ protected: m.index = -1; m.type = type; m.function = function; + m.scope = scope; members.insert(name, m); } else { + Q_ASSERT(scope == (*it).scope); if ((*it).type <= type) { (*it).type = type; (*it).function = function; @@ -448,7 +466,7 @@ protected: QV4::IR::BasicBlock *_block; QV4::IR::BasicBlock *_exitBlock; unsigned _returnAddress; - Environment *_env; + Environment *_variableEnvironment; Loop *_loop; AST::LabelledStatement *_labelledStatement; ScopeAndFinally *_scopeAndFinally; @@ -526,7 +544,7 @@ protected: // fields: Codegen *_cg; const QString _sourceCode; - Environment *_env; + Environment *_variableEnvironment; QStack<Environment *> _envStack; bool _allowFuncDecls; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6aac111897..8f8d374e24 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -52,7 +52,6 @@ #include "qv4compilationunitmapper_p.h" #include <QQmlPropertyMap> #include <QDateTime> -#include <QSaveFile> #include <QFile> #include <QFileInfo> #include <QScopedValueRollback> @@ -62,6 +61,7 @@ #include <private/qqmlirbuilder_p.h> #include <QCoreApplication> #include <QCryptographicHash> +#include <QSaveFile> #include <algorithm> @@ -77,6 +77,27 @@ namespace QV4 { namespace CompiledData { +#ifdef V4_BOOTSTRAP +static QString cacheFilePath(const QString &localSourcePath) +{ + const QString localCachePath = localSourcePath + QLatin1Char('c'); + return localCachePath; +} +#else +static QString cacheFilePath(const QUrl &url) +{ + const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); + const QString localCachePath = localSourcePath + QLatin1Char('c'); + if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) + return localCachePath; + QCryptographicHash fileNameHash(QCryptographicHash::Sha1); + fileNameHash.addData(localSourcePath.toUtf8()); + QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); + QDir::root().mkpath(directory); + return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix(); +} +#endif + #ifndef V4_BOOTSTRAP CompilationUnit::CompilationUnit() : data(0) @@ -207,7 +228,7 @@ void CompilationUnit::unlink() dependentScripts.at(ii)->release(); dependentScripts.clear(); - importCache = nullptr; + typeNameCache = nullptr; qDeleteAll(resolvedTypes); resolvedTypes.clear(); @@ -329,20 +350,68 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine, sizeof(data->dependencyMD5Checksum)) == 0; } -static QString cacheFilePath(const QUrl &url) +bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) { - const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); - const QString localCachePath = localSourcePath + QLatin1Char('c'); - if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) - return localCachePath; - QCryptographicHash fileNameHash(QCryptographicHash::Sha1); - fileNameHash.addData(localSourcePath.toUtf8()); - QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); - QDir::root().mkpath(directory); - return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix(); + if (!QQmlFile::isLocalFile(url)) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); + QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); + + CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, 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))) { + *errorString = QStringLiteral("QML source file has moved to a different location."); + return false; + } + + { + const QString foundArchitecture = stringAt(data->architectureIndex); + const QString expectedArchitecture = QSysInfo::buildAbi(); + if (foundArchitecture != expectedArchitecture) { + *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture); + return false; + } + } + + { + const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); + const QString expectedCodeGenerator = iselFactory->codeGeneratorName; + if (foundCodeGenerator != expectedCodeGenerator) { + *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); + return false; + } + } + + if (!memoryMapCode(errorString)) + return false; + + dataPtrChange.commit(); + free(const_cast<Unit*>(oldDataPtr)); + backingFile.reset(cacheFile.take()); + return true; +} + +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + *errorString = QStringLiteral("Missing code mapping backend"); + return false; } +#endif // V4_BOOTSTRAP + +#if defined(V4_BOOTSTRAP) +bool CompilationUnit::saveToDisk(const QString &unitUrl, QString *errorString) +#else bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) +#endif { errorString->clear(); @@ -351,10 +420,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return false; } +#if !defined(V4_BOOTSTRAP) if (!QQmlFile::isLocalFile(unitUrl)) { *errorString = QStringLiteral("File has to be a local file."); return false; } +#endif // Foo.qml -> Foo.qmlc QSaveFile cacheFile(cacheFilePath(unitUrl)); @@ -390,78 +461,105 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return true; } -bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) +void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) { - if (!QQmlFile::isLocalFile(url)) { - *errorString = QStringLiteral("File has to be a local file."); - return false; - } + Q_UNUSED(unit); +} - const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); - QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); +bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString) +{ + Q_UNUSED(device); + Q_UNUSED(unit); + *errorString = QStringLiteral("Saving code to disk is not supported in this configuration"); + return false; +} - CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString); - if (!mappedUnit) - return false; +Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) +{ + if (!irDocument->javaScriptCompilationUnit->data) + return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); - const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; - QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = irDocument->javaScriptCompilationUnit; + QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(irDocument->javaScriptCompilationUnit->data); - if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { - *errorString = QStringLiteral("QML source file has moved to a different location."); - return false; - } + QV4::Compiler::StringTableGenerator &stringTable = irDocument->jsGenerator.stringTable; - { - const QString foundArchitecture = stringAt(data->architectureIndex); - const QString expectedArchitecture = QSysInfo::buildAbi(); - if (foundArchitecture != expectedArchitecture) { - *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture); - return false; + // 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. + QVector<quint32> changedSignals; + QVector<QQmlJS::AST::FormalParameterList*> changedSignalParameters; + for (QmlIR::Object *o: qAsConst(irDocument->objects)) { + for (QmlIR::Binding *binding = o->firstBinding(); binding; binding = binding->next) { + if (!(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)) + continue; + + quint32 functionIndex = binding->value.compiledScriptIndex; + QmlIR::CompiledFunctionOrExpression *foe = o->functionsAndExpressions->slowAt(functionIndex); + if (!foe) + continue; + + // save absolute index + changedSignals << o->runtimeFunctionIndices.at(functionIndex); + + Q_ASSERT(foe->node); + Q_ASSERT(QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)); + + QQmlJS::AST::FormalParameterList *parameters = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)->formals; + changedSignalParameters << parameters; + + for (; parameters; parameters = parameters->next) + stringTable.registerString(parameters->name.toString()); } } - { - const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); - const QString expectedCodeGenerator = iselFactory->codeGeneratorName; - if (foundCodeGenerator != expectedCodeGenerator) { - *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); - return false; + QVector<quint32> signalParameterNameTable; + quint32 signalParameterNameTableOffset = jsUnit->unitSize; + + // Update signal signatures + if (!changedSignals.isEmpty()) { + if (jsUnit == compilationUnit->data) { + char *unitCopy = (char*)malloc(jsUnit->unitSize); + memcpy(unitCopy, jsUnit, jsUnit->unitSize); + jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy); } - } - if (!memoryMapCode(errorString)) - return false; + for (int i = 0; i < changedSignals.count(); ++i) { + const uint functionIndex = changedSignals.at(i); + // The data is now read-write due to the copy above, so the const_cast is ok. + QV4::CompiledData::Function *function = const_cast<QV4::CompiledData::Function *>(jsUnit->functionAt(functionIndex)); + Q_ASSERT(function->nFormals == quint32(0)); - dataPtrChange.commit(); - free(const_cast<Unit*>(oldDataPtr)); - backingFile.reset(cacheFile.take()); - return true; -} + function->formalsOffset = signalParameterNameTableOffset - jsUnit->functionOffsetTable()[functionIndex]; -void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) -{ - Q_UNUSED(unit); -} + for (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i); + parameters; parameters = parameters->next) { + signalParameterNameTable.append(stringTable.getStringId(parameters->name.toString())); + function->nFormals = function->nFormals + 1; + } -bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString) -{ - Q_UNUSED(device); - Q_UNUSED(unit); - *errorString = QStringLiteral("Saving code to disk is not supported in this configuration"); - return false; -} + // Hack to ensure an activation is created. + function->flags |= QV4::CompiledData::Function::HasCatchOrWith | QV4::CompiledData::Function::HasDirectEval; -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - *errorString = QStringLiteral("Missing code mapping backend"); - return false; -} -#endif // V4_BOOTSTRAP + signalParameterNameTableOffset += function->nFormals * sizeof(quint32); + } + } -Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) -{ - return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); + if (!signalParameterNameTable.isEmpty()) { + Q_ASSERT(jsUnit != compilationUnit->data); + const uint signalParameterTableSize = signalParameterNameTable.count() * sizeof(quint32); + uint newSize = jsUnit->unitSize + signalParameterTableSize; + const uint oldSize = jsUnit->unitSize; + char *unitWithSignalParameters = (char*)realloc(jsUnit, newSize); + memcpy(unitWithSignalParameters + oldSize, signalParameterNameTable.constData(), signalParameterTableSize); + jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitWithSignalParameters); + jsUnit->unitSize = newSize; + } + + if (jsUnit != compilationUnit->data) + jsUnit->flags &= ~QV4::CompiledData::Unit::StaticData; + + return jsUnit; } QString Binding::valueAsString(const Unit *unit) const @@ -624,7 +722,7 @@ static QByteArray ownLibraryChecksum() if (checksumInitialized) return libraryChecksum; checksumInitialized = true; -#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) +#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) && !defined(Q_OS_INTEGRITY) Dl_info libInfo; if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) { QFile library(QFile::decodeName(libInfo.dli_fname)); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 2682365182..13a0c4b075 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 0x08 +#define QV4_DATA_STRUCTURE_VERSION 0x09 class QIODevice; class QQmlPropertyCache; @@ -133,7 +133,7 @@ struct Location QJsonPrivate::qle_bitfield<20, 12> column; }; - Location() { line = 0; column = 0; } + Location() { line.val = 0; column.val = 0; } inline bool operator<(const Location &other) const { return line < other.line || @@ -153,7 +153,7 @@ struct RegExp QJsonPrivate::qle_bitfield<4, 28> stringIndex; }; - RegExp() { flags = 0; stringIndex = 0; } + RegExp() { flags.val = 0; stringIndex.val = 0; } }; struct Lookup @@ -171,7 +171,7 @@ struct Lookup QJsonPrivate::qle_bitfield<4, 28> nameIndex; }; - Lookup() { type_and_flags = 0; nameIndex = 0; } + Lookup() { type_and_flags.val = 0; nameIndex.val = 0; } }; struct JSClassMember @@ -625,7 +625,8 @@ struct Unit StaticData = 0x4, // Unit data persistent in memory? IsSingleton = 0x8, IsSharedLibrary = 0x10, // .pragma shared? - ContainsMachineCode = 0x20 // used to determine if we need to mmap with execute permissions + ContainsMachineCode = 0x20, // used to determine if we need to mmap with execute permissions + PendingTypeCompilation = 0x40 // the QML data structures present are incomplete and require type compilation }; LEUInt32 flags; LEUInt32 stringTableSize; @@ -777,31 +778,7 @@ struct TypeReferenceMap : QHash<int, TypeReference> }; #ifndef V4_BOOTSTRAP -struct ResolvedTypeReference -{ - ResolvedTypeReference() - : type(0) - , majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} - - QQmlType *type; - QQmlRefPointer<QQmlPropertyCache> typePropertyCache; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; - - int majorVersion; - int minorVersion; - // Types such as QQmlPropertyMap can add properties dynamically at run-time and - // therefore cannot have a property cache installed when instantiated. - bool isFullyDynamicType; - - QQmlPropertyCache *propertyCache() const; - QQmlPropertyCache *createPropertyCache(QQmlEngine *); - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); - - void doDynamicTypeCheck(); -}; +struct ResolvedTypeReference; // map from name index // While this could be a hash, a map is chosen here to provide a stable // order, which is used to calculating a check-sum on dependent meta-objects. @@ -841,10 +818,14 @@ 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; } - QV4::Heap::String **runtimeStrings; // Array QV4::Lookup *runtimeLookups; QV4::Value *runtimeRegularExpressions; QV4::InternalClass **runtimeClasses; @@ -855,7 +836,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QQmlPropertyCacheVector propertyCaches; QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject); } - QQmlRefPointer<QQmlTypeNameCache> importCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; // index is object index. This allows fast access to the // property data when initializing bindings, avoiding expensive @@ -918,17 +899,53 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount void destroy() Q_DECL_OVERRIDE; - bool saveToDisk(const QUrl &unitUrl, QString *errorString); bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString); protected: virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; - virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit); - virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); virtual bool memoryMapCode(QString *errorString); #endif // V4_BOOTSTRAP + +public: +#if defined(V4_BOOTSTRAP) + bool saveToDisk(const QString &unitUrl, QString *errorString); +#else + bool saveToDisk(const QUrl &unitUrl, QString *errorString); +#endif + +protected: + virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit); + virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); }; +#ifndef V4_BOOTSTRAP +struct ResolvedTypeReference +{ + ResolvedTypeReference() + : type(0) + , majorVersion(0) + , minorVersion(0) + , isFullyDynamicType(false) + {} + + QQmlType *type; + QQmlRefPointer<QQmlPropertyCache> typePropertyCache; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + + int majorVersion; + int minorVersion; + // Types such as QQmlPropertyMap can add properties dynamically at run-time and + // therefore cannot have a property cache installed when instantiated. + bool isFullyDynamicType; + + QQmlPropertyCache *propertyCache() const; + QQmlPropertyCache *createPropertyCache(QQmlEngine *); + bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); + + void doDynamicTypeCheck(); +}; +#endif + } } diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index cd822a2614..9cfac4a676 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -220,7 +220,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO registerString(*f->locals.at(i)); } - CompiledData::LEUInt32 *functionOffsets = reinterpret_cast<CompiledData::LEUInt32*>(alloca(irModule->functions.size() * sizeof(CompiledData::LEUInt32))); + Q_ALLOCA_VAR(CompiledData::LEUInt32, functionOffsets, irModule->functions.size() * sizeof(CompiledData::LEUInt32)); uint jsClassDataOffset = 0; char *dataPtr; diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index ca4e0b73d4..53d9956315 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -52,10 +52,11 @@ // #include <private/qv4global_p.h> #include <private/qv4value_p.h> -#include <private/qv4function_p.h> #include <private/qv4runtime_p.h> +#if !defined(V4_BOOTSTRAP) QT_REQUIRE_CONFIG(qml_interpreter); +#endif QT_BEGIN_NAMESPACE diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 9dbebd1128..04844302d9 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -39,15 +39,15 @@ #include "qv4isel_util_p.h" #include "qv4isel_moth_p.h" -#include "qv4vme_moth_p.h" #include "qv4ssa_p.h" -#include <private/qv4debugging_p.h> -#include <private/qv4function_p.h> -#include <private/qv4regexpobject_p.h> #include <private/qv4compileddata_p.h> -#include <private/qqmlengine_p.h> #include <wtf/MathExtras.h> +#if !defined(V4_BOOTSTRAP) +#include "qv4vme_moth_p.h" +#include <private/qv4function_p.h> +#endif + #undef USE_TYPE_INFO using namespace QV4; @@ -1185,8 +1185,11 @@ void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg) void InstructionSelection::callBuiltinPopScope() { + QT_WARNING_PUSH + QT_WARNING_DISABLE_GCC("-Wuninitialized") Instruction::CallBuiltinPopScope call; addInstruction(call); + QT_WARNING_POP } void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) @@ -1335,8 +1338,11 @@ void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result) void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject() { + QT_WARNING_PUSH + QT_WARNING_DISABLE_GCC("-Wuninitialized") Instruction::CallBuiltinConvertThisToObject call; addInstruction(call); + QT_WARNING_POP } ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) @@ -1425,6 +1431,8 @@ CompilationUnit::~CompilationUnit() { } +#if !defined(V4_BOOTSTRAP) + void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) { #ifdef MOTH_THREADED_INTERPRETER @@ -1461,6 +1469,31 @@ void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) } } +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + Q_UNUSED(errorString); + codeRefs.resize(data->functionTableSize); + + const char *basePtr = reinterpret_cast<const char *>(data); + + for (uint i = 0; i < data->functionTableSize; ++i) { + const CompiledData::Function *compiledFunction = data->functionAt(i); + const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset)); +#ifdef MOTH_THREADED_INTERPRETER + // for the threaded interpreter we need to make a copy of the data because it needs to be + // modified for the instruction handler addresses. + QByteArray code(codePtr, compiledFunction->codeSize); +#else + QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); +#endif + codeRefs[i] = code; + } + + return true; +} + +#endif // V4_BOOTSTRAP + void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) { const int codeAlignment = 16; @@ -1482,7 +1515,7 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray padding; -#ifdef MOTH_THREADED_INTERPRETER +#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP) // Map from instruction label back to instruction type. Only needed when persisting // already linked compilation units; QHash<void*, int> reverseInstructionMapping; @@ -1511,7 +1544,7 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray code = codeRefs.at(i); -#ifdef MOTH_THREADED_INTERPRETER +#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP) if (!reverseInstructionMapping.isEmpty()) { char *codePtr = code.data(); // detaches int index = 0; @@ -1541,29 +1574,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit return true; } -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - Q_UNUSED(errorString); - codeRefs.resize(data->functionTableSize); - - const char *basePtr = reinterpret_cast<const char *>(data); - - for (uint i = 0; i < data->functionTableSize; ++i) { - const CompiledData::Function *compiledFunction = data->functionAt(i); - const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset)); -#ifdef MOTH_THREADED_INTERPRETER - // for the threaded interpreter we need to make a copy of the data because it needs to be - // modified for the instruction handler addresses. - QByteArray code(codePtr, compiledFunction->codeSize); -#else - QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); -#endif - codeRefs[i] = code; - } - - return true; -} - QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading() { QQmlRefPointer<CompiledData::CompilationUnit> result; diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index afe5fe342e..41469f1985 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -59,7 +59,9 @@ #include <private/qv4value_p.h> #include "qv4instr_moth_p.h" +#if !defined(V4_BOOTSTRAP) QT_REQUIRE_CONFIG(qml_interpreter); +#endif QT_BEGIN_NAMESPACE @@ -69,10 +71,12 @@ namespace Moth { struct CompilationUnit : public QV4::CompiledData::CompilationUnit { virtual ~CompilationUnit(); +#if !defined(V4_BOOTSTRAP) void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; + bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; +#endif void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; - bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; QVector<QByteArray> codeRefs; diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 73aa6c4975..04bc3d86e5 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -315,20 +315,20 @@ struct Q_AUTOTEST_EXPORT Expr { Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {} bool isLValue() const; - Const *asConst() { return as<Const>(); } - String *asString() { return as<String>(); } - RegExp *asRegExp() { return as<RegExp>(); } - Name *asName() { return as<Name>(); } - Temp *asTemp() { return as<Temp>(); } - ArgLocal *asArgLocal() { return as<ArgLocal>(); } - Closure *asClosure() { return as<Closure>(); } - Convert *asConvert() { return as<Convert>(); } - Unop *asUnop() { return as<Unop>(); } - Binop *asBinop() { return as<Binop>(); } - Call *asCall() { return as<Call>(); } - New *asNew() { return as<New>(); } - Subscript *asSubscript() { return as<Subscript>(); } - Member *asMember() { return as<Member>(); } + Const *asConst(); + String *asString(); + RegExp *asRegExp(); + Name *asName(); + Temp *asTemp(); + ArgLocal *asArgLocal(); + Closure *asClosure(); + Convert *asConvert(); + Unop *asUnop(); + Binop *asBinop(); + Call *asCall(); + New *asNew(); + Subscript *asSubscript(); + Member *asMember(); }; #define EXPR_VISIT_ALL_KINDS(e) \ @@ -773,12 +773,12 @@ struct Stmt { Stmt *asTerminator(); - Exp *asExp() { return as<Exp>(); } - Move *asMove() { return as<Move>(); } - Jump *asJump() { return as<Jump>(); } - CJump *asCJump() { return as<CJump>(); } - Ret *asRet() { return as<Ret>(); } - Phi *asPhi() { return as<Phi>(); } + Exp *asExp(); + Move *asMove(); + Jump *asJump(); + CJump *asCJump(); + Ret *asRet(); + Phi *asPhi(); int id() const { return _id; } @@ -1720,6 +1720,28 @@ inline Stmt *BasicBlock::RET(Expr *expr) return s; } +inline Const *Expr::asConst() { return as<Const>(); } +inline String *Expr::asString() { return as<String>(); } +inline RegExp *Expr::asRegExp() { return as<RegExp>(); } +inline Name *Expr::asName() { return as<Name>(); } +inline Temp *Expr::asTemp() { return as<Temp>(); } +inline ArgLocal *Expr::asArgLocal() { return as<ArgLocal>(); } +inline Closure *Expr::asClosure() { return as<Closure>(); } +inline Convert *Expr::asConvert() { return as<Convert>(); } +inline Unop *Expr::asUnop() { return as<Unop>(); } +inline Binop *Expr::asBinop() { return as<Binop>(); } +inline Call *Expr::asCall() { return as<Call>(); } +inline New *Expr::asNew() { return as<New>(); } +inline Subscript *Expr::asSubscript() { return as<Subscript>(); } +inline Member *Expr::asMember() { return as<Member>(); } + +inline Exp *Stmt::asExp() { return as<Exp>(); } +inline Move *Stmt::asMove() { return as<Move>(); } +inline Jump *Stmt::asJump() { return as<Jump>(); } +inline CJump *Stmt::asCJump() { return as<CJump>(); } +inline Ret *Stmt::asRet() { return as<Ret>(); } +inline Phi *Stmt::asPhi() { return as<Phi>(); } + } // end of namespace IR } // end of namespace QV4 diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 1d512711b8..10f0bbcf8f 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -5123,7 +5123,7 @@ void LifeTimeInterval::setFrom(int from) { Q_ASSERT(from > 0); if (_ranges.isEmpty()) { // this is the case where there is no use, only a define - _ranges.prepend(Range(from, from)); + _ranges.prepend(LifeTimeIntervalRange(from, from)); if (_end == InvalidPosition) _end = from; } else { @@ -5137,17 +5137,17 @@ void LifeTimeInterval::addRange(int from, int to) { Q_ASSERT(to >= from); if (_ranges.isEmpty()) { - _ranges.prepend(Range(from, to)); + _ranges.prepend(LifeTimeIntervalRange(from, to)); _end = to; return; } - Range *p = &_ranges.first(); + LifeTimeIntervalRange *p = &_ranges.first(); if (to + 1 >= p->start && p->end + 1 >= from) { p->start = qMin(p->start, from); p->end = qMax(p->end, to); while (_ranges.count() > 1) { - Range *p1 = p + 1; + LifeTimeIntervalRange *p1 = p + 1; if (p->end + 1 < p1->start || p1->end + 1 < p->start) break; p1->start = qMin(p->start, p1->start); @@ -5157,10 +5157,10 @@ void LifeTimeInterval::addRange(int from, int to) { } } else { if (to < p->start) { - _ranges.prepend(Range(from, to)); + _ranges.prepend(LifeTimeIntervalRange(from, to)); } else { Q_ASSERT(from > _ranges.last().end); - _ranges.push_back(Range(from, to)); + _ranges.push_back(LifeTimeIntervalRange(from, to)); } } @@ -5206,7 +5206,7 @@ LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart) } else { // find the first range where the temp will get active again: while (!newInterval._ranges.isEmpty()) { - const Range &range = newInterval._ranges.first(); + const LifeTimeIntervalRange &range = newInterval._ranges.first(); if (range.start > newStart) { // The split position is before the start of the range. Either we managed to skip // over the correct range, or we got an invalid split request. Either way, this diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index db8b6edd1a..c07abd04c4 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -63,20 +63,28 @@ class QQmlEnginePrivate; namespace QV4 { namespace IR { -class Q_AUTOTEST_EXPORT LifeTimeInterval { -public: - struct Range { - int start; - int end; +struct LifeTimeIntervalRange { + int start; + int end; - Range(int start = InvalidPosition, int end = InvalidPosition) - : start(start) - , end(end) - {} + LifeTimeIntervalRange(int start = -1, int end = -1) + : start(start) + , end(end) + {} - bool covers(int position) const { return start <= position && position <= end; } - }; - typedef QVarLengthArray<Range, 4> Ranges; + bool covers(int position) const { return start <= position && position <= end; } +}; +} // IR namespace +} // QV4 namespace + +Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeIntervalRange, Q_PRIMITIVE_TYPE); + +namespace QV4 { +namespace IR { + +class Q_AUTOTEST_EXPORT LifeTimeInterval { +public: + typedef QVarLengthArray<LifeTimeIntervalRange, 4> Ranges; private: Temp _temp; @@ -137,7 +145,7 @@ public: // Validate the new range if (_end != InvalidPosition) { Q_ASSERT(!_ranges.isEmpty()); - for (const Range &range : qAsConst(_ranges)) { + for (const LifeTimeIntervalRange &range : qAsConst(_ranges)) { Q_ASSERT(range.start >= 0); Q_ASSERT(range.end >= 0); Q_ASSERT(range.start <= range.end); @@ -457,7 +465,6 @@ protected: Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeInterval, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeInterval::Range, Q_PRIMITIVE_TYPE); QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 3a507bef74..41fb2c5b7b 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -73,7 +73,7 @@ struct QQmlProfiler {}; struct QQmlBindingProfiler { - QQmlBindingProfiler(quintptr, QQmlBinding *, QV4::FunctionObject *) {} + QQmlBindingProfiler(quintptr, QV4::Function *) {} }; struct QQmlHandlingSignalProfiler diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index ac6600f38c..4523ee39d8 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -167,9 +167,9 @@ additional features. See the \l {qtqml-javascript-hostenvironment.html} The QML engine provides automatic type conversion between QVariantList and JavaScript arrays, and between QVariantMap and JavaScript objects. -For example, the function defined in QML below left expects two arguments, an +For example, the function defined in QML below expects two arguments, an array and an object, and prints their contents using the standard JavaScript -syntax for array and object item access. The C++ code below right calls this +syntax for array and object item access. The C++ code below calls this function, passing a QVariantList and a QVariantMap, which are automatically converted to JavaScript array and object values, repectively: @@ -204,9 +204,9 @@ when it is passed to C++. The QML engine provides automatic type conversion between QDateTime values and JavaScript \c Date objects. -For example, the function defined in QML below left expects a JavaScript +For example, the function defined in QML below expects a JavaScript \c Date object, and also returns a new \c Date object with the current date and -time. The C++ code below right calls this function, passing a QDateTime value +time. The C++ code below calls this function, passing a QDateTime value that is automatically converted by the engine into a \c Date object when it is passed to the \c readDate() function. In turn, the readDate() function returns a \c Date object that is automatically converted into a QDateTime value when it @@ -215,7 +215,7 @@ is received in C++: \table \header \row - +\li QML \li \qml // MyItem.qml @@ -227,6 +227,7 @@ Item { } \endqml \row +\li C++ \li \code // C++ diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index e06451b2bc..7d4a543089 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -282,6 +282,9 @@ int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor) template<typename T, int metaObjectRevision> int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason) + +template<typename T, typename E, int metaObjectRevision> +int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason) \endcode For example, if \c BaseType is changed and now has a revision 1, you can diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc index ed0d049564..3bffd2eb6f 100644 --- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc +++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc @@ -390,8 +390,8 @@ that is a public slot: \endcode If an instance of \c MessageBoard was set as the context data for a file \c -MyItem.qml, as shown below left, then \c MyItem.qml could invoke the two -methods, as shown below right: +MyItem.qml, then \c MyItem.qml could invoke the two methods as shown in the +examples below: \table \row diff --git a/src/qml/doc/src/cppintegration/topic.qdoc b/src/qml/doc/src/cppintegration/topic.qdoc index a349fd0713..1aa3bb6ab5 100644 --- a/src/qml/doc/src/cppintegration/topic.qdoc +++ b/src/qml/doc/src/cppintegration/topic.qdoc @@ -30,7 +30,7 @@ \brief Description of how to integrate QML and C++ code QML is designed to be easily extensible through C++ code. The classes in the \l {Qt QML} module -enables QML objects to be loaded and manipulated from C++, and the nature of QML engine's +enable QML objects to be loaded and manipulated from C++, and the nature of QML engine's integration with Qt's \l{Meta Object System}{meta object system} enables C++ functionality to be invoked directly from QML. This allows the development of hybrid applications which are implemented with a mixture of QML, JavaScript and C++ code. diff --git a/src/qml/doc/src/javascript/functionlist.qdoc b/src/qml/doc/src/javascript/functionlist.qdoc index f5b1ed3cf1..fd916e1e24 100644 --- a/src/qml/doc/src/javascript/functionlist.qdoc +++ b/src/qml/doc/src/javascript/functionlist.qdoc @@ -182,6 +182,7 @@ \li lastIndexOf(searchString, position) \li localeCompare(that) \li match(regexp) + \li repeat(count) // ECMAScript 6: Added in Qt 5.9 \li replace(searchValue, replaceValue) \li search(regexp) \li slice(start, end) diff --git a/src/qml/doc/src/javascript/imports.qdoc b/src/qml/doc/src/javascript/imports.qdoc index d4ce25c92b..489da08ada 100644 --- a/src/qml/doc/src/javascript/imports.qdoc +++ b/src/qml/doc/src/javascript/imports.qdoc @@ -56,10 +56,10 @@ import "jsfile.js" as Logic \endcode Imported JavaScript resources are always qualified using the "as" keyword. The -qualifier for JavaScript resources must be unique, so there is always a -one-to-one mapping between qualifiers and JavaScript files. (This also means -qualifiers cannot be named the same as built-in JavaScript objects such as -\c Date and \c Math). +qualifier for JavaScript resources must start with an uppercase letter, and must +be unique, so there is always a one-to-one mapping between qualifiers and JavaScript +files. (This also means qualifiers cannot be named the same as built-in JavaScript +objects such as \c Date and \c Math). The functions defined in an imported JavaScript file are available to objects defined in the importing QML document, via the diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index 030eb72b5f..33f58dc1b9 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -630,7 +630,7 @@ a \l {MouseArea::}{clicked} signal that is emitted when the user clicks within the mouse area. An object can be notified through a \l{Signal handler attributes} -{signal handler} whenever it a particular signal is emitted. A signal handler +{signal handler} whenever a particular signal is emitted. A signal handler is declared with the syntax \e on<Signal> where \e <Signal> is the name of the signal, with the first letter capitalized. The signal handler must be declared within the definition of the object that emits the signal, and the handler diff --git a/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc index ffbf2282a6..a486b47f03 100644 --- a/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc +++ b/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc @@ -324,6 +324,9 @@ property is only invoked when the property is reassigned to a different object v \li Values in the list are accessed using the \c [index] syntax \endlist + Values can be dynamically added to the list by using the \c push method, + as if it were a JavaScript Array + A \c list can only store QML objects, and cannot contain any \l {QML Basic Types}{basic type} values. (To store basic types within a list, use the \l var type instead.) diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc index f32574fcc1..b2bb9ebd18 100644 --- a/src/qml/doc/src/qmltypereference.qdoc +++ b/src/qml/doc/src/qmltypereference.qdoc @@ -89,7 +89,7 @@ provided: \ingroup qtquickbasictypes \brief a date value. -The \c date type refers to a date value. +The \c date type refers to a date value, including the time of the day. To create a \c date value, specify it as a "YYYY-MM-DD" string: @@ -100,7 +100,7 @@ MyDatePicker { minDate: "2000-01-01"; maxDate: "2020-12-31" } To read a date value returned from a C++ extension class, use \l{QtQml::Qt::formatDate()}{Qt.formatDate()} and \l{QtQml::Qt::formatDateTime()}{Qt.formatDateTime()}. -When integrating with C++, note that any QDate value +When integrating with C++, note that any QDate or QDateTime value \l{qtqml-cppintegration-data.html}{passed into QML from C++} is automatically converted into a \c date value, and vice-versa. diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 018396318e..ca270a0648 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -39,11 +39,6 @@ #include "qv4isel_masm_p.h" #include "qv4runtime_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4regexpobject_p.h" -#include "qv4lookup_p.h" -#include "qv4function_p.h" #include "qv4ssa_p.h" #include "qv4regalloc_p.h" #include "qv4assembler_p.h" @@ -51,6 +46,10 @@ #include <assembler/LinkBuffer.h> #include <WTFStubs.h> +#if !defined(V4_BOOTSTRAP) +#include "qv4function_p.h" +#endif + #include <iostream> #include <QBuffer> #include <QCoreApplication> @@ -68,6 +67,8 @@ CompilationUnit::~CompilationUnit() { } +#if !defined(V4_BOOTSTRAP) + void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) { runtimeFunctions.resize(data->functionTableSize); @@ -81,6 +82,26 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) } } +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + Q_UNUSED(errorString); + codeRefs.resize(data->functionTableSize); + + const char *basePtr = reinterpret_cast<const char *>(data); + + for (uint i = 0; i < data->functionTableSize; ++i) { + const CompiledData::Function *compiledFunction = data->functionAt(i); + void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset)); + JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr)); + JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize); + codeRefs[i] = codeRef; + } + + return true; +} + +#endif // !defined(V4_BOOTSTRAP) + void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) { const int codeAlignment = 16; @@ -128,27 +149,11 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit return true; } -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - Q_UNUSED(errorString); - codeRefs.resize(data->functionTableSize); - - const char *basePtr = reinterpret_cast<const char *>(data); +template <typename TargetConfiguration> +const typename Assembler<TargetConfiguration>::VoidType Assembler<TargetConfiguration>::Void; - for (uint i = 0; i < data->functionTableSize; ++i) { - const CompiledData::Function *compiledFunction = data->functionAt(i); - void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset)); - JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr)); - JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize); - codeRefs[i] = codeRef; - } - - return true; -} - -const Assembler::VoidType Assembler::Void; - -Assembler::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) +template <typename TargetConfiguration> +Assembler<TargetConfiguration>::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) : _function(function) , _nextBlock(0) , _executableAllocator(executableAllocator) @@ -159,14 +164,16 @@ Assembler::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* _labelPatches.resize(_function->basicBlockCount()); } -void Assembler::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock) { _addrs[block->index()] = label(); catchBlock = block->catchBlock; _nextBlock = nextBlock; } -void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) { Q_UNUSED(current); @@ -174,12 +181,14 @@ void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) _patches[target->index()].push_back(jump()); } -void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) { _patches[targetBlock->index()].push_back(targetJump); } -void Assembler::addPatch(DataLabelPtr patch, Label target) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, Label target) { DataLabelPatch p; p.dataLabel = patch; @@ -187,37 +196,21 @@ void Assembler::addPatch(DataLabelPtr patch, Label target) _dataLabelPatches.push_back(p); } -void Assembler::addPatch(DataLabelPtr patch, IR::BasicBlock *target) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, IR::BasicBlock *target) { _labelPatches[target->index()].push_back(patch); } -void Assembler::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { - generateCJumpOnCompare(NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock); -} - -#ifdef QV4_USE_64_BIT_VALUE_ENCODING -void Assembler::generateCJumpOnCompare(RelationalCondition cond, - RegisterID left, - TrustedImm64 right, - IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - if (trueBlock == _nextBlock) { - Jump target = branch64(invert(cond), left, right); - addPatch(falseBlock, target); - } else { - Jump target = branch64(cond, left, right); - addPatch(trueBlock, target); - jumpToBlock(currentBlock, falseBlock); - } + generateCJumpOnCompare(RelationalCondition::NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock); } -#endif -void Assembler::generateCJumpOnCompare(RelationalCondition cond, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right, IR::BasicBlock *currentBlock, @@ -234,7 +227,8 @@ void Assembler::generateCJumpOnCompare(RelationalCondition cond, } } -void Assembler::generateCJumpOnCompare(RelationalCondition cond, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right, IR::BasicBlock *currentBlock, @@ -251,7 +245,8 @@ void Assembler::generateCJumpOnCompare(RelationalCondition cond, } } -Assembler::Pointer Assembler::loadAddress(RegisterID tmp, IR::Expr *e) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadAddress(RegisterID tmp, IR::Expr *e) { IR::Temp *t = e->asTemp(); if (t) @@ -260,7 +255,8 @@ Assembler::Pointer Assembler::loadAddress(RegisterID tmp, IR::Expr *e) return loadArgLocalAddress(tmp, e->asArgLocal()); } -Assembler::Pointer Assembler::loadTempAddress(IR::Temp *t) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadTempAddress(IR::Temp *t) { if (t->kind == IR::Temp::StackSlot) return stackSlotPointer(t); @@ -268,7 +264,8 @@ Assembler::Pointer Assembler::loadTempAddress(IR::Temp *t) Q_UNREACHABLE(); } -Assembler::Pointer Assembler::loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al) { int32_t offset = 0; int scope = al->scope; @@ -298,7 +295,8 @@ Assembler::Pointer Assembler::loadArgLocalAddress(RegisterID baseReg, IR::ArgLoc return Pointer(baseReg, offset); } -Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &string) +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); @@ -307,12 +305,14 @@ Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &s return Pointer(reg, id * sizeof(QV4::String*)); } -Assembler::Address Assembler::loadConstant(IR::Const *c, RegisterID baseReg) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(IR::Const *c, RegisterID baseReg) { return loadConstant(convertToValue(c), baseReg); } -Assembler::Address Assembler::loadConstant(const Primitive &v, RegisterID baseReg) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const Primitive &v, RegisterID baseReg) { loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), baseReg); loadPtr(Address(baseReg, qOffsetOf(QV4::Heap::ExecutionContext, constantTable)), baseReg); @@ -320,33 +320,36 @@ Assembler::Address Assembler::loadConstant(const Primitive &v, RegisterID baseRe return Address(baseReg, index * sizeof(QV4::Value)); } -void Assembler::loadStringRef(RegisterID reg, const QString &string) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::loadStringRef(RegisterID reg, const QString &string) { const int id = _jsGenerator->registerString(string); move(TrustedImm32(id), reg); } -void Assembler::storeValue(QV4::Primitive value, IR::Expr *destination) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::storeValue(QV4::Primitive value, IR::Expr *destination) { Address addr = loadAddress(ScratchRegister, destination); storeValue(value, addr); } -void Assembler::enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, const RegisterInformation &fpRegistersToSave) { platformEnterStandardStackFrame(this); - move(StackPointerRegister, FramePointerRegister); + move(StackPointerRegister, JITTargetPlatform::FramePointerRegister); const int frameSize = _stackLayout->calculateStackFrameSize(); subPtr(TrustedImm32(frameSize), StackPointerRegister); - Address slotAddr(FramePointerRegister, 0); + Address slotAddr(JITTargetPlatform::FramePointerRegister, 0); for (int i = 0, ei = fpRegistersToSave.size(); i < ei; ++i) { Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); slotAddr.offset -= sizeof(double); - JSC::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr); + TargetConfiguration::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr); } for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) { Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); @@ -355,10 +358,11 @@ void Assembler::enterStandardStackFrame(const RegisterInformation ®ularRegist } } -void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, const RegisterInformation &fpRegistersToSave) { - Address slotAddr(FramePointerRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double)); + Address slotAddr(JITTargetPlatform::FramePointerRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double)); // restore the callee saved registers for (int i = regularRegistersToSave.size() - 1; i >= 0; --i) { @@ -368,23 +372,14 @@ void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegist } for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) { Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); - JSC::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); + TargetConfiguration::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); slotAddr.offset += sizeof(double); } Q_ASSERT(slotAddr.offset == 0); const int frameSize = _stackLayout->calculateStackFrameSize(); - // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't - // work well for large immediates. -#if CPU(ARM_THUMB2) - move(TrustedImm32(frameSize), JSC::ARMRegisters::r3); - add32(JSC::ARMRegisters::r3, StackPointerRegister); -#else - addPtr(TrustedImm32(frameSize), StackPointerRegister); -#endif - - platformLeaveStandardStackFrame(this); + platformLeaveStandardStackFrame(this, frameSize); } @@ -393,7 +388,8 @@ void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegist // Try to load the source expression into the destination FP register. This assumes that two // general purpose (integer) registers are available: the ScratchRegister and the // ReturnValueRegister. It returns a Jump if no conversion can be performed. -Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRegisterID dest) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::genTryDoubleConversion(IR::Expr *src, FPRegisterID dest) { switch (src->type) { case IR::DoubleType: @@ -436,11 +432,10 @@ Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe isNoInt.link(this); #ifdef QV4_USE_64_BIT_VALUE_ENCODING rshift32(TrustedImm32(Value::IsDoubleTag_Shift), ScratchRegister); - Assembler::Jump isNoDbl = branch32(Equal, ScratchRegister, TrustedImm32(0)); + Assembler::Jump isNoDbl = branch32(RelationalCondition::Equal, JITTargetPlatform::ScratchRegister, TrustedImm32(0)); #else and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister); - Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::NotDouble_Mask)); + Assembler::Jump isNoDbl = branch32(RelationalCondition::Equal, JITTargetPlatform::ScratchRegister, TrustedImm32(Value::NotDouble_Mask)); #endif toDoubleRegister(src, dest); intDone.link(this); @@ -448,10 +443,11 @@ Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe return isNoDbl; } -Assembler::Jump Assembler::branchDouble(bool invertCondition, IR::AluOp op, +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right) { - Assembler::DoubleCondition cond; + DoubleCondition cond; switch (op) { case IR::OpGt: cond = Assembler::DoubleGreaterThan; break; case IR::OpLt: cond = Assembler::DoubleLessThan; break; @@ -465,12 +461,13 @@ Assembler::Jump Assembler::branchDouble(bool invertCondition, IR::AluOp op, Q_UNREACHABLE(); } if (invertCondition) - cond = JSC::MacroAssembler::invert(cond); + cond = TargetConfiguration::MacroAssembler::invert(cond); - return JSC::MacroAssembler::branchDouble(cond, toDoubleRegister(left, FPGpr0), toDoubleRegister(right, FPGpr1)); + return TargetConfiguration::MacroAssembler::branchDouble(cond, toDoubleRegister(left, FPGpr0), toDoubleRegister(right, JITTargetPlatform::FPGpr1)); } -Assembler::Jump Assembler::branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right) { Assembler::RelationalCondition cond; switch (op) { @@ -486,18 +483,51 @@ Assembler::Jump Assembler::branchInt32(bool invertCondition, IR::AluOp op, IR::E Q_UNREACHABLE(); } if (invertCondition) - cond = JSC::MacroAssembler::invert(cond); + cond = TargetConfiguration::MacroAssembler::invert(cond); - return JSC::MacroAssembler::branch32(cond, - toInt32Register(left, Assembler::ScratchRegister), - toInt32Register(right, Assembler::ReturnValueRegister)); + return TargetConfiguration::MacroAssembler::branch32(cond, + toInt32Register(left, Assembler::ScratchRegister), + toInt32Register(right, Assembler::ReturnValueRegister)); } -void Assembler::setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave) { _stackLayout.reset(new StackLayout(_function, maxArgCountForBuiltins, regularRegistersToSave, fpRegistersToSave)); } +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave) +{ + if (!s) { + // this only happens if the method doesn't have a return statement and can + // only exit through an exception + } 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); + RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); + } else { + Q_UNREACHABLE(); + Q_UNUSED(s); + } + + Label leaveStackFrame = label(); + + 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))); + + leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave); + ret(); + + exceptionReturnLabel = label(); + QV4::Primitive retVal = Primitive::undefinedValue(); + RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); + jump(leaveStackFrame); +} namespace { class QIODevicePrintStream: public FilePrintStream @@ -516,7 +546,7 @@ public: ~QIODevicePrintStream() {} - void vprintf(const char* format, va_list argList) WTF_ATTRIBUTE_PRINTF(2, 0) + void vprintf(const char* format, va_list argList) override WTF_ATTRIBUTE_PRINTF(2, 0) { const int written = qvsnprintf(buf.data(), buf.size(), format, argList); if (written > 0) @@ -524,7 +554,7 @@ public: memset(buf.data(), 0, qMin(written, buf.size())); } - void flush() + void flush() override {} private: @@ -563,7 +593,8 @@ static void qt_closePmap() #endif -JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) +template <typename TargetConfiguration> +JSC::MacroAssemblerCodeRef Assembler<TargetConfiguration>::link(int *codeSize) { Label endOfCode = label(); @@ -577,7 +608,7 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) } JSC::JSGlobalData dummy(_executableAllocator); - JSC::LinkBuffer linkBuffer(dummy, this, 0); + JSC::LinkBuffer<typename TargetConfiguration::MacroAssembler> linkBuffer(dummy, this, 0); for (const DataLabelPatch &p : qAsConst(_dataLabelPatches)) linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); @@ -668,4 +699,14 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) return codeRef; } +template class QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>; +#if defined(V4_BOOTSTRAP) +#if !CPU(ARM_THUMB2) +template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; +#endif +#if !CPU(ARM64) +template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>; +#endif +#endif + #endif diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index de9c246ed6..fd65c9b3d2 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -55,7 +55,8 @@ #include "private/qv4isel_p.h" #include "private/qv4isel_util_p.h" #include "private/qv4value_p.h" -#include "private/qv4lookup_p.h" +#include "private/qv4context_p.h" +#include "private/qv4engine_p.h" #include "qv4targetplatform_p.h" #include <config.h> @@ -73,46 +74,671 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { -class InstructionSelection; - struct CompilationUnit : public QV4::CompiledData::CompilationUnit { virtual ~CompilationUnit(); +#if !defined(V4_BOOTSTRAP) void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; + bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; +#endif void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; - bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; // Coderef + execution engine QVector<JSC::MacroAssemblerCodeRef> codeRefs; }; -struct LookupCall { - JSC::MacroAssembler::Address addr; - uint getterSetterOffset; +template <typename PlatformAssembler, TargetOperatingSystemSpecialization Specialization> +struct AssemblerTargetConfiguration +{ + typedef JSC::MacroAssembler<PlatformAssembler> MacroAssembler; + typedef TargetPlatform<PlatformAssembler, Specialization> Platform; + // More things coming here in the future, such as Target OS +}; + +#if CPU(ARM_THUMB2) +typedef JSC::MacroAssemblerARMv7 DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#elif CPU(ARM64) +typedef JSC::MacroAssemblerARM64 DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#elif CPU(ARM_TRADITIONAL) +typedef JSC::MacroAssemblerARM DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#elif CPU(MIPS) +typedef JSC::MacroAssemblerMIPS DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#elif CPU(X86) +typedef JSC::MacroAssemblerX86 DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#elif CPU(X86_64) +typedef JSC::MacroAssemblerX86_64 DefaultPlatformMacroAssembler; + +#if OS(WINDOWS) +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, WindowsSpecialization> DefaultAssemblerTargetConfiguration; +#else +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#endif - LookupCall(const JSC::MacroAssembler::Address &addr, uint getterSetterOffset) - : addr(addr) - , getterSetterOffset(getterSetterOffset) - {} +#elif CPU(SH4) +typedef JSC::MacroAssemblerSH4 DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#endif + +#define isel_stringIfyx(s) #s +#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__) + + +template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform, int RegisterSize> +struct RegisterSizeDependentAssembler +{ }; -struct RuntimeCall { - JSC::MacroAssembler::Address addr; +template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform> +struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatform, 4> +{ + using RegisterID = typename JITAssembler::RegisterID; + using FPRegisterID = typename JITAssembler::FPRegisterID; + using RelationalCondition = typename JITAssembler::RelationalCondition; + using ResultCondition = typename JITAssembler::ResultCondition; + using Address = typename JITAssembler::Address; + using Pointer = typename JITAssembler::Pointer; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + using TrustedImm64 = typename JITAssembler::TrustedImm64; + using Jump = typename JITAssembler::Jump; + using Label = typename JITAssembler::Label; + + static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) + { + as->MacroAssembler::loadDouble(addr, dest); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr) + { + as->MacroAssembler::storeDouble(source, addr); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) + { + Pointer ptr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->storeDouble(source, ptr); + } + + static void storeValue(JITAssembler *as, QV4::Primitive value, Address destination) + { + as->store32(TrustedImm32(value.int_32()), destination); + destination.offset += 4; + as->store32(TrustedImm32(value.tag()), destination); + } + + template <typename Source, typename Destination> + static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination) + { + as->loadDouble(source, TargetPlatform::FPGpr0); + as->storeDouble(TargetPlatform::FPGpr0, destination); + } + + static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target) + { + as->MacroAssembler::loadDouble(as->loadConstant(c, TargetPlatform::ScratchRegister), target); + } + + static void storeReturnValue(JITAssembler *as, FPRegisterID dest) + { + as->moveIntsToDouble(TargetPlatform::LowReturnValueRegister, TargetPlatform::HighReturnValueRegister, dest, TargetPlatform::FPGpr0); + } + + static void storeReturnValue(JITAssembler *as, const Pointer &dest) + { + Address destination = dest; + as->store32(TargetPlatform::LowReturnValueRegister, destination); + destination.offset += 4; + as->store32(TargetPlatform::HighReturnValueRegister, destination); + } + + static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t) + { + const auto lowReg = TargetPlatform::LowReturnValueRegister; + const auto highReg = TargetPlatform::HighReturnValueRegister; + + if (t->kind == IR::Temp::PhysicalRegister) { + switch (t->type) { + case IR::DoubleType: + as->moveDoubleToInts((FPRegisterID) t->index, lowReg, highReg); + break; + case IR::UInt32Type: { + RegisterID srcReg = (RegisterID) t->index; + Jump intRange = as->branch32(JITAssembler::GreaterThanOrEqual, srcReg, TrustedImm32(0)); + as->convertUInt32ToDouble(srcReg, TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); + as->moveDoubleToInts(TargetPlatform::FPGpr0, lowReg, highReg); + Jump done = as->jump(); + intRange.link(as); + as->move(srcReg, lowReg); + as->move(TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); + done.link(as); + } break; + case IR::SInt32Type: + as->move((RegisterID) t->index, lowReg); + as->move(TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); + break; + case IR::BoolType: + as->move((RegisterID) t->index, lowReg); + as->move(TrustedImm32(QV4::Value::Boolean_Type_Internal), highReg); + break; + default: + Q_UNREACHABLE(); + } + } else { + Pointer addr = as->loadAddress(TargetPlatform::ScratchRegister, t); + as->load32(addr, lowReg); + addr.offset += 4; + as->load32(addr, highReg); + } + } + + static void setFunctionReturnValueFromConst(JITAssembler *as, QV4::Primitive retVal) + { + as->move(TrustedImm32(retVal.int_32()), TargetPlatform::LowReturnValueRegister); + as->move(TrustedImm32(retVal.tag()), TargetPlatform::HighReturnValueRegister); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber) + { + Q_UNUSED(as); + Q_UNUSED(temp); + Q_UNUSED(dest); + Q_UNUSED(argumentNumber); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::ArgLocal* al, RegisterID dest, int argumentNumber) + { + Q_UNUSED(as); + Q_UNUSED(al); + Q_UNUSED(dest); + Q_UNUSED(argumentNumber); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber) + { + Q_UNUSED(as); + Q_UNUSED(c); + Q_UNUSED(dest); + Q_UNUSED(argumentNumber); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber) + { + Q_UNUSED(as); + Q_UNUSED(expr); + Q_UNUSED(dest); + Q_UNUSED(argumentNumber); + } + + static void zeroRegister(JITAssembler *as, RegisterID reg) + { + as->move(TrustedImm32(0), reg); + } + + static void zeroStackSlot(JITAssembler *as, int slot) + { + as->poke(TrustedImm32(0), slot); + } + + static void generateCJumpOnUndefined(JITAssembler *as, + RelationalCondition cond, IR::Expr *right, + RegisterID scratchRegister, RegisterID tagRegister, + IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock, + IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) + { + Pointer tagAddr = as->loadAddress(scratchRegister, right); + as->load32(tagAddr, tagRegister); + Jump j = as->branch32(JITAssembler::invert(cond), tagRegister, TrustedImm32(0)); + as->addPatch(falseBlock, j); + + tagAddr.offset += 4; + as->load32(tagAddr, tagRegister); + const TrustedImm32 tag(QV4::Value::Managed_Type_Internal); + Q_ASSERT(nextBlock == as->nextBlock()); + Q_UNUSED(nextBlock); + as->generateCJumpOnCompare(cond, tagRegister, tag, currentBlock, trueBlock, falseBlock); + } + + static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target) + { + Q_ASSERT(source->type == IR::VarType); + // load the tag: + Pointer addr = as->loadAddress(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)); + IR::Temp *targetTemp = target->asTemp(); + if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { + as->load32(addr, TargetPlatform::ReturnValueRegister); + Pointer targetAddr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->store32(TargetPlatform::ReturnValueRegister, targetAddr); + targetAddr.offset += 4; + as->store32(TrustedImm32(Value::Integer_Type_Internal), targetAddr); + } else { + as->load32(addr, (RegisterID) targetTemp->index); + } + Jump intDone = as->jump(); + + // not an int: + fallback.link(as); + generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, + as->loadAddress(TargetPlatform::ScratchRegister, source)); + as->storeInt32(TargetPlatform::ReturnValueRegister, target); + + intDone.link(as); + } + + static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr) + { + as->store32(registerWithPtr, destAddr); + destAddr.offset += 4; + as->store32(TrustedImm32(QV4::Value::Managed_Type_Internal_32), destAddr); + } + + static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) + { + as->and32(TrustedImm32(Value::NotDouble_Mask), tagOrValueRegister); + return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister, + TrustedImm32(Value::NotDouble_Mask)); + } - inline RuntimeCall(uint offset = uint(INT_MIN)); - bool isValid() const { return addr.offset >= 0; } + static void initializeLocalVariables(JITAssembler *as, int localsCount) + { + as->move(TrustedImm32(0), TargetPlatform::ReturnValueRegister); + as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister); + Label loop = as->label(); + as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); + as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister); + as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); + as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister); + Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); + jump.linkTo(loop, as); + } }; -class Assembler : public JSC::MacroAssembler, public TargetPlatform +template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform> +struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatform, 8> +{ + using RegisterID = typename JITAssembler::RegisterID; + using FPRegisterID = typename JITAssembler::FPRegisterID; + using Address = typename JITAssembler::Address; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + using TrustedImm64 = typename JITAssembler::TrustedImm64; + using Pointer = typename JITAssembler::Pointer; + using RelationalCondition = typename JITAssembler::RelationalCondition; + using ResultCondition = typename JITAssembler::ResultCondition; + using BranchTruncateType = typename JITAssembler::BranchTruncateType; + using Jump = typename JITAssembler::Jump; + using Label = typename JITAssembler::Label; + + static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) + { + as->load64(addr, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr) + { + as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->store64(TargetPlatform::ReturnValueRegister, addr); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) + { + as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + Pointer ptr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->store64(TargetPlatform::ReturnValueRegister, ptr); + } + + static void storeReturnValue(JITAssembler *as, FPRegisterID dest) + { + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); + } + + static void storeReturnValue(JITAssembler *as, const Pointer &dest) + { + as->store64(TargetPlatform::ReturnValueRegister, dest); + } + + static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t) + { + if (t->kind == IR::Temp::PhysicalRegister) { + if (t->type == IR::DoubleType) { + as->moveDoubleTo64((FPRegisterID) t->index, + TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), + TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + } else if (t->type == IR::UInt32Type) { + RegisterID srcReg = (RegisterID) t->index; + Jump intRange = as->branch32(RelationalCondition::GreaterThanOrEqual, srcReg, TrustedImm32(0)); + as->convertUInt32ToDouble(srcReg, TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); + as->moveDoubleTo64(TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + Jump done = as->jump(); + intRange.link(as); + as->zeroExtend32ToPtr(srcReg, TargetPlatform::ReturnValueRegister); + quint64 tag = QV4::Value::Integer_Type_Internal; + as->or64(TrustedImm64(tag << 32), + TargetPlatform::ReturnValueRegister); + done.link(as); + } else { + as->zeroExtend32ToPtr((RegisterID) t->index, TargetPlatform::ReturnValueRegister); + quint64 tag; + switch (t->type) { + case IR::SInt32Type: + tag = QV4::Value::Integer_Type_Internal; + break; + case IR::BoolType: + tag = QV4::Value::Boolean_Type_Internal; + break; + default: + tag = 31337; // bogus value + Q_UNREACHABLE(); + } + as->or64(TrustedImm64(tag << 32), + TargetPlatform::ReturnValueRegister); + } + } else { + as->copyValue(TargetPlatform::ReturnValueRegister, t); + } + } + + static void setFunctionReturnValueFromConst(JITAssembler *as, QV4::Primitive retVal) + { + as->move(TrustedImm64(retVal.rawValue()), TargetPlatform::ReturnValueRegister); + } + + static void storeValue(JITAssembler *as, QV4::Primitive value, Address destination) + { + as->store64(TrustedImm64(value.rawValue()), destination); + } + + template <typename Source, typename Destination> + static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination) + { + // 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); + } + + static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target) + { + Q_STATIC_ASSERT(sizeof(int64_t) == sizeof(double)); + int64_t i; + memcpy(&i, &c->value, sizeof(double)); + as->move(TrustedImm64(i), TargetPlatform::ReturnValueRegister); + as->move64ToDouble(TargetPlatform::ReturnValueRegister, target); + } + + static void loadArgumentInRegister(JITAssembler *as, Address addressOfValue, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + as->load64(addressOfValue, dest); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (temp) { + Pointer addr = as->loadTempAddress(temp); + as->load64(addr, dest); + } else { + QV4::Value undefined = QV4::Primitive::undefinedValue(); + as->move(TrustedImm64(undefined.rawValue()), dest); + } + } + + static void loadArgumentInRegister(JITAssembler *as, IR::ArgLocal* al, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (al) { + Pointer addr = as->loadArgLocalAddress(dest, al); + as->load64(addr, dest); + } else { + QV4::Value undefined = QV4::Primitive::undefinedValue(); + as->move(TrustedImm64(undefined.rawValue()), dest); + } + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + QV4::Value v = convertToValue(c); + as->move(TrustedImm64(v.rawValue()), dest); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (!expr) { + QV4::Value undefined = QV4::Primitive::undefinedValue(); + as->move(TrustedImm64(undefined.rawValue()), dest); + } else if (IR::Temp *t = expr->asTemp()){ + loadArgumentInRegister(as, t, dest, argumentNumber); + } else if (IR::ArgLocal *al = expr->asArgLocal()) { + loadArgumentInRegister(as, al, dest, argumentNumber); + } else if (IR::Const *c = expr->asConst()) { + loadArgumentInRegister(as, c, dest, argumentNumber); + } else { + Q_ASSERT(!"unimplemented expression type in loadArgument"); + } + } + + static void zeroRegister(JITAssembler *as, RegisterID reg) + { + as->move(TrustedImm64(0), reg); + } + + static void zeroStackSlot(JITAssembler *as, int slot) + { + as->store64(TrustedImm64(0), as->addressForPoke(slot)); + } + + static void generateCJumpOnCompare(JITAssembler *as, + RelationalCondition cond, + RegisterID left, + TrustedImm64 right, + IR::BasicBlock *nextBlock, + IR::BasicBlock *currentBlock, + IR::BasicBlock *trueBlock, + IR::BasicBlock *falseBlock) + { + if (trueBlock == nextBlock) { + Jump target = as->branch64(as->invert(cond), left, right); + as->addPatch(falseBlock, target); + } else { + Jump target = as->branch64(cond, left, right); + as->addPatch(trueBlock, target); + as->jumpToBlock(currentBlock, falseBlock); + } + } + + static void generateCJumpOnUndefined(JITAssembler *as, + RelationalCondition cond, IR::Expr *right, + RegisterID scratchRegister, RegisterID tagRegister, + IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock, + IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) + { + Pointer addr = as->loadAddress(scratchRegister, right); + as->load64(addr, tagRegister); + const TrustedImm64 tag(0); + generateCJumpOnCompare(as, cond, tagRegister, tag, nextBlock, currentBlock, trueBlock, falseBlock); + } + + static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target) + { + Q_ASSERT(source->type == IR::VarType); + Pointer addr = as->loadAddress(TargetPlatform::ScratchRegister, source); + as->load64(addr, TargetPlatform::ScratchRegister); + as->move(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + + // check if it's integer convertible + as->urshift64(TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), TargetPlatform::ScratchRegister); + Jump isIntConvertible = as->branch32(RelationalCondition::Equal, TargetPlatform::ScratchRegister, TrustedImm32(3)); + + // nope, not integer convertible, so check for a double: + as->urshift64(TrustedImm32( + QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift), + TargetPlatform::ScratchRegister); + Jump fallback = as->branch32(RelationalCondition::GreaterThan, TargetPlatform::ScratchRegister, TrustedImm32(0)); + + // it's a double + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->move64ToDouble(TargetPlatform::ReturnValueRegister, TargetPlatform::FPGpr0); + Jump success = + as->branchTruncateDoubleToInt32(TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister, + BranchTruncateType::BranchIfTruncateSuccessful); + + // not an int: + fallback.link(as); + generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, + as->loadAddress(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); + as->store32(TargetPlatform::ReturnValueRegister, targetAddr); + targetAddr.offset += 4; + as->store32(TrustedImm32(Value::Integer_Type_Internal), targetAddr); + } else { + as->storeInt32(TargetPlatform::ReturnValueRegister, target); + } + } + + static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr) + { + as->store64(registerWithPtr, destAddr); + } + + static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) + { + as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagOrValueRegister); + return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister, + TrustedImm32(0)); + } + + static void initializeLocalVariables(JITAssembler *as, int localsCount) + { + as->move(TrustedImm64(0), TargetPlatform::ReturnValueRegister); + as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister); + Label loop = as->label(); + as->store64(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); + as->add64(TrustedImm32(8), TargetPlatform::LocalsRegister); + Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); + jump.linkTo(loop, as); + } +}; + +template <typename TargetConfiguration> +class Assembler : public TargetConfiguration::MacroAssembler, public TargetConfiguration::Platform { Q_DISABLE_COPY(Assembler) public: Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator); + using MacroAssembler = typename TargetConfiguration::MacroAssembler; + using RegisterID = typename MacroAssembler::RegisterID; + using FPRegisterID = typename MacroAssembler::FPRegisterID; + using Address = typename MacroAssembler::Address; + using Label = typename MacroAssembler::Label; + using Jump = typename MacroAssembler::Jump; + using DataLabelPtr = typename MacroAssembler::DataLabelPtr; + using TrustedImm32 = typename MacroAssembler::TrustedImm32; + using TrustedImm64 = typename MacroAssembler::TrustedImm64; + using TrustedImmPtr = typename MacroAssembler::TrustedImmPtr; + using RelationalCondition = typename MacroAssembler::RelationalCondition; + using typename MacroAssembler::DoubleCondition; + using MacroAssembler::label; + using MacroAssembler::move; + using MacroAssembler::jump; + using MacroAssembler::add32; + using MacroAssembler::and32; + using MacroAssembler::store32; + using MacroAssembler::loadPtr; + using MacroAssembler::load32; + using MacroAssembler::branch32; + using MacroAssembler::subDouble; + using MacroAssembler::subPtr; + using MacroAssembler::addPtr; + using MacroAssembler::call; + using MacroAssembler::poke; + using MacroAssembler::branchTruncateDoubleToUint32; + using MacroAssembler::or32; + using MacroAssembler::moveDouble; + using MacroAssembler::convertUInt32ToDouble; + using MacroAssembler::invert; + using MacroAssembler::convertInt32ToDouble; + using MacroAssembler::rshift32; + using MacroAssembler::storePtr; + using MacroAssembler::ret; + + using JITTargetPlatform = typename TargetConfiguration::Platform; + using JITTargetPlatform::RegisterArgumentCount; + using JITTargetPlatform::StackSpaceAllocatedUponFunctionEntry; + using JITTargetPlatform::RegisterSize; + using JITTargetPlatform::StackAlignment; + using JITTargetPlatform::ReturnValueRegister; + using JITTargetPlatform::StackPointerRegister; + using JITTargetPlatform::ScratchRegister; + using JITTargetPlatform::EngineRegister; + using JITTargetPlatform::StackShadowSpace; + using JITTargetPlatform::registerForArgument; + using JITTargetPlatform::FPGpr0; + using JITTargetPlatform::platformEnterStandardStackFrame; + using JITTargetPlatform::platformLeaveStandardStackFrame; + + using RegisterSizeDependentOps = RegisterSizeDependentAssembler<Assembler<TargetConfiguration>, MacroAssembler, JITTargetPlatform, RegisterSize>; + + struct LookupCall { + Address addr; + uint getterSetterOffset; + + LookupCall(const Address &addr, uint getterSetterOffset) + : addr(addr) + , getterSetterOffset(getterSetterOffset) + {} + }; + + struct RuntimeCall { + Address addr; + + inline RuntimeCall(uint offset = uint(INT_MIN)); + bool isValid() const { return addr.offset >= 0; } + }; + // Explicit type to allow distinguishing between // pushing an address itself or the value it points // to onto the stack when calling functions. @@ -319,20 +945,29 @@ public: void addPatch(DataLabelPtr patch, IR::BasicBlock *target); void generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm64 right, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock); -#endif void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - Jump genTryDoubleConversion(IR::Expr *src, Assembler::FPRegisterID dest); - Assembler::Jump branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); - Assembler::Jump branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); + void generateCJumpOnUndefined(RelationalCondition cond, IR::Expr *right, + RegisterID scratchRegister, RegisterID tagRegister, + IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, + IR::BasicBlock *falseBlock) + { + RegisterSizeDependentOps::generateCJumpOnUndefined(this, cond, right, scratchRegister, tagRegister, + _nextBlock, currentBlock, trueBlock, falseBlock); + } + + Jump generateIsDoubleCheck(RegisterID tagOrValueRegister) + { + return RegisterSizeDependentOps::generateIsDoubleCheck(this, tagOrValueRegister); + } + + Jump genTryDoubleConversion(IR::Expr *src, FPRegisterID dest); + 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 loadTempAddress(IR::Temp *t); @@ -396,7 +1031,7 @@ public: void loadArgumentInRegister(PointerToValue temp, RegisterID dest, int argumentNumber) { if (!temp.value) { - move(TrustedImmPtr(0), dest); + RegisterSizeDependentOps::zeroRegister(this, dest); } else { Pointer addr = toAddress(dest, temp.value, argumentNumber); loadArgumentInRegister(addr, dest, argumentNumber); @@ -415,70 +1050,31 @@ public: loadArgumentInRegister(addr, dest, argumentNumber); } -#ifdef VALUE_FITS_IN_REGISTER void loadArgumentInRegister(IR::Temp* temp, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - if (temp) { - Pointer addr = loadTempAddress(temp); - load64(addr, dest); - } else { - QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.rawValue()), dest); - } + RegisterSizeDependentOps::loadArgumentInRegister(this, temp, dest, argumentNumber); } void loadArgumentInRegister(IR::ArgLocal* al, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - if (al) { - Pointer addr = loadArgLocalAddress(dest, al); - load64(addr, dest); - } else { - QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.rawValue()), dest); - } + RegisterSizeDependentOps::loadArgumentInRegister(this, al, dest, argumentNumber); } void loadArgumentInRegister(IR::Const* c, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - QV4::Value v = convertToValue(c); - move(TrustedImm64(v.rawValue()), dest); + RegisterSizeDependentOps::loadArgumentInRegister(this, c, dest, argumentNumber); } void loadArgumentInRegister(IR::Expr* expr, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - if (!expr) { - QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.rawValue()), dest); - } else if (IR::Temp *t = expr->asTemp()){ - loadArgumentInRegister(t, dest, argumentNumber); - } else if (IR::ArgLocal *al = expr->asArgLocal()) { - loadArgumentInRegister(al, dest, argumentNumber); - } else if (IR::Const *c = expr->asConst()) { - loadArgumentInRegister(c, dest, argumentNumber); - } else { - Q_ASSERT(!"unimplemented expression type in loadArgument"); - } - } -#else - void loadArgumentInRegister(IR::Expr*, RegisterID) - { - Q_ASSERT(!"unimplemented: expression in loadArgument"); + RegisterSizeDependentOps::loadArgumentInRegister(this, expr, dest, argumentNumber); } -#endif void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest, int argumentNumber) { Q_UNUSED(argumentNumber); - xorPtr(dest, dest); + RegisterSizeDependentOps::zeroRegister(this, dest); if (imm32.m_value) move(imm32, dest); } @@ -499,55 +1095,13 @@ public: void storeReturnValue(FPRegisterID dest) { -#ifdef VALUE_FITS_IN_REGISTER - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - move64ToDouble(ReturnValueRegister, dest); -#elif defined(Q_PROCESSOR_ARM) - moveIntsToDouble(JSC::ARMRegisters::r0, JSC::ARMRegisters::r1, dest, FPGpr0); -#elif defined(Q_PROCESSOR_X86) - moveIntsToDouble(JSC::X86Registers::eax, JSC::X86Registers::edx, dest, FPGpr0); -#elif defined(Q_PROCESSOR_MIPS) - moveIntsToDouble(JSC::MIPSRegisters::v0, JSC::MIPSRegisters::v1, dest, FPGpr0); -#else - subPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); - Pointer tmp(StackPointerRegister, 0); - storeReturnValue(tmp); - loadDouble(tmp, dest); - addPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); -#endif + RegisterSizeDependentOps::storeReturnValue(this, dest); } -#ifdef VALUE_FITS_IN_REGISTER - void storeReturnValue(const Pointer &dest) - { - store64(ReturnValueRegister, dest); - } -#elif defined(Q_PROCESSOR_X86) void storeReturnValue(const Pointer &dest) { - Pointer destination = dest; - store32(JSC::X86Registers::eax, destination); - destination.offset += 4; - store32(JSC::X86Registers::edx, destination); + RegisterSizeDependentOps::storeReturnValue(this, dest); } -#elif defined(Q_PROCESSOR_ARM) - void storeReturnValue(const Pointer &dest) - { - Pointer destination = dest; - store32(JSC::ARMRegisters::r0, destination); - destination.offset += 4; - store32(JSC::ARMRegisters::r1, destination); - } -#elif defined(Q_PROCESSOR_MIPS) - void storeReturnValue(const Pointer &dest) - { - Pointer destination = dest; - store32(JSC::MIPSRegisters::v0, destination); - destination.offset += 4; - store32(JSC::MIPSRegisters::v1, destination); - } -#endif void storeReturnValue(IR::Expr *target) { @@ -609,7 +1163,7 @@ public: Pointer ptr = toAddress(ScratchRegister, temp.value, argumentNumber); loadArgumentOnStack<StackSlot>(ptr, argumentNumber); } else { - poke(TrustedImmPtr(0), StackSlot); + RegisterSizeDependentOps::zeroStackSlot(this, StackSlot); } } @@ -648,38 +1202,18 @@ public: moveDouble(source, (FPRegisterID) targetTemp->index); return; } -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - moveDoubleTo64(source, ReturnValueRegister); - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - Pointer ptr = loadAddress(ScratchRegister, target); - store64(ReturnValueRegister, ptr); -#else - Pointer ptr = loadAddress(ScratchRegister, target); - storeDouble(source, ptr); -#endif + RegisterSizeDependentOps::storeDouble(this, source, target); } -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - // We need to (de)mangle the double + void loadDouble(Address addr, FPRegisterID dest) { - load64(addr, ReturnValueRegister); - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - move64ToDouble(ReturnValueRegister, dest); + RegisterSizeDependentOps::loadDouble(this, addr, dest); } void storeDouble(FPRegisterID source, Address addr) { - moveDoubleTo64(source, ReturnValueRegister); - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - store64(ReturnValueRegister, addr); + RegisterSizeDependentOps::storeDouble(this, source, addr); } -#else - using JSC::MacroAssembler::loadDouble; - using JSC::MacroAssembler::storeDouble; -#endif template <typename Result, typename Source> void copyValue(Result result, Source source); @@ -691,8 +1225,15 @@ public: { Q_ASSERT(!source->asTemp() || source->asTemp()->kind != IR::Temp::PhysicalRegister); Q_ASSERT(target.base != scratchRegister); - JSC::MacroAssembler::loadDouble(loadAddress(scratchRegister, source), FPGpr0); - JSC::MacroAssembler::storeDouble(FPGpr0, target); + TargetConfiguration::MacroAssembler::loadDouble(loadAddress(scratchRegister, source), FPGpr0); + TargetConfiguration::MacroAssembler::storeDouble(FPGpr0, target); + } + + // 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)); } void storeValue(QV4::Primitive value, RegisterID destination) @@ -704,13 +1245,7 @@ public: void storeValue(QV4::Primitive value, Address destination) { -#ifdef VALUE_FITS_IN_REGISTER - store64(TrustedImm64(value.rawValue()), destination); -#else - store32(TrustedImm32(value.int_32()), destination); - destination.offset += 4; - store32(TrustedImm32(value.tag()), destination); -#endif + RegisterSizeDependentOps::storeValue(this, value, destination); } void storeValue(QV4::Primitive value, IR::Expr* temp); @@ -722,7 +1257,7 @@ public: void checkException() { load32(Address(EngineRegister, qOffsetOf(QV4::ExecutionEngine, hasException)), ScratchRegister); - Jump exceptionThrown = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); + Jump exceptionThrown = branch32(RelationalCondition::NotEqual, ScratchRegister, TrustedImm32(0)); if (catchBlock) addPatch(catchBlock, exceptionThrown); else @@ -781,6 +1316,27 @@ public: enum { Size = 0 }; }; + template <typename T> bool prepareCall(T &) + { return true; } + + bool prepareCall(LookupCall &lookupCall) + { + // 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)), + lookupCall.addr.base); + // pre-calculate the indirect address for the lookupCall table: + if (lookupCall.addr.offset) + addPtr(TrustedImm32(lookupCall.addr.offset), lookupCall.addr.base); + // store it as the first argument + loadArgumentOnStackOrRegister<0>(lookupCall.addr.base); + // set the destination addresses offset to the getterSetterOffset. The base is the lookupCall table's address + lookupCall.addr.offset = lookupCall.getterSetterOffset; + return false; + } + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { @@ -813,12 +1369,11 @@ public: loadArgumentOnStackOrRegister<2>(arg3); loadArgumentOnStackOrRegister<1>(arg2); - if (prepareCall(function, this)) + if (prepareCall(function)) loadArgumentOnStackOrRegister<0>(arg1); -#ifdef RESTORE_EBX_ON_CALL - load32(ebxAddressOnStack(), JSC::X86Registers::ebx); // restore the GOT ptr -#endif + if (JITTargetPlatform::gotRegister != -1) + load32(Address(JITTargetPlatform::FramePointerRegister, JITTargetPlatform::savedGOTRegisterSlotOnStack()), static_cast<RegisterID>(JITTargetPlatform::gotRegister)); // restore the GOT ptr callAbsolute(functionName, function); @@ -957,7 +1512,7 @@ public: void storeUInt32(RegisterID reg, Pointer addr) { // The UInt32 representation in QV4::Value is really convoluted. See also toUInt32Register. - Jump intRange = branch32(GreaterThanOrEqual, reg, TrustedImm32(0)); + Jump intRange = branch32(RelationalCondition::GreaterThanOrEqual, reg, TrustedImm32(0)); convertUInt32ToDouble(reg, FPGpr0, ReturnValueRegister); storeDouble(FPGpr0, addr); Jump done = jump(); @@ -980,15 +1535,7 @@ public: FPRegisterID toDoubleRegister(IR::Expr *e, FPRegisterID target = FPGpr0) { if (IR::Const *c = e->asConst()) { -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Q_STATIC_ASSERT(sizeof(int64_t) == sizeof(double)); - int64_t i; - memcpy(&i, &c->value, sizeof(double)); - move(TrustedImm64(i), ReturnValueRegister); - move64ToDouble(ReturnValueRegister, target); -#else - JSC::MacroAssembler::loadDouble(loadConstant(c, ScratchRegister), target); -#endif + RegisterSizeDependentOps::loadDoubleConstant(this, c, target); return target; } @@ -1047,7 +1594,7 @@ public: Pointer tagAddr = addr; tagAddr.offset += 4; load32(tagAddr, scratchReg); - Jump inIntRange = branch32(Equal, scratchReg, TrustedImm32(QV4::Value::Integer_Type_Internal)); + Jump inIntRange = branch32(RelationalCondition::Equal, scratchReg, TrustedImm32(QV4::Value::Integer_Type_Internal)); // it's not in signed int range, so load it as a double, and truncate it down loadDouble(addr, FPGpr0); @@ -1065,10 +1612,21 @@ public: return scratchReg; } + void returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave); + JSC::MacroAssemblerCodeRef link(int *codeSize); void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave); const StackLayout &stackLayout() const { return *_stackLayout.data(); } + void initializeLocalVariables() + { + const int locals = _stackLayout->calculateJSStackFrameSize(); + if (locals <= 0) + return; + loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)), JITTargetPlatform::LocalsRegister); + RegisterSizeDependentOps::initializeLocalVariables(this, locals); + storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop))); + } Label exceptionReturnLabel; IR::BasicBlock * catchBlock; @@ -1095,22 +1653,16 @@ private: QV4::Compiler::JSUnitGenerator *_jsGenerator; }; +template <typename TargetConfiguration> template <typename Result, typename Source> -void Assembler::copyValue(Result result, Source source) +void Assembler<TargetConfiguration>::copyValue(Result result, Source source) { -#ifdef VALUE_FITS_IN_REGISTER - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgumentInRegister(source, ReturnValueRegister, 0); - storeReturnValue(result); -#else - loadDouble(source, FPGpr0); - storeDouble(FPGpr0, result); -#endif + RegisterSizeDependentOps::copyValueViaRegisters(this, source, result); } +template <typename TargetConfiguration> template <typename Result> -void Assembler::copyValue(Result result, IR::Expr* source) +void Assembler<TargetConfiguration>::copyValue(Result result, IR::Expr* source) { if (source->type == IR::BoolType) { RegisterID reg = toInt32Register(source, ScratchRegister); @@ -1124,15 +1676,7 @@ void Assembler::copyValue(Result result, IR::Expr* source) } else if (source->type == IR::DoubleType) { storeDouble(toDoubleRegister(source), result); } else if (source->asTemp() || source->asArgLocal()) { -#ifdef VALUE_FITS_IN_REGISTER - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgumentInRegister(source, ReturnValueRegister, 0); - storeReturnValue(result); -#else - loadDouble(source, FPGpr0); - storeDouble(FPGpr0, result); -#endif + RegisterSizeDependentOps::copyValueViaRegisters(this, source, result); } else if (IR::Const *c = source->asConst()) { QV4::Primitive v = convertToValue(c); storeValue(v, result); @@ -1141,34 +1685,12 @@ void Assembler::copyValue(Result result, IR::Expr* source) } } -inline RuntimeCall::RuntimeCall(uint offset) +template <typename TargetConfiguration> +inline Assembler<TargetConfiguration>::RuntimeCall::RuntimeCall(uint offset) : addr(Assembler::EngineRegister, offset + qOffsetOf(QV4::ExecutionEngine, runtime)) { } - - -template <typename T> inline bool prepareCall(T &, Assembler *) -{ return true; } - -template <> inline bool prepareCall(LookupCall &lookupCall, Assembler *as) -{ - // IMPORTANT! See generateLookupCall in qv4isel_masm_p.h for details! - - // load the table from the context - as->loadPtr(Assembler::Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - as->loadPtr(Assembler::Address(Assembler::ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, lookups)), - lookupCall.addr.base); - // pre-calculate the indirect address for the lookupCall table: - if (lookupCall.addr.offset) - as->addPtr(Assembler::TrustedImm32(lookupCall.addr.offset), lookupCall.addr.base); - // store it as the first argument - as->loadArgumentOnStackOrRegister<0>(lookupCall.addr.base); - // set the destination addresses offset to the getterSetterOffset. The base is the lookupCall table's address - lookupCall.addr.offset = lookupCall.getterSetterOffset; - return false; -} - } // end of namespace JIT } // end of namespace QV4 diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index d2758c4a47..22067bbb13 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -41,8 +41,128 @@ #if ENABLE(ASSEMBLER) -using namespace QV4; -using namespace JIT; +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace JIT { + +template <typename JITAssembler> +struct ArchitectureSpecificBinaryOperation +{ + using FPRegisterID = typename JITAssembler::FPRegisterID; + + static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + Q_UNUSED(as); + Q_UNUSED(lhs); + Q_UNUSED(rhs); + Q_UNUSED(targetReg); + return false; + } + static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + Q_UNUSED(as); + Q_UNUSED(lhs); + Q_UNUSED(rhs); + Q_UNUSED(targetReg); + return false; + } + static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + Q_UNUSED(as); + Q_UNUSED(lhs); + Q_UNUSED(rhs); + Q_UNUSED(targetReg); + return false; + } + static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + Q_UNUSED(as); + Q_UNUSED(lhs); + Q_UNUSED(rhs); + Q_UNUSED(targetReg); + return false; + } +}; + +#if CPU(X86) +template <> +struct ArchitectureSpecificBinaryOperation<Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>> +{ + using JITAssembler = Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>; + using FPRegisterID = JITAssembler::FPRegisterID; + using Address = JITAssembler::Address; + + static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address] + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); + as->addDouble(addr, targetReg); + return true; + } + if (IR::Temp *t = rhs->asTemp()) { // Y = X + [temp-memory-address] -> Y = X; Y += [temp-memory-address] + if (t->kind != IR::Temp::PhysicalRegister) { + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + as->addDouble(as->loadTempAddress(t), targetReg); + return true; + } + } + return false; + } + static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address] + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); + as->mulDouble(addr, targetReg); + return true; + } + if (IR::Temp *t = rhs->asTemp()) { // Y = X * [temp-memory-address] -> Y = X; Y *= [temp-memory-address] + if (t->kind != IR::Temp::PhysicalRegister) { + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + as->mulDouble(as->loadTempAddress(t), targetReg); + return true; + } + } + return false; + } + static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address] + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); + as->subDouble(addr, targetReg); + return true; + } + if (IR::Temp *t = rhs->asTemp()) { // Y = X - [temp-memory-address] -> Y = X; Y -= [temp-memory-address] + if (t->kind != IR::Temp::PhysicalRegister) { + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + as->subDouble(as->loadTempAddress(t), targetReg); + return true; + } + } + return false; + } + static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address] + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); + as->divDouble(addr, targetReg); + return true; + } + if (IR::Temp *t = rhs->asTemp()) { // Y = X / [temp-memory-address] -> Y = X; Y /= [temp-memory-address] + if (t->kind != IR::Temp::PhysicalRegister) { + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + as->divDouble(as->loadTempAddress(t), targetReg); + return true; + } + } + return false; + } +}; +#endif #define OP(op) \ { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } @@ -57,7 +177,8 @@ using namespace JIT; #define NULL_OP \ { 0, 0, 0, 0, 0, false } -const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { +template <typename JITAssembler> +const typename Binop<JITAssembler>::OpInfo Binop<JITAssembler>::operations[IR::LastAluOp + 1] = { NULL_OP, // OpInvalid NULL_OP, // OpIfTrue NULL_OP, // OpNot @@ -67,20 +188,20 @@ const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { NULL_OP, // OpIncrement NULL_OP, // OpDecrement - INLINE_OP(bitAnd, &Binop::inline_and32, &Binop::inline_and32), // OpBitAnd - INLINE_OP(bitOr, &Binop::inline_or32, &Binop::inline_or32), // OpBitOr - INLINE_OP(bitXor, &Binop::inline_xor32, &Binop::inline_xor32), // OpBitXor + INLINE_OP(bitAnd, &Binop<JITAssembler>::inline_and32, &Binop<JITAssembler>::inline_and32), // OpBitAnd + INLINE_OP(bitOr, &Binop<JITAssembler>::inline_or32, &Binop<JITAssembler>::inline_or32), // OpBitOr + INLINE_OP(bitXor, &Binop<JITAssembler>::inline_xor32, &Binop<JITAssembler>::inline_xor32), // OpBitXor - INLINE_OPCONTEXT(add, &Binop::inline_add32, &Binop::inline_add32), // OpAdd - INLINE_OP(sub, &Binop::inline_sub32, &Binop::inline_sub32), // OpSub - INLINE_OP(mul, &Binop::inline_mul32, &Binop::inline_mul32), // OpMul + INLINE_OPCONTEXT(add, &Binop<JITAssembler>::inline_add32, &Binop<JITAssembler>::inline_add32), // OpAdd + INLINE_OP(sub, &Binop<JITAssembler>::inline_sub32, &Binop<JITAssembler>::inline_sub32), // OpSub + INLINE_OP(mul, &Binop<JITAssembler>::inline_mul32, &Binop<JITAssembler>::inline_mul32), // OpMul OP(div), // OpDiv OP(mod), // OpMod - INLINE_OP(shl, &Binop::inline_shl32, &Binop::inline_shl32), // OpLShift - INLINE_OP(shr, &Binop::inline_shr32, &Binop::inline_shr32), // OpRShift - INLINE_OP(ushr, &Binop::inline_ushr32, &Binop::inline_ushr32), // OpURShift + INLINE_OP(shl, &Binop<JITAssembler>::inline_shl32, &Binop<JITAssembler>::inline_shl32), // OpLShift + INLINE_OP(shr, &Binop<JITAssembler>::inline_shr32, &Binop<JITAssembler>::inline_shr32), // OpRShift + INLINE_OP(ushr, &Binop<JITAssembler>::inline_ushr32, &Binop<JITAssembler>::inline_ushr32), // OpURShift OP(greaterThan), // OpGt OP(lessThan), // OpLt @@ -100,7 +221,8 @@ const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { -void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) +template <typename JITAssembler> +void Binop<JITAssembler>::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) { if (op != IR::OpMod && lhs->type == IR::DoubleType && rhs->type == IR::DoubleType) { @@ -125,15 +247,15 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) info = stringAdd; } - RuntimeCall fallBack(info.fallbackImplementation); - RuntimeCall context(info.contextImplementation); + typename JITAssembler::RuntimeCall fallBack(info.fallbackImplementation); + typename JITAssembler::RuntimeCall context(info.contextImplementation); if (fallBack.isValid()) { as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, fallBack, PointerToValue(lhs), PointerToValue(rhs)); } else if (context.isValid()) { as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, context, - Assembler::EngineRegister, + JITAssembler::EngineRegister, PointerToValue(lhs), PointerToValue(rhs)); } else { @@ -145,119 +267,70 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) } -void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) +template <typename JITAssembler> +void Binop<JITAssembler>::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); FPRegisterID targetReg; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) targetReg = (FPRegisterID) targetTemp->index; else - targetReg = Assembler::FPGpr0; + targetReg = JITAssembler::FPGpr0; switch (op) { case IR::OpAdd: if (lhs->asConst()) std::swap(lhs, rhs); // Y = constant + X -> Y = X + constant -#if CPU(X86) - if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, Assembler::ScratchRegister); - as->addDouble(addr, targetReg); + if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleAdd(as, lhs, rhs, targetReg)) break; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X + [temp-memory-address] -> Y = X; Y += [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->addDouble(as->loadTempAddress(t), targetReg); - break; - } - } -#endif - as->addDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + + as->addDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; case IR::OpMul: if (lhs->asConst()) std::swap(lhs, rhs); // Y = constant * X -> Y = X * constant -#if CPU(X86) - if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, Assembler::ScratchRegister); - as->mulDouble(addr, targetReg); + if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleMul(as, lhs, rhs, targetReg)) break; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X * [temp-memory-address] -> Y = X; Y *= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->mulDouble(as->loadTempAddress(t), targetReg); - break; - } - } -#endif - as->mulDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + + as->mulDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; case IR::OpSub: -#if CPU(X86) - if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, Assembler::ScratchRegister); - as->subDouble(addr, targetReg); + if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleSub(as, lhs, rhs, targetReg)) break; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X - [temp-memory-address] -> Y = X; Y -= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->subDouble(as->loadTempAddress(t), targetReg); - break; - } - } -#endif + if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister && targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister && targetTemp->index == rhs->asTemp()->index) { // Y = X - Y -> Tmp = Y; Y = X - Tmp - as->moveDouble(as->toDoubleRegister(rhs, Assembler::FPGpr1), Assembler::FPGpr1); - as->subDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), Assembler::FPGpr1, targetReg); + as->moveDouble(as->toDoubleRegister(rhs, JITAssembler::FPGpr1), JITAssembler::FPGpr1); + as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), JITAssembler::FPGpr1, targetReg); break; } - as->subDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; case IR::OpDiv: -#if CPU(X86) - if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, Assembler::ScratchRegister); - as->divDouble(addr, targetReg); + if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleDiv(as, lhs, rhs, targetReg)) break; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X / [temp-memory-address] -> Y = X; Y /= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->divDouble(as->loadTempAddress(t), targetReg); - break; - } - } -#endif if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister && targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister && targetTemp->index == rhs->asTemp()->index) { // Y = X / Y -> Tmp = Y; Y = X / Tmp - as->moveDouble(as->toDoubleRegister(rhs, Assembler::FPGpr1), Assembler::FPGpr1); - as->divDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), Assembler::FPGpr1, targetReg); + as->moveDouble(as->toDoubleRegister(rhs, JITAssembler::FPGpr1), JITAssembler::FPGpr1); + as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), JITAssembler::FPGpr1, targetReg); break; } - as->divDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; default: { - Q_ASSERT(target->type == IR::BoolType); Jump trueCase = as->branchDouble(false, op, lhs, rhs); as->storeBool(false, target); Jump done = as->jump(); @@ -271,8 +344,8 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) as->storeDouble(targetReg, target); } - -bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) +template <typename JITAssembler> +bool Binop<JITAssembler>::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { Q_ASSERT(leftSource->type == IR::SInt32Type); Q_ASSERT(rightSource->type == IR::SInt32Type); @@ -305,7 +378,7 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta bool inplaceOpWithAddress = false; IR::Temp *targetTemp = target->asTemp(); - RegisterID targetReg = Assembler::ReturnValueRegister; + RegisterID targetReg = JITAssembler::ReturnValueRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { IR::Temp *rhs = rightSource->asTemp(); if (!rhs || rhs->kind != IR::Temp::PhysicalRegister || rhs->index != targetTemp->index) { @@ -369,12 +442,12 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta && targetTemp->index == rightSource->asTemp()->index) { // X = Y - X -> Tmp = X; X = Y; X -= Tmp targetReg = (RegisterID) targetTemp->index; - as->move(targetReg, Assembler::ScratchRegister); + as->move(targetReg, JITAssembler::ScratchRegister); as->move(as->toInt32Register(leftSource, targetReg), targetReg); - as->sub32(Assembler::ScratchRegister, targetReg); + as->sub32(JITAssembler::ScratchRegister, targetReg); } else { as->move(as->toInt32Register(leftSource, targetReg), targetReg); - as->sub32(as->toInt32Register(rightSource, Assembler::ScratchRegister), targetReg); + as->sub32(as->toInt32Register(rightSource, JITAssembler::ScratchRegister), targetReg); } as->storeInt32(targetReg, target); return true; @@ -419,7 +492,7 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta return false; } } else if (inplaceOpWithAddress) { // All cases of X = X op [address-of-Y] - Pointer rhsAddr = as->loadAddress(Assembler::ScratchRegister, rightSource); + Pointer rhsAddr = as->loadAddress(JITAssembler::ScratchRegister, rightSource); switch (op) { case IR::OpBitAnd: as->and32(rhsAddr, targetReg); break; case IR::OpBitOr: as->or32 (rhsAddr, targetReg); break; @@ -433,7 +506,7 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta return false; } } else { // All cases of Z = X op Y - RegisterID r = as->toInt32Register(rightSource, Assembler::ScratchRegister); + RegisterID r = as->toInt32Register(rightSource, JITAssembler::ScratchRegister); switch (op) { case IR::OpBitAnd: as->and32(l, r, targetReg); break; case IR::OpBitOr: as->or32 (l, r, targetReg); break; @@ -452,18 +525,18 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta // Not all CPUs accept shifts over more than 31 bits, and some CPUs (like ARM) will do // surprising stuff when shifting over 0 bits. #define CHECK_RHS(op) { \ - as->and32(TrustedImm32(0x1f), r, Assembler::ScratchRegister); \ - Jump notZero = as->branch32(RelationalCondition::NotEqual, Assembler::ScratchRegister, TrustedImm32(0)); \ + as->and32(TrustedImm32(0x1f), r, JITAssembler::ScratchRegister); \ + Jump notZero = as->branch32(RelationalCondition::NotEqual, JITAssembler::ScratchRegister, TrustedImm32(0)); \ as->move(l, targetReg); \ Jump done = as->jump(); \ notZero.link(as); \ op; \ done.link(as); \ } - case IR::OpLShift: CHECK_RHS(as->lshift32(l, Assembler::ScratchRegister, targetReg)); break; - case IR::OpRShift: CHECK_RHS(as->rshift32(l, Assembler::ScratchRegister, targetReg)); break; + case IR::OpLShift: CHECK_RHS(as->lshift32(l, JITAssembler::ScratchRegister, targetReg)); break; + case IR::OpRShift: CHECK_RHS(as->rshift32(l, JITAssembler::ScratchRegister, targetReg)); break; case IR::OpURShift: - CHECK_RHS(as->urshift32(l, Assembler::ScratchRegister, targetReg)); + CHECK_RHS(as->urshift32(l, JITAssembler::ScratchRegister, targetReg)); as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! return true; @@ -481,17 +554,19 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta return true; } -static inline Assembler::FPRegisterID getFreeFPReg(IR::Expr *shouldNotOverlap, unsigned hint) +template <typename JITAssembler> +inline typename JITAssembler::FPRegisterID getFreeFPReg(IR::Expr *shouldNotOverlap, unsigned hint) { if (IR::Temp *t = shouldNotOverlap->asTemp()) if (t->type == IR::DoubleType) if (t->kind == IR::Temp::PhysicalRegister) if (t->index == hint) - return Assembler::FPRegisterID(hint + 1); - return Assembler::FPRegisterID(hint); + return typename JITAssembler::FPRegisterID(hint + 1); + return typename JITAssembler::FPRegisterID(hint); } -Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) +template <typename JITAssembler> +typename JITAssembler::Jump Binop<JITAssembler>::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { Jump done; @@ -505,8 +580,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc // register. switch (op) { case IR::OpAdd: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -520,8 +595,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc rightIsNoDbl.link(as); } break; case IR::OpMul: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -535,8 +610,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc rightIsNoDbl.link(as); } break; case IR::OpSub: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -550,8 +625,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc rightIsNoDbl.link(as); } break; case IR::OpDiv: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -571,4 +646,20 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc return done; } +template struct QV4::JIT::Binop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>; +#if defined(V4_BOOTSTRAP) +#if !CPU(ARM_THUMB2) +template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>; +#endif +#if !CPU(ARM64) +template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>; +#endif +#endif + +} // end of namespace JIT +} // end of namespace QV4 + +QT_END_NAMESPACE + + #endif diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h index 3742e99e5a..d2d9ba7753 100644 --- a/src/qml/jit/qv4binop_p.h +++ b/src/qml/jit/qv4binop_p.h @@ -61,21 +61,22 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { +template <typename JITAssembler> struct Binop { - Binop(Assembler *assembler, IR::AluOp operation) + Binop(JITAssembler *assembler, IR::AluOp operation) : as(assembler) , op(operation) {} - using Jump = Assembler::Jump; - using Address = Assembler::Address; - using RegisterID = Assembler::RegisterID; - using FPRegisterID = Assembler::FPRegisterID; - using TrustedImm32 = Assembler::TrustedImm32; - using ResultCondition = Assembler::ResultCondition; - using RelationalCondition = Assembler::RelationalCondition; - using Pointer = Assembler::Pointer; - using PointerToValue = Assembler::PointerToValue; + using Jump = typename JITAssembler::Jump; + using Address = typename JITAssembler::Address; + using RegisterID = typename JITAssembler::RegisterID; + using FPRegisterID = typename JITAssembler::FPRegisterID; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + using ResultCondition = typename JITAssembler::ResultCondition; + using RelationalCondition = typename JITAssembler::RelationalCondition; + using Pointer = typename JITAssembler::Pointer; + using PointerToValue = typename JITAssembler::PointerToValue; void generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target); void doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target); @@ -103,8 +104,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) return as->branchAdd32(ResultCondition::Overflow, addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - return as->branchAdd32(ResultCondition::Overflow, Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + return as->branchAdd32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); #endif } @@ -118,8 +119,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) return as->branchSub32(ResultCondition::Overflow, addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - return as->branchSub32(ResultCondition::Overflow, Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + return as->branchSub32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); #endif } @@ -131,10 +132,10 @@ struct Binop { Jump inline_mul32(Address addr, RegisterID reg) { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return as->branchMul32(Assembler::Overflow, addr, reg); + return as->branchMul32(JITAssembler::Overflow, addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - return as->branchMul32(ResultCondition::Overflow, Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + return as->branchMul32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); #endif } @@ -145,9 +146,9 @@ struct Binop { Jump inline_shl32(Address addr, RegisterID reg) { - as->load32(addr, Assembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), Assembler::ScratchRegister); - as->lshift32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); + as->lshift32(JITAssembler::ScratchRegister, reg); return Jump(); } @@ -160,9 +161,9 @@ struct Binop { Jump inline_shr32(Address addr, RegisterID reg) { - as->load32(addr, Assembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), Assembler::ScratchRegister); - as->rshift32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); + as->rshift32(JITAssembler::ScratchRegister, reg); return Jump(); } @@ -175,9 +176,9 @@ struct Binop { Jump inline_ushr32(Address addr, RegisterID reg) { - as->load32(addr, Assembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), Assembler::ScratchRegister); - as->urshift32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); + as->urshift32(JITAssembler::ScratchRegister, reg); return as->branchTest32(ResultCondition::Signed, reg, reg); } @@ -193,8 +194,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) as->and32(addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - as->and32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(JITAssembler::ScratchRegister, reg); #endif return Jump(); } @@ -210,8 +211,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) as->or32(addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - as->or32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->or32(JITAssembler::ScratchRegister, reg); #endif return Jump(); } @@ -227,8 +228,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) as->xor32(addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - as->xor32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->xor32(JITAssembler::ScratchRegister, reg); #endif return Jump(); } @@ -241,7 +242,7 @@ struct Binop { - Assembler *as; + JITAssembler *as; IR::AluOp op; }; diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 20752b5c34..69d6951bb9 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -39,11 +39,7 @@ #include "qv4isel_masm_p.h" #include "qv4runtime_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4regexpobject_p.h" #include "qv4lookup_p.h" -#include "qv4function_p.h" #include "qv4ssa_p.h" #include "qv4regalloc_p.h" #include "qv4assembler_p.h" @@ -68,7 +64,8 @@ using namespace QV4; using namespace QV4::JIT; -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) +template <typename JITAssembler> +InstructionSelection<JITAssembler>::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) , _block(0) , _as(0) @@ -79,12 +76,14 @@ InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::Ex module->unitFlags |= QV4::CompiledData::Unit::ContainsMachineCode; } -InstructionSelection::~InstructionSelection() +template <typename JITAssembler> +InstructionSelection<JITAssembler>::~InstructionSelection() { delete _as; } -void InstructionSelection::run(int functionIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::run(int functionIndex) { IR::Function *function = irModule->functions[functionIndex]; qSwap(_function, function); @@ -93,8 +92,8 @@ void InstructionSelection::run(int functionIndex) opt.run(qmlEngine); static const bool withRegisterAllocator = qEnvironmentVariableIsEmpty("QV4_NO_REGALLOC"); - if (Assembler::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) { - RegisterAllocator regalloc(Assembler::getRegisterInfo()); + if (JITTargetPlatform::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) { + RegisterAllocator regalloc(JITTargetPlatform::getRegisterInfo()); regalloc.run(_function, opt); calculateRegistersToSave(regalloc.usedRegisters()); } else { @@ -103,49 +102,24 @@ void InstructionSelection::run(int functionIndex) opt.convertOutOfSSA(); ConvertTemps().toStackSlots(_function); IR::Optimizer::showMeTheCode(_function, "After stack slot allocation"); - calculateRegistersToSave(Assembler::getRegisterInfo()); // FIXME: this saves all registers. We can probably do with a subset: those that are not used by the register allocator. + calculateRegistersToSave(JITTargetPlatform::getRegisterInfo()); // FIXME: this saves all registers. We can probably do with a subset: those that are not used by the register allocator. } BitVector removableJumps = opt.calculateOptionalJumps(); qSwap(_removableJumps, removableJumps); - Assembler* oldAssembler = _as; - _as = new Assembler(jsGenerator, _function, executableAllocator); + JITAssembler* oldAssembler = _as; + _as = new JITAssembler(jsGenerator, _function, executableAllocator); _as->setStackLayout(6, // 6 == max argc for calls to built-ins with an argument array regularRegistersToSave.size(), fpRegistersToSave.size()); _as->enterStandardStackFrame(regularRegistersToSave, fpRegistersToSave); -#ifdef ARGUMENTS_IN_REGISTERS - _as->move(_as->registerForArgument(0), Assembler::EngineRegister); -#else - _as->loadPtr(addressForArgument(0), Assembler::EngineRegister); -#endif - - const int locals = _as->stackLayout().calculateJSStackFrameSize(); - if (locals > 0) { - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)), Assembler::LocalsRegister); -#ifdef VALUE_FITS_IN_REGISTER - _as->move(Assembler::TrustedImm64(0), Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm32(locals), Assembler::ScratchRegister); - Assembler::Label loop = _as->label(); - _as->store64(Assembler::ReturnValueRegister, Assembler::Address(Assembler::LocalsRegister)); - _as->add64(Assembler::TrustedImm32(8), Assembler::LocalsRegister); - Assembler::Jump jump = _as->branchSub32(Assembler::NonZero, Assembler::TrustedImm32(1), Assembler::ScratchRegister); - jump.linkTo(loop, _as); -#else - _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm32(locals), Assembler::ScratchRegister); - Assembler::Label loop = _as->label(); - _as->store32(Assembler::ReturnValueRegister, Assembler::Address(Assembler::LocalsRegister)); - _as->add32(Assembler::TrustedImm32(4), Assembler::LocalsRegister); - _as->store32(Assembler::ReturnValueRegister, Assembler::Address(Assembler::LocalsRegister)); - _as->add32(Assembler::TrustedImm32(4), Assembler::LocalsRegister); - Assembler::Jump jump = _as->branchSub32(Assembler::NonZero, Assembler::TrustedImm32(1), Assembler::ScratchRegister); - jump.linkTo(loop, _as); -#endif - _as->storePtr(Assembler::LocalsRegister, Address(Assembler::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop))); - } + if (JITTargetPlatform::RegisterArgumentCount > 0) + _as->move(_as->registerForArgument(0), JITTargetPlatform::EngineRegister); + else + _as->loadPtr(addressForArgument(0), JITTargetPlatform::EngineRegister); + _as->initializeLocalVariables(); int lastLine = 0; for (int i = 0, ei = _function->basicBlockCount(); i != ei; ++i) { @@ -158,9 +132,9 @@ void InstructionSelection::run(int functionIndex) for (IR::Stmt *s : _block->statements()) { if (s->location.isValid()) { if (int(s->location.startLine) != lastLine) { - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - Assembler::Address lineAddr(Assembler::ScratchRegister, qOffsetOf(QV4::ExecutionContext::Data, lineNumber)); - _as->store32(Assembler::TrustedImm32(s->location.startLine), lineAddr); + _as->loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister); + Address lineAddr(JITTargetPlatform::ScratchRegister, qOffsetOf(QV4::ExecutionContext::Data, lineNumber)); + _as->store32(TrustedImm32(s->location.startLine), lineAddr); lastLine = s->location.startLine; } } @@ -181,165 +155,187 @@ void InstructionSelection::run(int functionIndex) qSwap(_removableJumps, removableJumps); } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep() +template <typename JITAssembler> +QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection<JITAssembler>::backendCompileStep() { QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; result.adopt(compilationUnit.take()); return result; } -void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) { prepareCallData(args, 0); if (useFastLookups && func->global) { uint index = registerGlobalGetterLookup(*func->id); - generateRuntimeCall(result, callGlobalLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), + generateRuntimeCall(_as, result, callGlobalLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); } else { - generateRuntimeCall(result, callActivationProperty, - Assembler::EngineRegister, - Assembler::StringToIndex(*func->id), + generateRuntimeCall(_as, result, callActivationProperty, + JITTargetPlatform::EngineRegister, + StringToIndex(*func->id), baseAddressForCallData()); } } -void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) { if (kind == IR::Member::MemberOfQmlScopeObject) { - generateRuntimeCall(result, typeofScopeObjectProperty, Assembler::EngineRegister, - Assembler::PointerToValue(base), - Assembler::TrustedImm32(propertyIndex)); + generateRuntimeCall(_as, result, typeofScopeObjectProperty, JITTargetPlatform::EngineRegister, + PointerToValue(base), + TrustedImm32(propertyIndex)); } else if (kind == IR::Member::MemberOfQmlContextObject) { - generateRuntimeCall(result, typeofContextObjectProperty, - Assembler::EngineRegister, Assembler::PointerToValue(base), - Assembler::TrustedImm32(propertyIndex)); + generateRuntimeCall(_as, result, typeofContextObjectProperty, + JITTargetPlatform::EngineRegister, PointerToValue(base), + TrustedImm32(propertyIndex)); } else { Q_UNREACHABLE(); } } -void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) { - generateRuntimeCall(result, typeofMember, Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, typeofMember, JITTargetPlatform::EngineRegister, + PointerToValue(base), StringToIndex(name)); } -void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) { - generateRuntimeCall(result, typeofElement, - Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + generateRuntimeCall(_as, result, typeofElement, + JITTargetPlatform::EngineRegister, + PointerToValue(base), PointerToValue(index)); } -void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofName(const QString &name, IR::Expr *result) { - generateRuntimeCall(result, typeofName, Assembler::EngineRegister, - Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, typeofName, JITTargetPlatform::EngineRegister, + StringToIndex(name)); } -void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) { - generateRuntimeCall(result, typeofValue, Assembler::EngineRegister, - Assembler::PointerToValue(value)); + generateRuntimeCall(_as, result, typeofValue, JITTargetPlatform::EngineRegister, + PointerToValue(value)); } -void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) { - generateRuntimeCall(result, deleteMember, Assembler::EngineRegister, - Assembler::Reference(base), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, deleteMember, JITTargetPlatform::EngineRegister, + Reference(base), StringToIndex(name)); } -void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) { - generateRuntimeCall(result, deleteElement, Assembler::EngineRegister, - Assembler::Reference(base), Assembler::PointerToValue(index)); + generateRuntimeCall(_as, result, deleteElement, JITTargetPlatform::EngineRegister, + Reference(base), PointerToValue(index)); } -void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteName(const QString &name, IR::Expr *result) { - generateRuntimeCall(result, deleteName, Assembler::EngineRegister, - Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, deleteName, JITTargetPlatform::EngineRegister, + StringToIndex(name)); } -void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteValue(IR::Expr *result) { _as->storeValue(Primitive::fromBoolean(false), result); } -void InstructionSelection::callBuiltinThrow(IR::Expr *arg) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinThrow(IR::Expr *arg) { - generateRuntimeCall(Assembler::ReturnValueRegister, throwException, Assembler::EngineRegister, - Assembler::PointerToValue(arg)); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, throwException, JITTargetPlatform::EngineRegister, + PointerToValue(arg)); } -void InstructionSelection::callBuiltinReThrow() +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinReThrow() { _as->jumpToExceptionHandler(); } -void InstructionSelection::callBuiltinUnwindException(IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinUnwindException(IR::Expr *result) { - generateRuntimeCall(result, unwindException, Assembler::EngineRegister); + generateRuntimeCall(_as, result, unwindException, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinPushCatchScope(const QString &exceptionName) { - generateRuntimeCall(Assembler::Void, pushCatchScope, Assembler::EngineRegister, Assembler::StringToIndex(exceptionName)); + generateRuntimeCall(_as, JITAssembler::Void, pushCatchScope, JITTargetPlatform::EngineRegister, StringToIndex(exceptionName)); } -void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) { Q_ASSERT(arg); Q_ASSERT(result); - generateRuntimeCall(result, foreachIterator, Assembler::EngineRegister, Assembler::PointerToValue(arg)); + generateRuntimeCall(_as, result, foreachIterator, JITTargetPlatform::EngineRegister, PointerToValue(arg)); } -void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) { Q_ASSERT(arg); Q_ASSERT(result); - generateRuntimeCall(result, foreachNextPropertyName, Assembler::Reference(arg)); + generateRuntimeCall(_as, result, foreachNextPropertyName, Reference(arg)); } -void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinPushWithScope(IR::Expr *arg) { Q_ASSERT(arg); - generateRuntimeCall(Assembler::Void, pushWithScope, Assembler::Reference(arg), Assembler::EngineRegister); + generateRuntimeCall(_as, JITAssembler::Void, pushWithScope, Reference(arg), JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinPopScope() +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinPopScope() { - generateRuntimeCall(Assembler::Void, popScope, Assembler::EngineRegister); + generateRuntimeCall(_as, JITAssembler::Void, popScope, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeclareVar(bool deletable, const QString &name) { - generateRuntimeCall(Assembler::Void, declareVar, Assembler::EngineRegister, - Assembler::TrustedImm32(deletable), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, JITAssembler::Void, declareVar, JITTargetPlatform::EngineRegister, + TrustedImm32(deletable), StringToIndex(name)); } -void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) { Q_ASSERT(result); int length = prepareVariableArguments(args); - generateRuntimeCall(result, arrayLiteral, Assembler::EngineRegister, - baseAddressForCallArguments(), Assembler::TrustedImm32(length)); + generateRuntimeCall(_as, result, arrayLiteral, JITTargetPlatform::EngineRegister, + baseAddressForCallArguments(), TrustedImm32(length)); } -void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) { Q_ASSERT(result); @@ -415,81 +411,83 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int it = it->next; } - generateRuntimeCall(result, objectLiteral, Assembler::EngineRegister, - baseAddressForCallArguments(), Assembler::TrustedImm32(classId), - Assembler::TrustedImm32(arrayValueCount), Assembler::TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30))); + generateRuntimeCall(_as, result, objectLiteral, JITTargetPlatform::EngineRegister, + baseAddressForCallArguments(), TrustedImm32(classId), + TrustedImm32(arrayValueCount), TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30))); } -void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinSetupArgumentObject(IR::Expr *result) { - generateRuntimeCall(result, setupArgumentsObject, Assembler::EngineRegister); + generateRuntimeCall(_as, result, setupArgumentsObject, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinConvertThisToObject() +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinConvertThisToObject() { - generateRuntimeCall(Assembler::Void, convertThisToObject, Assembler::EngineRegister); + generateRuntimeCall(_as, JITAssembler::Void, convertThisToObject, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(value); prepareCallData(args, 0); if (value->asConst()) - generateRuntimeCall(result, callValue, Assembler::EngineRegister, - Assembler::PointerToValue(value), + generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister, + PointerToValue(value), baseAddressForCallData()); else - generateRuntimeCall(result, callValue, Assembler::EngineRegister, - Assembler::Reference(value), + generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister, + Reference(value), baseAddressForCallData()); } -void InstructionSelection::loadThisObject(IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadThisObject(IR::Expr *temp) { - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - _as->loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(ExecutionContext::Data, callData)), Assembler::ScratchRegister); -#if defined(VALUE_FITS_IN_REGISTER) - _as->load64(Pointer(Assembler::ScratchRegister, qOffsetOf(CallData, thisObject)), - Assembler::ReturnValueRegister); - _as->storeReturnValue(temp); -#else - _as->copyValue(temp, Pointer(Assembler::ScratchRegister, qOffsetOf(CallData, thisObject))); -#endif + _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))); } -void InstructionSelection::loadQmlContext(IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadQmlContext(IR::Expr *temp) { - generateRuntimeCall(temp, getQmlContext, Assembler::EngineRegister); + generateRuntimeCall(_as, temp, getQmlContext, JITTargetPlatform::EngineRegister); } -void InstructionSelection::loadQmlImportedScripts(IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadQmlImportedScripts(IR::Expr *temp) { - generateRuntimeCall(temp, getQmlImportedScripts, Assembler::EngineRegister); + generateRuntimeCall(_as, temp, getQmlImportedScripts, JITTargetPlatform::EngineRegister); } -void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadQmlSingleton(const QString &name, IR::Expr *temp) { - generateRuntimeCall(temp, getQmlSingleton, Assembler::EngineRegister, Assembler::StringToIndex(name)); + generateRuntimeCall(_as, temp, getQmlSingleton, JITTargetPlatform::EngineRegister, StringToIndex(name)); } -void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadConst(IR::Const *sourceConst, IR::Expr *target) { if (IR::Temp *targetTemp = target->asTemp()) { if (targetTemp->kind == IR::Temp::PhysicalRegister) { if (targetTemp->type == IR::DoubleType) { Q_ASSERT(sourceConst->type == IR::DoubleType); - _as->toDoubleRegister(sourceConst, (Assembler::FPRegisterID) targetTemp->index); + _as->toDoubleRegister(sourceConst, (FPRegisterID) targetTemp->index); } else if (targetTemp->type == IR::SInt32Type) { Q_ASSERT(sourceConst->type == IR::SInt32Type); - _as->toInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); + _as->toInt32Register(sourceConst, (RegisterID) targetTemp->index); } else if (targetTemp->type == IR::UInt32Type) { Q_ASSERT(sourceConst->type == IR::UInt32Type); - _as->toUInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); + _as->toUInt32Register(sourceConst, (RegisterID) targetTemp->index); } else if (targetTemp->type == IR::BoolType) { Q_ASSERT(sourceConst->type == IR::BoolType); - _as->move(Assembler::TrustedImm32(convertToValue(sourceConst).int_32()), - (Assembler::RegisterID) targetTemp->index); + _as->move(TrustedImm32(convertToValue(sourceConst).int_32()), + (RegisterID) targetTemp->index); } else { Q_UNREACHABLE(); } @@ -500,147 +498,155 @@ void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target) _as->storeValue(convertToValue(sourceConst), target); } -void InstructionSelection::loadString(const QString &str, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadString(const QString &str, IR::Expr *target) { - Pointer srcAddr = _as->loadStringAddress(Assembler::ReturnValueRegister, str); - _as->loadPtr(srcAddr, Assembler::ReturnValueRegister); - Pointer destAddr = _as->loadAddress(Assembler::ScratchRegister, target); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - _as->store64(Assembler::ReturnValueRegister, destAddr); -#else - _as->store32(Assembler::ReturnValueRegister, destAddr); - destAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(QV4::Value::Managed_Type_Internal), destAddr); -#endif + 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); } -void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) { int id = registerRegExp(sourceRegexp); - generateRuntimeCall(target, regexpLiteral, Assembler::EngineRegister, Assembler::TrustedImm32(id)); + generateRuntimeCall(_as, target, regexpLiteral, JITTargetPlatform::EngineRegister, TrustedImm32(id)); } -void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getActivationProperty(const IR::Name *name, IR::Expr *target) { if (useFastLookups && name->global) { uint index = registerGlobalGetterLookup(*name->id); - generateLookupCall(target, index, qOffsetOf(QV4::Lookup, globalGetter), Assembler::EngineRegister, Assembler::Void); + generateLookupCall(target, index, qOffsetOf(QV4::Lookup, globalGetter), JITTargetPlatform::EngineRegister, JITAssembler::Void); return; } - generateRuntimeCall(target, getActivationProperty, Assembler::EngineRegister, Assembler::StringToIndex(*name->id)); + generateRuntimeCall(_as, target, getActivationProperty, JITTargetPlatform::EngineRegister, StringToIndex(*name->id)); } -void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setActivationProperty(IR::Expr *source, const QString &targetName) { // ### should use a lookup call here - generateRuntimeCall(Assembler::Void, setActivationProperty, - Assembler::EngineRegister, Assembler::StringToIndex(targetName), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setActivationProperty, + JITTargetPlatform::EngineRegister, StringToIndex(targetName), PointerToValue(source)); } -void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::initClosure(IR::Closure *closure, IR::Expr *target) { int id = closure->value; - generateRuntimeCall(target, closure, Assembler::EngineRegister, Assembler::TrustedImm32(id)); + generateRuntimeCall(_as, target, closure, JITTargetPlatform::EngineRegister, TrustedImm32(id)); } -void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getProperty(IR::Expr *base, const QString &name, IR::Expr *target) { if (useFastLookups) { uint index = registerGetterLookup(name); - generateLookupCall(target, index, qOffsetOf(QV4::Lookup, getter), Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::Void); + generateLookupCall(target, index, qOffsetOf(QV4::Lookup, getter), JITTargetPlatform::EngineRegister, PointerToValue(base), JITAssembler::Void); } else { - generateRuntimeCall(target, getProperty, Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, target, getProperty, JITTargetPlatform::EngineRegister, + PointerToValue(base), StringToIndex(name)); } } -void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) { if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(target, getQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index), Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired)); else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(target, getQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index), Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired)); else if (kind == IR::Member::MemberOfIdObjectsArray) - generateRuntimeCall(target, getQmlIdObject, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); + generateRuntimeCall(_as, target, getQmlIdObject, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index)); else Q_ASSERT(false); } -void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) { if (attachedPropertiesId != 0) - generateRuntimeCall(target, getQmlAttachedProperty, Assembler::EngineRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex)); + generateRuntimeCall(_as, target, getQmlAttachedProperty, JITTargetPlatform::EngineRegister, TrustedImm32(attachedPropertiesId), TrustedImm32(propertyIndex)); else if (isSingleton) - generateRuntimeCall(target, getQmlSingletonQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), - Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlSingletonQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex), + TrustedImm32(captureRequired)); else - generateRuntimeCall(target, getQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), - Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex), + TrustedImm32(captureRequired)); } -void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) { if (useFastLookups) { uint index = registerSetterLookup(targetName); - generateLookupCall(Assembler::Void, index, qOffsetOf(QV4::Lookup, setter), - Assembler::EngineRegister, - Assembler::PointerToValue(targetBase), - Assembler::PointerToValue(source)); + generateLookupCall(JITAssembler::Void, index, qOffsetOf(QV4::Lookup, setter), + JITTargetPlatform::EngineRegister, + PointerToValue(targetBase), + PointerToValue(source)); } else { - generateRuntimeCall(Assembler::Void, setProperty, Assembler::EngineRegister, - Assembler::PointerToValue(targetBase), Assembler::StringToIndex(targetName), - Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setProperty, JITTargetPlatform::EngineRegister, + PointerToValue(targetBase), StringToIndex(targetName), + PointerToValue(source)); } } -void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) { if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(Assembler::Void, setQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), - Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), + TrustedImm32(propertyIndex), PointerToValue(source)); else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(Assembler::Void, setQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), - Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), + TrustedImm32(propertyIndex), PointerToValue(source)); else Q_ASSERT(false); } -void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) { - generateRuntimeCall(Assembler::Void, setQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), - Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), + TrustedImm32(propertyIndex), PointerToValue(source)); } -void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) { if (useFastLookups) { uint lookup = registerIndexedGetterLookup(); generateLookupCall(target, lookup, qOffsetOf(QV4::Lookup, indexedGetter), - Assembler::PointerToValue(base), - Assembler::PointerToValue(index)); + PointerToValue(base), + PointerToValue(index)); return; } - generateRuntimeCall(target, getElement, Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + generateRuntimeCall(_as, target, getElement, JITTargetPlatform::EngineRegister, + PointerToValue(base), PointerToValue(index)); } -void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) { if (useFastLookups) { uint lookup = registerIndexedSetterLookup(); - generateLookupCall(Assembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter), - Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), - Assembler::PointerToValue(source)); + generateLookupCall(JITAssembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter), + PointerToValue(targetBase), PointerToValue(targetIndex), + PointerToValue(source)); return; } - generateRuntimeCall(Assembler::Void, setElement, Assembler::EngineRegister, - Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), - Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setElement, JITTargetPlatform::EngineRegister, + PointerToValue(targetBase), PointerToValue(targetIndex), + PointerToValue(source)); } -void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::copyValue(IR::Expr *source, IR::Expr *target) { IR::Temp *sourceTemp = source->asTemp(); IR::Temp *targetTemp = target->asTemp(); @@ -655,25 +661,25 @@ void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { if (sourceTemp->type == IR::DoubleType) - _as->moveDouble((Assembler::FPRegisterID) sourceTemp->index, - (Assembler::FPRegisterID) targetTemp->index); + _as->moveDouble((FPRegisterID) sourceTemp->index, + (FPRegisterID) targetTemp->index); else - _as->move((Assembler::RegisterID) sourceTemp->index, - (Assembler::RegisterID) targetTemp->index); + _as->move((RegisterID) sourceTemp->index, + (RegisterID) targetTemp->index); return; } else { switch (sourceTemp->type) { case IR::DoubleType: - _as->storeDouble((Assembler::FPRegisterID) sourceTemp->index, target); + _as->storeDouble((FPRegisterID) sourceTemp->index, target); break; case IR::SInt32Type: - _as->storeInt32((Assembler::RegisterID) sourceTemp->index, target); + _as->storeInt32((RegisterID) sourceTemp->index, target); break; case IR::UInt32Type: - _as->storeUInt32((Assembler::RegisterID) sourceTemp->index, target); + _as->storeUInt32((RegisterID) sourceTemp->index, target); break; case IR::BoolType: - _as->storeBool((Assembler::RegisterID) sourceTemp->index, target); + _as->storeBool((RegisterID) sourceTemp->index, target); break; default: Q_ASSERT(!"Unreachable"); @@ -685,19 +691,19 @@ void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) switch (targetTemp->type) { case IR::DoubleType: Q_ASSERT(source->type == IR::DoubleType); - _as->toDoubleRegister(source, (Assembler::FPRegisterID) targetTemp->index); + _as->toDoubleRegister(source, (FPRegisterID) targetTemp->index); return; case IR::BoolType: Q_ASSERT(source->type == IR::BoolType); - _as->toInt32Register(source, (Assembler::RegisterID) targetTemp->index); + _as->toInt32Register(source, (RegisterID) targetTemp->index); return; case IR::SInt32Type: Q_ASSERT(source->type == IR::SInt32Type); - _as->toInt32Register(source, (Assembler::RegisterID) targetTemp->index); + _as->toInt32Register(source, (RegisterID) targetTemp->index); return; case IR::UInt32Type: Q_ASSERT(source->type == IR::UInt32Type); - _as->toUInt32Register(source, (Assembler::RegisterID) targetTemp->index); + _as->toUInt32Register(source, (RegisterID) targetTemp->index); return; default: Q_ASSERT(!"Unreachable"); @@ -706,10 +712,11 @@ void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) } // The target is not a physical register, nor is the source. So we can do a memory-to-memory copy: - _as->memcopyValue(_as->loadAddress(Assembler::ReturnValueRegister, target), source, Assembler::ScratchRegister); + _as->memcopyValue(_as->loadAddress(JITTargetPlatform::ReturnValueRegister, target), source, JITTargetPlatform::ScratchRegister); } -void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr *target) { IR::Temp *sourceTemp = source->asTemp(); IR::Temp *targetTemp = target->asTemp(); @@ -719,26 +726,27 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) Q_ASSERT(sourceTemp->type == targetTemp->type); if (sourceTemp->type == IR::DoubleType) { - _as->moveDouble((Assembler::FPRegisterID) targetTemp->index, Assembler::FPGpr0); - _as->moveDouble((Assembler::FPRegisterID) sourceTemp->index, - (Assembler::FPRegisterID) targetTemp->index); - _as->moveDouble(Assembler::FPGpr0, (Assembler::FPRegisterID) sourceTemp->index); + _as->moveDouble((FPRegisterID) targetTemp->index, JITTargetPlatform::FPGpr0); + _as->moveDouble((FPRegisterID) sourceTemp->index, + (FPRegisterID) targetTemp->index); + _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) sourceTemp->index); } else { - _as->swap((Assembler::RegisterID) sourceTemp->index, - (Assembler::RegisterID) targetTemp->index); + _as->swap((RegisterID) sourceTemp->index, + (RegisterID) targetTemp->index); } return; } } 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. - Assembler::Pointer sAddr = _as->loadAddress(Assembler::ScratchRegister, source); - Assembler::Pointer tAddr = _as->loadAddress(Assembler::ReturnValueRegister, target); + 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 - _as->JSC::MacroAssembler::loadDouble(sAddr, Assembler::FPGpr0); - _as->JSC::MacroAssembler::loadDouble(tAddr, Assembler::FPGpr1); - _as->JSC::MacroAssembler::storeDouble(Assembler::FPGpr1, sAddr); - _as->JSC::MacroAssembler::storeDouble(Assembler::FPGpr0, tAddr); + 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); return; } } @@ -749,18 +757,18 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) Q_ASSERT(memExpr); Q_ASSERT(regTemp); - Assembler::Pointer addr = _as->loadAddress(Assembler::ReturnValueRegister, memExpr); + Pointer addr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, memExpr); if (regTemp->type == IR::DoubleType) { - _as->loadDouble(addr, Assembler::FPGpr0); - _as->storeDouble((Assembler::FPRegisterID) regTemp->index, addr); - _as->moveDouble(Assembler::FPGpr0, (Assembler::FPRegisterID) regTemp->index); + _as->loadDouble(addr, JITTargetPlatform::FPGpr0); + _as->storeDouble((FPRegisterID) regTemp->index, addr); + _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) regTemp->index); } else if (regTemp->type == IR::UInt32Type) { - _as->toUInt32Register(addr, Assembler::ScratchRegister); - _as->storeUInt32((Assembler::RegisterID) regTemp->index, addr); - _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) regTemp->index); + _as->toUInt32Register(addr, JITTargetPlatform::ScratchRegister); + _as->storeUInt32((RegisterID) regTemp->index, addr); + _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); } else { - _as->load32(addr, Assembler::ScratchRegister); - _as->store32((Assembler::RegisterID) regTemp->index, addr); + _as->load32(addr, JITTargetPlatform::ScratchRegister); + _as->store32((RegisterID) regTemp->index, addr); if (regTemp->type != memExpr->type) { addr.offset += 4; quint32 tag; @@ -775,55 +783,59 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) tag = 31337; // bogus value Q_UNREACHABLE(); } - _as->store32(Assembler::TrustedImm32(tag), addr); + _as->store32(TrustedImm32(tag), addr); } - _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) regTemp->index); + _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); } } #define setOp(op, opName, operation) \ do { \ - op = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + op = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) #define setOpContext(op, opName, operation) \ do { \ - opContext = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + opContext = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) -void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) { - QV4::JIT::Unop unop(_as, oper); + QV4::JIT::Unop<JITAssembler> unop(_as, oper); unop.generate(source, target); } -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { - QV4::JIT::Binop binop(_as, oper); + QV4::JIT::Binop<JITAssembler> binop(_as, oper); binop.generate(leftSource, rightSource, target); } -void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) { prepareCallData(args, base); if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(result, callQmlScopeObjectProperty, - Assembler::EngineRegister, - Assembler::TrustedImm32(propertyIndex), + generateRuntimeCall(_as, result, callQmlScopeObjectProperty, + JITTargetPlatform::EngineRegister, + TrustedImm32(propertyIndex), baseAddressForCallData()); else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(result, callQmlContextObjectProperty, - Assembler::EngineRegister, - Assembler::TrustedImm32(propertyIndex), + generateRuntimeCall(_as, result, callQmlContextObjectProperty, + JITTargetPlatform::EngineRegister, + TrustedImm32(propertyIndex), baseAddressForCallData()); else Q_ASSERT(false); } -void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(base != 0); @@ -832,29 +844,31 @@ void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR: if (useFastLookups) { uint index = registerGetterLookup(name); - generateRuntimeCall(result, callPropertyLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), + generateRuntimeCall(_as, result, callPropertyLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); } else { - generateRuntimeCall(result, callProperty, Assembler::EngineRegister, - Assembler::StringToIndex(name), + generateRuntimeCall(_as, result, callProperty, JITTargetPlatform::EngineRegister, + StringToIndex(name), baseAddressForCallData()); } } -void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(base != 0); prepareCallData(args, base); - generateRuntimeCall(result, callElement, Assembler::EngineRegister, - Assembler::PointerToValue(index), + generateRuntimeCall(_as, result, callElement, JITTargetPlatform::EngineRegister, + PointerToValue(index), baseAddressForCallData()); } -void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertType(IR::Expr *source, IR::Expr *target) { switch (target->type) { case IR::DoubleType: @@ -875,7 +889,8 @@ void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target) } } -void InstructionSelection::convertTypeSlowPath(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeSlowPath(IR::Expr *source, IR::Expr *target) { Q_ASSERT(target->type != IR::BoolType); @@ -885,7 +900,8 @@ void InstructionSelection::convertTypeSlowPath(IR::Expr *source, IR::Expr *targe copyValue(source, target); } -void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToDouble(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::SInt32Type: @@ -897,51 +913,37 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe convertUIntToDouble(source, target); break; case IR::UndefinedType: - _as->loadDouble(_as->loadAddress(Assembler::ScratchRegister, source), Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, target); + _as->loadDouble(_as->loadAddress(JITTargetPlatform::ScratchRegister, source), JITTargetPlatform::FPGpr0); + _as->storeDouble(JITTargetPlatform::FPGpr0, target); break; case IR::StringType: case IR::VarType: { // load the tag: - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, source); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ScratchRegister); + _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); // check if it's an int32: - Assembler::Jump isNoInt = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); + Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, + TrustedImm32(Value::Integer_Type_Internal)); convertIntToDouble(source, target); - Assembler::Jump intDone = _as->jump(); + Jump intDone = _as->jump(); // not an int, check if it's NOT a double: isNoInt.link(_as); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - _as->rshift32(Assembler::TrustedImm32(Value::IsDoubleTag_Shift), Assembler::ScratchRegister); - Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(0)); -#else - _as->and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister); - Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::NotDouble_Mask)); -#endif + Jump isDbl = _as->generateIsDoubleCheck(JITTargetPlatform::ScratchRegister); - generateRuntimeCall(target, toDouble, Assembler::PointerToValue(source)); - Assembler::Jump noDoubleDone = _as->jump(); + generateRuntimeCall(_as, target, toDouble, PointerToValue(source)); + Jump noDoubleDone = _as->jump(); // it is a double: isDbl.link(_as); - Assembler::Pointer addr2 = _as->loadAddress(Assembler::ScratchRegister, source); + Pointer addr2 = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); IR::Temp *targetTemp = target->asTemp(); if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { -#if Q_PROCESSOR_WORDSIZE == 8 - _as->load64(addr2, Assembler::ScratchRegister); - _as->store64(Assembler::ScratchRegister, _as->loadAddress(Assembler::ReturnValueRegister, target)); -#else - _as->loadDouble(addr2, Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(Assembler::ReturnValueRegister, target)); -#endif + _as->memcopyValue(target, addr2, JITTargetPlatform::FPGpr0, JITTargetPlatform::ReturnValueRegister); } else { - _as->loadDouble(addr2, (Assembler::FPRegisterID) targetTemp->index); + _as->loadDouble(addr2, (FPRegisterID) targetTemp->index); } noDoubleDone.link(_as); @@ -953,7 +955,8 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe } } -void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToBool(IR::Expr *source, IR::Expr *target) { IR::Temp *sourceTemp = source->asTemp(); switch (source->type) { @@ -965,16 +968,16 @@ void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) // The source is in a register if the register allocator is used. If the register // allocator was not used, then that means that we can use any register for to // load the double into. - Assembler::FPRegisterID reg; + FPRegisterID reg; if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) - reg = (Assembler::FPRegisterID) sourceTemp->index; + reg = (FPRegisterID) sourceTemp->index; else - reg = _as->toDoubleRegister(source, (Assembler::FPRegisterID) 1); - Assembler::Jump nonZero = _as->branchDoubleNonZero(reg, Assembler::FPGpr0); + reg = _as->toDoubleRegister(source, (FPRegisterID) 1); + Jump nonZero = _as->branchDoubleNonZero(reg, JITTargetPlatform::FPGpr0); // it's 0, so false: _as->storeBool(false, target); - Assembler::Jump done = _as->jump(); + Jump done = _as->jump(); // it's non-zero, so true: nonZero.link(_as); @@ -988,283 +991,220 @@ void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) _as->storeBool(false, target); break; case IR::StringType: - generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean, - Assembler::PointerToValue(source)); - _as->storeBool(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, + PointerToValue(source)); + _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); case IR::VarType: default: - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - Assembler::Pointer tagAddr = addr; + Pointer addr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + Pointer tagAddr = addr; tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ReturnValueRegister); + _as->load32(tagAddr, JITTargetPlatform::ReturnValueRegister); // checkif it's a bool: - Assembler::Jump notBool = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(Value::Boolean_Type_Internal)); - _as->load32(addr, Assembler::ReturnValueRegister); - Assembler::Jump boolDone = _as->jump(); + Jump notBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, + TrustedImm32(Value::Boolean_Type_Internal)); + _as->load32(addr, JITTargetPlatform::ReturnValueRegister); + Jump boolDone = _as->jump(); // check if it's an int32: notBool.link(_as); - Assembler::Jump fallback = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); - _as->load32(addr, Assembler::ReturnValueRegister); - Assembler::Jump isZero = _as->branch32(Assembler::Equal, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(0)); - _as->move(Assembler::TrustedImm32(1), Assembler::ReturnValueRegister); - Assembler::Jump intDone = _as->jump(); + Jump fallback = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, + TrustedImm32(Value::Integer_Type_Internal)); + _as->load32(addr, JITTargetPlatform::ReturnValueRegister); + Jump isZero = _as->branch32(RelationalCondition::Equal, JITTargetPlatform::ReturnValueRegister, + TrustedImm32(0)); + _as->move(TrustedImm32(1), JITTargetPlatform::ReturnValueRegister); + Jump intDone = _as->jump(); // not an int: fallback.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean, - Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, + PointerToValue(source)); isZero.link(_as); intDone.link(_as); boolDone.link(_as); - _as->storeBool(Assembler::ReturnValueRegister, target); + _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); break; } } -void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToSInt32(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::VarType: { - -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - _as->load64(addr, Assembler::ScratchRegister); - _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - - // check if it's integer convertible - _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), Assembler::ScratchRegister); - Assembler::Jump isIntConvertible = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(3)); - - // nope, not integer convertible, so check for a double: - _as->urshift64(Assembler::TrustedImm32( - QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift), - Assembler::ScratchRegister); - Assembler::Jump fallback = _as->branch32(Assembler::GreaterThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); - - // it's a double - _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister); - _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - _as->move64ToDouble(Assembler::ReturnValueRegister, Assembler::FPGpr0); - Assembler::Jump success = - _as->branchTruncateDoubleToInt32(Assembler::FPGpr0, Assembler::ReturnValueRegister, - Assembler::BranchIfTruncateSuccessful); - - // not an int: - fallback.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - - isIntConvertible.link(_as); - success.link(_as); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - Assembler::Pointer targetAddr = _as->loadAddress(Assembler::ScratchRegister, target); - _as->store32(Assembler::ReturnValueRegister, targetAddr); - targetAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(Value::Integer_Type_Internal), targetAddr); - } else { - _as->storeInt32(Assembler::ReturnValueRegister, target); - } -#else - // load the tag: - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - Assembler::Pointer tagAddr = addr; - tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ReturnValueRegister); - - // check if it's an int32: - Assembler::Jump fallback = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - _as->load32(addr, Assembler::ReturnValueRegister); - Assembler::Pointer targetAddr = _as->loadAddress(Assembler::ScratchRegister, target); - _as->store32(Assembler::ReturnValueRegister, targetAddr); - targetAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(Value::Integer_Type_Internal), targetAddr); - } else { - _as->load32(addr, (Assembler::RegisterID) targetTemp->index); - } - Assembler::Jump intDone = _as->jump(); - - // not an int: - fallback.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - _as->storeInt32(Assembler::ReturnValueRegister, target); - - intDone.link(_as); -#endif - + JITAssembler::RegisterSizeDependentOps::convertVarToSInt32(_as, source, target); } break; case IR::DoubleType: { - Assembler::Jump success = + Jump success = _as->branchTruncateDoubleToInt32(_as->toDoubleRegister(source), - Assembler::ReturnValueRegister, - Assembler::BranchIfTruncateSuccessful); - generateRuntimeCall(Assembler::ReturnValueRegister, doubleToInt, - Assembler::PointerToValue(source)); + JITTargetPlatform::ReturnValueRegister, + BranchTruncateType::BranchIfTruncateSuccessful); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToInt, + PointerToValue(source)); success.link(_as); - _as->storeInt32(Assembler::ReturnValueRegister, target); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); } break; case IR::UInt32Type: - _as->storeInt32(_as->toUInt32Register(source, Assembler::ReturnValueRegister), target); + _as->storeInt32(_as->toUInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); break; case IR::NullType: case IR::UndefinedType: - _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister); - _as->storeInt32(Assembler::ReturnValueRegister, target); + _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); break; case IR::BoolType: - _as->storeInt32(_as->toInt32Register(source, Assembler::ReturnValueRegister), target); + _as->storeInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); break; case IR::StringType: default: - generateRuntimeCall(Assembler::ReturnValueRegister, toInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - _as->storeInt32(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toInt, + _as->loadAddress(JITTargetPlatform::ScratchRegister, source)); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); break; } // switch (source->type) } -void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToUInt32(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::VarType: { // load the tag: - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, source); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ScratchRegister); + _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); // check if it's an int32: - Assembler::Jump isNoInt = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - _as->storeUInt32(_as->toInt32Register(addr, Assembler::ScratchRegister), target); - Assembler::Jump intDone = _as->jump(); + Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, + TrustedImm32(Value::Integer_Type_Internal)); + Pointer addr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + _as->storeUInt32(_as->toInt32Register(addr, JITTargetPlatform::ScratchRegister), target); + Jump intDone = _as->jump(); // not an int: isNoInt.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toUInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - _as->storeInt32(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, + _as->loadAddress(JITTargetPlatform::ScratchRegister, source)); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); intDone.link(_as); } break; case IR::DoubleType: { - Assembler::FPRegisterID reg = _as->toDoubleRegister(source); - Assembler::Jump success = - _as->branchTruncateDoubleToUint32(reg, Assembler::ReturnValueRegister, - Assembler::BranchIfTruncateSuccessful); - generateRuntimeCall(Assembler::ReturnValueRegister, doubleToUInt, - Assembler::PointerToValue(source)); + FPRegisterID reg = _as->toDoubleRegister(source); + Jump success = + _as->branchTruncateDoubleToUint32(reg, JITTargetPlatform::ReturnValueRegister, + BranchTruncateType::BranchIfTruncateSuccessful); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToUInt, + PointerToValue(source)); success.link(_as); - _as->storeUInt32(Assembler::ReturnValueRegister, target); + _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); } break; case IR::NullType: case IR::UndefinedType: - _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister); - _as->storeUInt32(Assembler::ReturnValueRegister, target); + _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); + _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); break; case IR::StringType: - generateRuntimeCall(Assembler::ReturnValueRegister, toUInt, - Assembler::PointerToValue(source)); - _as->storeUInt32(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, + PointerToValue(source)); + _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); break; case IR::SInt32Type: case IR::BoolType: - _as->storeUInt32(_as->toInt32Register(source, Assembler::ReturnValueRegister), target); + _as->storeUInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); break; default: break; } // switch (source->type) } -void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(func != 0); prepareCallData(args, 0); if (useFastLookups && func->global) { uint index = registerGlobalGetterLookup(*func->id); - generateRuntimeCall(result, constructGlobalLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), baseAddressForCallData()); + generateRuntimeCall(_as, result, constructGlobalLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); return; } - generateRuntimeCall(result, constructActivationProperty, - Assembler::EngineRegister, - Assembler::StringToIndex(*func->id), + generateRuntimeCall(_as, result, constructActivationProperty, + JITTargetPlatform::EngineRegister, + StringToIndex(*func->id), baseAddressForCallData()); } -void InstructionSelection::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { prepareCallData(args, base); if (useFastLookups) { uint index = registerGetterLookup(name); - generateRuntimeCall(result, constructPropertyLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), + generateRuntimeCall(_as, result, constructPropertyLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); return; } - generateRuntimeCall(result, constructProperty, Assembler::EngineRegister, - Assembler::StringToIndex(name), + generateRuntimeCall(_as, result, constructProperty, JITTargetPlatform::EngineRegister, + StringToIndex(name), baseAddressForCallData()); } -void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(value != 0); prepareCallData(args, 0); - generateRuntimeCall(result, constructValue, - Assembler::EngineRegister, - Assembler::Reference(value), + generateRuntimeCall(_as, result, constructValue, + JITTargetPlatform::EngineRegister, + Reference(value), baseAddressForCallData()); } -void InstructionSelection::visitJump(IR::Jump *s) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitJump(IR::Jump *s) { if (!_removableJumps.at(_block->index())) _as->jumpToBlock(_block, s->target); } -void InstructionSelection::visitCJump(IR::CJump *s) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitCJump(IR::CJump *s) { IR::Temp *t = s->cond->asTemp(); if (t || s->cond->asArgLocal()) { - Assembler::RegisterID reg; + RegisterID reg; if (t && t->kind == IR::Temp::PhysicalRegister) { Q_ASSERT(t->type == IR::BoolType); - reg = (Assembler::RegisterID) t->index; + reg = (RegisterID) t->index; } else if (t && t->kind == IR::Temp::StackSlot && t->type == IR::BoolType) { - reg = Assembler::ReturnValueRegister; + reg = JITTargetPlatform::ReturnValueRegister; _as->toInt32Register(t, reg); } else { - Address temp = _as->loadAddress(Assembler::ScratchRegister, s->cond); + Address temp = _as->loadAddress(JITTargetPlatform::ScratchRegister, s->cond); Address tag = temp; tag.offset += QV4::Value::tagOffset(); - Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal)); + Jump booleanConversion = _as->branch32(RelationalCondition::NotEqual, tag, TrustedImm32(QV4::Value::Boolean_Type_Internal)); Address data = temp; data.offset += QV4::Value::valueOffset(); - _as->load32(data, Assembler::ReturnValueRegister); - Assembler::Jump testBoolean = _as->jump(); + _as->load32(data, JITTargetPlatform::ReturnValueRegister); + Jump testBoolean = _as->jump(); booleanConversion.link(_as); - reg = Assembler::ReturnValueRegister; - generateRuntimeCall(reg, toBoolean, Assembler::Reference(s->cond)); + reg = JITTargetPlatform::ReturnValueRegister; + generateRuntimeCall(_as, reg, toBoolean, Reference(s->cond)); testBoolean.link(_as); } @@ -1274,9 +1214,9 @@ void InstructionSelection::visitCJump(IR::CJump *s) } else if (IR::Const *c = s->cond->asConst()) { // TODO: SSA optimization for constant condition evaluation should remove this. // See also visitCJump() in RegAllocInfo. - generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean, - Assembler::PointerToValue(c)); - _as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, s->iftrue, s->iffalse); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, + PointerToValue(c)); + _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse); return; } else if (IR::Binop *b = s->cond->asBinop()) { if (b->left->type == IR::DoubleType && b->right->type == IR::DoubleType @@ -1296,8 +1236,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) return; } - RuntimeCall op; - RuntimeCall opContext; + typename JITAssembler::RuntimeCall op; + typename JITAssembler::RuntimeCall opContext; const char *opName = 0; bool needsExceptionCheck; switch (b->op) { @@ -1321,165 +1261,30 @@ void InstructionSelection::visitCJump(IR::CJump *s) // elimination (which isn't there either) would remove the whole else block. if (opContext.isValid()) _as->generateFunctionCallImp(needsExceptionCheck, - Assembler::ReturnValueRegister, opName, opContext, - Assembler::EngineRegister, - Assembler::PointerToValue(b->left), - Assembler::PointerToValue(b->right)); + JITTargetPlatform::ReturnValueRegister, opName, opContext, + JITTargetPlatform::EngineRegister, + PointerToValue(b->left), + PointerToValue(b->right)); else _as->generateFunctionCallImp(needsExceptionCheck, - Assembler::ReturnValueRegister, opName, op, - Assembler::PointerToValue(b->left), - Assembler::PointerToValue(b->right)); + JITTargetPlatform::ReturnValueRegister, opName, op, + PointerToValue(b->left), + PointerToValue(b->right)); - _as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, s->iftrue, s->iffalse); + _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse); return; } Q_UNREACHABLE(); } -void InstructionSelection::visitRet(IR::Ret *s) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitRet(IR::Ret *s) { - if (!s) { - // this only happens if the method doesn't have a return statement and can - // only exit through an exception - } else if (IR::Temp *t = s->expr->asTemp()) { -#if CPU(X86) || CPU(ARM) || CPU(MIPS) - -# if CPU(X86) - Assembler::RegisterID lowReg = JSC::X86Registers::eax; - Assembler::RegisterID highReg = JSC::X86Registers::edx; -# elif CPU(MIPS) - Assembler::RegisterID lowReg = JSC::MIPSRegisters::v0; - Assembler::RegisterID highReg = JSC::MIPSRegisters::v1; -# else // CPU(ARM) - Assembler::RegisterID lowReg = JSC::ARMRegisters::r0; - Assembler::RegisterID highReg = JSC::ARMRegisters::r1; -# endif - - if (t->kind == IR::Temp::PhysicalRegister) { - switch (t->type) { - case IR::DoubleType: - _as->moveDoubleToInts((Assembler::FPRegisterID) t->index, lowReg, highReg); - break; - case IR::UInt32Type: { - Assembler::RegisterID srcReg = (Assembler::RegisterID) t->index; - Assembler::Jump intRange = _as->branch32(Assembler::GreaterThanOrEqual, srcReg, Assembler::TrustedImm32(0)); - _as->convertUInt32ToDouble(srcReg, Assembler::FPGpr0, Assembler::ReturnValueRegister); - _as->moveDoubleToInts(Assembler::FPGpr0, lowReg, highReg); - Assembler::Jump done = _as->jump(); - intRange.link(_as); - _as->move(srcReg, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); - done.link(_as); - } break; - case IR::SInt32Type: - _as->move((Assembler::RegisterID) t->index, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); - break; - case IR::BoolType: - _as->move((Assembler::RegisterID) t->index, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal), highReg); - break; - default: - Q_UNREACHABLE(); - } - } else { - Pointer addr = _as->loadAddress(Assembler::ScratchRegister, t); - _as->load32(addr, lowReg); - addr.offset += 4; - _as->load32(addr, highReg); - } -#else - if (t->kind == IR::Temp::PhysicalRegister) { - if (t->type == IR::DoubleType) { - _as->moveDoubleTo64((Assembler::FPRegisterID) t->index, - Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), - Assembler::ScratchRegister); - _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - } else if (t->type == IR::UInt32Type) { - Assembler::RegisterID srcReg = (Assembler::RegisterID) t->index; - Assembler::Jump intRange = _as->branch32(Assembler::GreaterThanOrEqual, srcReg, Assembler::TrustedImm32(0)); - _as->convertUInt32ToDouble(srcReg, Assembler::FPGpr0, Assembler::ReturnValueRegister); - _as->moveDoubleTo64(Assembler::FPGpr0, Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister); - _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - Assembler::Jump done = _as->jump(); - intRange.link(_as); - _as->zeroExtend32ToPtr(srcReg, Assembler::ReturnValueRegister); - quint64 tag = QV4::Value::Integer_Type_Internal; - _as->or64(Assembler::TrustedImm64(tag << 32), - Assembler::ReturnValueRegister); - done.link(_as); - } else { - _as->zeroExtend32ToPtr((Assembler::RegisterID) t->index, Assembler::ReturnValueRegister); - quint64 tag; - switch (t->type) { - case IR::SInt32Type: - tag = QV4::Value::Integer_Type_Internal; - break; - case IR::BoolType: - tag = QV4::Value::Boolean_Type_Internal; - break; - default: - tag = 31337; // bogus value - Q_UNREACHABLE(); - } - _as->or64(Assembler::TrustedImm64(tag << 32), - Assembler::ReturnValueRegister); - } - } else { - _as->copyValue(Assembler::ReturnValueRegister, t); - } -#endif - } else if (IR::Const *c = s->expr->asConst()) { - QV4::Primitive retVal = convertToValue(c); -#if CPU(X86) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::X86Registers::eax); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::X86Registers::edx); -#elif CPU(ARM) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::ARMRegisters::r0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::ARMRegisters::r1); -#elif CPU(MIPS) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::MIPSRegisters::v0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::MIPSRegisters::v1); -#else - _as->move(Assembler::TrustedImm64(retVal.rawValue()), Assembler::ReturnValueRegister); -#endif - } else { - Q_UNREACHABLE(); - Q_UNUSED(s); - } - - Assembler::Label leaveStackFrame = _as->label(); - - const int locals = _as->stackLayout().calculateJSStackFrameSize(); - _as->subPtr(Assembler::TrustedImm32(sizeof(QV4::Value)*locals), Assembler::LocalsRegister); - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - _as->loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(ExecutionContext::Data, engine)), Assembler::ScratchRegister); - _as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop))); - - _as->leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave); - _as->ret(); - - _as->exceptionReturnLabel = _as->label(); - QV4::Primitive retVal = Primitive::undefinedValue(); -#if CPU(X86) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::X86Registers::eax); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::X86Registers::edx); -#elif CPU(ARM) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::ARMRegisters::r0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::ARMRegisters::r1); -#elif CPU(MIPS) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::MIPSRegisters::v0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::MIPSRegisters::v1); -#else - _as->move(Assembler::TrustedImm64(retVal.rawValue()), Assembler::ReturnValueRegister); -#endif - _as->jump(leaveStackFrame); + _as->returnFromFunction(s, regularRegistersToSave, fpRegistersToSave); } -int InstructionSelection::prepareVariableArguments(IR::ExprList* args) +template <typename JITAssembler> +int InstructionSelection<JITAssembler>::prepareVariableArguments(IR::ExprList* args) { int argc = 0; for (IR::ExprList *it = args; it; it = it->next) { @@ -1492,7 +1297,7 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) Q_ASSERT(arg != 0); Pointer dst(_as->stackLayout().argumentAddressForCall(i)); if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), Assembler::ScratchRegister); + _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister); else _as->copyValue(dst, arg); } @@ -1500,7 +1305,8 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) return argc; } -int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObject) +template <typename JITAssembler> +int InstructionSelection<JITAssembler>::prepareCallData(IR::ExprList* args, IR::Expr *thisObject) { int argc = 0; for (IR::ExprList *it = args; it; it = it->next) { @@ -1508,9 +1314,9 @@ int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObje } Pointer p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, tag)); - _as->store32(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), p); + _as->store32(TrustedImm32(QV4::Value::Integer_Type_Internal), p); p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, argc)); - _as->store32(Assembler::TrustedImm32(argc), p); + _as->store32(TrustedImm32(argc), p); p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, thisObject)); if (!thisObject) _as->storeValue(QV4::Primitive::undefinedValue(), p); @@ -1523,25 +1329,24 @@ int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObje Q_ASSERT(arg != 0); Pointer dst(_as->stackLayout().argumentAddressForCall(i)); if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), Assembler::ScratchRegister); + _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister); else _as->copyValue(dst, arg); } return argc; } -void InstructionSelection::calculateRegistersToSave(const RegisterInformation &used) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::calculateRegistersToSave(const RegisterInformation &used) { regularRegistersToSave.clear(); fpRegistersToSave.clear(); - for (const RegisterInfo &ri : Assembler::getRegisterInfo()) { -#if defined(RESTORE_EBX_ON_CALL) - if (ri.isRegularRegister() && ri.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) { + for (const RegisterInfo &ri : JITTargetPlatform::getRegisterInfo()) { + if (JITTargetPlatform::gotRegister != -1 && ri.isRegularRegister() && ri.reg<RegisterID>() == JITTargetPlatform::gotRegister) { regularRegistersToSave.append(ri); continue; } -#endif // RESTORE_EBX_ON_CALL if (ri.isCallerSaved()) continue; if (ri.isRegularRegister()) { @@ -1564,35 +1369,38 @@ bool operator==(const Primitive &v1, const Primitive &v2) } // QV4 namespace QT_END_NAMESPACE -bool InstructionSelection::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) { if (_as->nextBlock() == iftrue) { - Assembler::Jump target = _as->branchDouble(true, op, left, right); + Jump target = _as->branchDouble(true, op, left, right); _as->addPatch(iffalse, target); } else { - Assembler::Jump target = _as->branchDouble(false, op, left, right); + Jump target = _as->branchDouble(false, op, left, right); _as->addPatch(iftrue, target); _as->jumpToBlock(_block, iffalse); } return true; } -bool InstructionSelection::visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) { if (_as->nextBlock() == iftrue) { - Assembler::Jump target = _as->branchInt32(true, op, left, right); + Jump target = _as->branchInt32(true, op, left, right); _as->addPatch(iffalse, target); } else { - Assembler::Jump target = _as->branchInt32(false, op, left, right); + Jump target = _as->branchInt32(false, op, left, right); _as->addPatch(iftrue, target); _as->jumpToBlock(_block, iffalse); } return true; } -void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { Q_ASSERT(binop->op == IR::OpStrictEqual || binop->op == IR::OpStrictNotEqual); @@ -1607,15 +1415,16 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr IR::Expr *left = binop->left; IR::Expr *right = binop->right; - generateRuntimeCall(Assembler::ReturnValueRegister, compareStrictEqual, - Assembler::PointerToValue(left), Assembler::PointerToValue(right)); - _as->generateCJumpOnCompare(binop->op == IR::OpStrictEqual ? Assembler::NotEqual : Assembler::Equal, - Assembler::ReturnValueRegister, Assembler::TrustedImm32(0), + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, compareStrictEqual, + PointerToValue(left), PointerToValue(right)); + _as->generateCJumpOnCompare(binop->op == IR::OpStrictEqual ? RelationalCondition::NotEqual : RelationalCondition::Equal, + JITTargetPlatform::ReturnValueRegister, TrustedImm32(0), _block, trueBlock, falseBlock); } // Only load the non-null temp. -bool InstructionSelection::visitCJumpStrictNull(IR::Binop *binop, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpStrictNull(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { @@ -1640,19 +1449,20 @@ bool InstructionSelection::visitCJumpStrictNull(IR::Binop *binop, return true; } - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, varSrc); tagAddr.offset += 4; - const Assembler::RegisterID tagReg = Assembler::ScratchRegister; + const RegisterID tagReg = JITTargetPlatform::ScratchRegister; _as->load32(tagAddr, tagReg); - Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal - : Assembler::NotEqual; - const Assembler::TrustedImm32 tag(QV4::Value::Null_Type_Internal); + RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal + : RelationalCondition::NotEqual; + const TrustedImm32 tag(QV4::Value::Null_Type_Internal); _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); return true; } -bool InstructionSelection::visitCJumpStrictUndefined(IR::Binop *binop, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpStrictUndefined(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { @@ -1677,28 +1487,15 @@ bool InstructionSelection::visitCJumpStrictUndefined(IR::Binop *binop, return true; } - Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal - : Assembler::NotEqual; - const Assembler::RegisterID tagReg = Assembler::ReturnValueRegister; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, varSrc); - _as->load64(addr, tagReg); - const Assembler::TrustedImm64 tag(0); -#else // !QV4_USE_64_BIT_VALUE_ENCODING - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); - _as->load32(tagAddr, tagReg); - Assembler::Jump j = _as->branch32(Assembler::invert(cond), tagReg, Assembler::TrustedImm32(0)); - _as->addPatch(falseBlock, j); - - tagAddr.offset += 4; - _as->load32(tagAddr, tagReg); - const Assembler::TrustedImm32 tag(QV4::Value::Managed_Type_Internal); -#endif - _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); + RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal + : RelationalCondition::NotEqual; + const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister; + _as->generateCJumpOnUndefined(cond, varSrc, JITTargetPlatform::ScratchRegister, tagReg, _block, trueBlock, falseBlock); return true; } -bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { IR::Expr *boolSrc = 0, *otherSrc = 0; @@ -1712,13 +1509,20 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock // neither operands are statically typed as bool, so bail out. return false; } + if (otherSrc->type == IR::UnknownType) { + // Ok, we really need to call into the runtime. + // (This case doesn't happen when the optimizer ran, because everything will be typed (yes, + // possibly as "var" meaning anything), but it does happen for $0===true, which is generated + // for things where the optimizer didn't run (like functions with a try block).) + return false; + } - Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal - : Assembler::NotEqual; + RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal + : RelationalCondition::NotEqual; if (otherSrc->type == IR::BoolType) { // both are boolean - Assembler::RegisterID one = _as->toBoolRegister(boolSrc, Assembler::ReturnValueRegister); - Assembler::RegisterID two = _as->toBoolRegister(otherSrc, Assembler::ScratchRegister); + RegisterID one = _as->toBoolRegister(boolSrc, JITTargetPlatform::ReturnValueRegister); + RegisterID two = _as->toBoolRegister(otherSrc, JITTargetPlatform::ScratchRegister); _as->generateCJumpOnCompare(cond, one, two, _block, trueBlock, falseBlock); return true; } @@ -1728,13 +1532,13 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock return true; } - Assembler::Pointer otherAddr = _as->loadAddress(Assembler::ReturnValueRegister, otherSrc); + Pointer otherAddr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, otherSrc); otherAddr.offset += 4; // tag address // check if the tag of the var operand is indicates 'boolean' - _as->load32(otherAddr, Assembler::ScratchRegister); - Assembler::Jump noBool = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal)); + _as->load32(otherAddr, JITTargetPlatform::ScratchRegister); + Jump noBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, + TrustedImm32(QV4::Value::Boolean_Type_Internal)); if (binop->op == IR::OpStrictEqual) _as->addPatch(falseBlock, noBool); else @@ -1742,14 +1546,15 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock // ok, both are boolean, so let's load them and compare them. otherAddr.offset -= 4; // int_32 address - _as->load32(otherAddr, Assembler::ReturnValueRegister); - Assembler::RegisterID boolReg = _as->toBoolRegister(boolSrc, Assembler::ScratchRegister); - _as->generateCJumpOnCompare(cond, boolReg, Assembler::ReturnValueRegister, _block, trueBlock, + _as->load32(otherAddr, JITTargetPlatform::ReturnValueRegister); + RegisterID boolReg = _as->toBoolRegister(boolSrc, JITTargetPlatform::ScratchRegister); + _as->generateCJumpOnCompare(cond, boolReg, JITTargetPlatform::ReturnValueRegister, _block, trueBlock, falseBlock); return true; } -bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { @@ -1776,18 +1581,18 @@ bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Bin return true; } - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, varSrc); tagAddr.offset += 4; - const Assembler::RegisterID tagReg = Assembler::ReturnValueRegister; + const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister; _as->load32(tagAddr, tagReg); if (binop->op == IR::OpNotEqual) qSwap(trueBlock, falseBlock); - Assembler::Jump isNull = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Null_Type_Internal))); - Assembler::Jump isNotUndefinedTag = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(int(QV4::Value::Managed_Type_Internal))); + Jump isNull = _as->branch32(RelationalCondition::Equal, tagReg, TrustedImm32(int(QV4::Value::Null_Type_Internal))); + Jump isNotUndefinedTag = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(int(QV4::Value::Managed_Type_Internal))); tagAddr.offset -= 4; _as->load32(tagAddr, tagReg); - Assembler::Jump isNotUndefinedValue = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(0)); + Jump isNotUndefinedValue = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(0)); _as->addPatch(trueBlock, isNull); _as->addPatch(falseBlock, isNotUndefinedTag); _as->addPatch(falseBlock, isNotUndefinedValue); @@ -1797,7 +1602,8 @@ bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Bin } -void InstructionSelection::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { Q_ASSERT(binop->op == IR::OpEqual || binop->op == IR::OpNotEqual); @@ -1808,18 +1614,55 @@ void InstructionSelection::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *tru IR::Expr *left = binop->left; IR::Expr *right = binop->right; - generateRuntimeCall(Assembler::ReturnValueRegister, compareEqual, - Assembler::PointerToValue(left), Assembler::PointerToValue(right)); - _as->generateCJumpOnCompare(binop->op == IR::OpEqual ? Assembler::NotEqual : Assembler::Equal, - Assembler::ReturnValueRegister, Assembler::TrustedImm32(0), + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, compareEqual, + PointerToValue(left), PointerToValue(right)); + _as->generateCJumpOnCompare(binop->op == IR::OpEqual ? RelationalCondition::NotEqual : RelationalCondition::Equal, + JITTargetPlatform::ReturnValueRegister, TrustedImm32(0), _block, trueBlock, falseBlock); } -QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading() +template <typename JITAssembler> +QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory<JITAssembler>::createUnitForLoading() { QQmlRefPointer<CompiledData::CompilationUnit> result; result.adopt(new JIT::CompilationUnit); return result; } +QT_BEGIN_NAMESPACE +namespace QV4 { namespace JIT { +template class Q_QML_EXPORT InstructionSelection<>; +template class Q_QML_EXPORT ISelFactory<>; +#if defined(V4_BOOTSTRAP) + +Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture) +{ + using ARMv7CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; + using ARM64CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>; + + if (architecture == QLatin1String("armv7")) + return new ISelFactory<ARMv7CrossAssembler>; + else if (architecture == QLatin1String("armv8")) + return new ISelFactory<ARM64CrossAssembler>; + + QString hostArch; +#if CPU(ARM_THUMB2) + hostArch = QStringLiteral("armv7"); +#elif CPU(MIPS) + hostArch = QStringLiteral("mips"); +#elif CPU(X86) + hostArch = QStringLiteral("x86"); +#elif CPU(X86_64) + hostArch = QStringLiteral("x86_64"); +#endif + if (!hostArch.isEmpty() && architecture == hostArch) + return new ISelFactory<>; + + return nullptr; +} + +#endif +} } +QT_END_NAMESPACE + #endif // ENABLE(ASSEMBLER) diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 012745c5f2..5c046cb397 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -72,6 +72,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { +template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>> class Q_QML_EXPORT InstructionSelection: protected IR::IRDecoder, public EvalInstructionSelection @@ -136,18 +137,31 @@ protected: void unop(IR::AluOp oper, IR::Expr *sourceTemp, IR::Expr *target) override; void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) override; - typedef Assembler::Address Address; - typedef Assembler::Pointer Pointer; + using Address = typename JITAssembler::Address; + using Pointer = typename JITAssembler::Pointer; + using PointerToValue = typename JITAssembler::PointerToValue; + using RegisterID = typename JITAssembler::RegisterID; + using FPRegisterID = typename JITAssembler::FPRegisterID; + using ResultCondition = typename JITAssembler::ResultCondition; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + using TrustedImm64 = typename JITAssembler::TrustedImm64; + using Label = typename JITAssembler::Label; + using Jump = typename JITAssembler::Jump; + using StringToIndex = typename JITAssembler::StringToIndex; + using Reference = typename JITAssembler::Reference; + using RelationalCondition = typename JITAssembler::RelationalCondition; + using BranchTruncateType = typename JITAssembler::BranchTruncateType; + using RuntimeCall = typename JITAssembler::RuntimeCall; + + using JITTargetPlatform = typename JITAssembler::JITTargetPlatform; -#if !defined(ARGUMENTS_IN_REGISTERS) Address addressForArgument(int index) const { // 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(Assembler::FramePointerRegister, (index + 2) * sizeof(void*)); + return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * sizeof(void*)); } -#endif Pointer baseAddressForCallArguments() { @@ -192,61 +206,55 @@ private: if (targetTemp->kind == IR::Temp::PhysicalRegister) { if (IR::Temp *sourceTemp = source->asTemp()) { if (sourceTemp->kind == IR::Temp::PhysicalRegister) { - _as->convertInt32ToDouble((Assembler::RegisterID) sourceTemp->index, - (Assembler::FPRegisterID) targetTemp->index); + _as->convertInt32ToDouble((RegisterID) sourceTemp->index, + (FPRegisterID) targetTemp->index); } else { - _as->convertInt32ToDouble(_as->loadAddress(Assembler::ReturnValueRegister, sourceTemp), - (Assembler::FPRegisterID) targetTemp->index); + _as->convertInt32ToDouble(_as->loadAddress(JITTargetPlatform::ReturnValueRegister, sourceTemp), + (FPRegisterID) targetTemp->index); } } else { - _as->convertInt32ToDouble(_as->toInt32Register(source, Assembler::ScratchRegister), - (Assembler::FPRegisterID) targetTemp->index); + _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), + (FPRegisterID) targetTemp->index); } return; } } - _as->convertInt32ToDouble(_as->toInt32Register(source, Assembler::ScratchRegister), - Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(Assembler::ReturnValueRegister, target)); + _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), + JITTargetPlatform::FPGpr0); + _as->storeDouble(JITTargetPlatform::FPGpr0, _as->loadAddress(JITTargetPlatform::ReturnValueRegister, target)); } void convertUIntToDouble(IR::Expr *source, IR::Expr *target) { - Assembler::RegisterID tmpReg = Assembler::ScratchRegister; - Assembler::RegisterID reg = _as->toInt32Register(source, tmpReg); + RegisterID tmpReg = JITTargetPlatform::ScratchRegister; + RegisterID reg = _as->toInt32Register(source, tmpReg); if (IR::Temp *targetTemp = target->asTemp()) { if (targetTemp->kind == IR::Temp::PhysicalRegister) { - _as->convertUInt32ToDouble(reg, (Assembler::FPRegisterID) targetTemp->index, tmpReg); + _as->convertUInt32ToDouble(reg, (FPRegisterID) targetTemp->index, tmpReg); return; } } _as->convertUInt32ToDouble(_as->toUInt32Register(source, tmpReg), - Assembler::FPGpr0, tmpReg); - _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(tmpReg, target)); + JITTargetPlatform::FPGpr0, tmpReg); + _as->storeDouble(JITTargetPlatform::FPGpr0, _as->loadAddress(tmpReg, target)); } void convertIntToBool(IR::Expr *source, IR::Expr *target) { - Assembler::RegisterID reg = Assembler::ScratchRegister; + RegisterID reg = JITTargetPlatform::ScratchRegister; if (IR::Temp *targetTemp = target->asTemp()) if (targetTemp->kind == IR::Temp::PhysicalRegister) - reg = (Assembler::RegisterID) targetTemp->index; + reg = (RegisterID) targetTemp->index; _as->move(_as->toInt32Register(source, reg), reg); - _as->compare32(Assembler::NotEqual, reg, Assembler::TrustedImm32(0), reg); + _as->compare32(RelationalCondition::NotEqual, reg, TrustedImm32(0), reg); _as->storeBool(reg, target); } - #define isel_stringIfyx(s) #s - #define isel_stringIfy(s) isel_stringIfyx(s) - - #define generateRuntimeCall(t, function, ...) \ - _as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) - int prepareVariableArguments(IR::ExprList* args); int prepareCallData(IR::ExprList* args, IR::Expr *thisObject); @@ -259,22 +267,22 @@ private: // goes into the same register as the return value (currently only ARM), the prepareCall // will combine loading the looupAddr into the register and calculating the indirect call // address. - Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup)); + Pointer lookupAddr(JITTargetPlatform::ReturnValueRegister, index * sizeof(QV4::Lookup)); _as->generateFunctionCallImp(true, retval, "lookup getter/setter", - LookupCall(lookupAddr, getterSetterOffset), lookupAddr, + typename JITAssembler::LookupCall(lookupAddr, getterSetterOffset), lookupAddr, arg1, arg2, arg3); } template <typename Retval, typename Arg1, typename Arg2> void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2) { - generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, Assembler::VoidType()); + generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, typename JITAssembler::VoidType()); } IR::BasicBlock *_block; BitVector _removableJumps; - Assembler* _as; + JITAssembler* _as; QScopedPointer<CompilationUnit> compilationUnit; QQmlEnginePrivate *qmlEngine; @@ -282,13 +290,14 @@ private: RegisterInformation fpRegistersToSave; }; +template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>> class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: ISelFactory() : EvalISelFactory(QStringLiteral("jit")) {} virtual ~ISelFactory() {} EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } + { return new InstructionSelection<JITAssembler>(qmlEngine, execAllocator, module, jsGenerator, this); } bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL { return true; } QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE Q_DECL_FINAL; diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index 3fb0815299..e5abaa7458 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -973,7 +973,15 @@ private: break; Q_ASSERT(!i->isFixedInterval()); - _liveIntervals.push_back(i); + auto it = _liveIntervals.begin(); + for (; it != _liveIntervals.end(); ++it) { + if ((*it)->temp() == i->temp()) { + *it = i; + break; + } + } + if (it == _liveIntervals.end()) + _liveIntervals.push_back(i); // qDebug() << "-- Activating interval for temp" << i->temp().index; _unprocessedReverseOrder.removeLast(); @@ -1521,7 +1529,7 @@ static inline int indexOfRangeCoveringPosition(const LifeTimeInterval::Ranges &r return -1; } -static inline int intersectionPosition(const LifeTimeInterval::Range &one, const LifeTimeInterval::Range &two) +static inline int intersectionPosition(const LifeTimeIntervalRange &one, const LifeTimeIntervalRange &two) { if (one.covers(two.start)) return two.start; @@ -1567,7 +1575,7 @@ static void longestAvailableReg(int *nextUses, int nextUseCount, int ®, int & #define CALLOC_ON_STACK(ty, ptr, sz, val) \ Q_ASSERT(sz > 0); \ - ty *ptr = reinterpret_cast<ty *>(alloca(sizeof(ty) * (sz))); \ + Q_ALLOCA_VAR(ty, ptr, sizeof(ty) * (sz)); \ for (ty *it = ptr, *eit = ptr + (sz); it != eit; ++it) \ *it = val; @@ -1779,9 +1787,9 @@ int RegisterAllocator::nextIntersection(const LifeTimeInterval ¤t, return -1; for (int currentEnd = currentRanges.size(); currentIt < currentEnd; ++currentIt) { - const LifeTimeInterval::Range currentRange = currentRanges.at(currentIt); + const LifeTimeIntervalRange currentRange = currentRanges.at(currentIt); for (int anotherIt = anotherItStart, anotherEnd = anotherRanges.size(); anotherIt < anotherEnd; ++anotherIt) { - const LifeTimeInterval::Range anotherRange = anotherRanges.at(anotherIt); + const LifeTimeIntervalRange anotherRange = anotherRanges.at(anotherIt); if (anotherRange.start > currentRange.end) break; int intersectPos = intersectionPosition(currentRange, anotherRange); diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h index 7e265258d5..fcc600eb2e 100644 --- a/src/qml/jit/qv4targetplatform_p.h +++ b/src/qml/jit/qv4targetplatform_p.h @@ -63,6 +63,11 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { +enum TargetOperatingSystemSpecialization { + NoOperatingSystemSpecialization, + WindowsSpecialization +}; + // The TargetPlatform class describes how the stack and the registers work on a CPU+ABI combination. // // All combinations have a separate definition, guarded by #ifdefs. The exceptions are: @@ -79,25 +84,38 @@ namespace JIT { // a call, we add a load it right before emitting the call instruction. // // NOTE: When adding new architecture, do not forget to whitelist it in qv4global_p.h! +template <typename PlatformAssembler, TargetOperatingSystemSpecialization specialization = NoOperatingSystemSpecialization> class TargetPlatform { -public: +}; + #if CPU(X86) && (OS(LINUX) || OS(WINDOWS) || OS(QNX) || OS(FREEBSD) || defined(Q_OS_IOS)) - enum { RegAllocIsSupported = 1 }; +template <> +class TargetPlatform<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerX86; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::edi; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::X86Registers::esi; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::ecx; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + enum { RegAllocIsSupported = 1 }; - static RegisterInformation getPlatformRegisterInfo() + static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::edi; + static const RegisterID EngineRegister = JSC::X86Registers::esi; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::ecx; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + static const RegisterID LowReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID HighReturnValueRegister = JSC::X86Registers::edx; + + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() << RI(JSC::X86Registers::edx, QStringLiteral("edx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::ebx, QStringLiteral("ebx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::edi, QStringLiteral("edi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) @@ -109,28 +127,31 @@ public: << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) ; + return info; } # define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 -# undef VALUE_FITS_IN_REGISTER static const int RegisterSize = 4; -# undef ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 0; - static JSC::MacroAssembler::RegisterID registerForArgument(int) { Q_UNREACHABLE(); } + static RegisterID registerForArgument(int) { Q_UNREACHABLE(); } static const int StackAlignment = 16; static const int StackShadowSpace = 0; static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(FramePointerRegister); } + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) + { + if (frameSize > 0) + as->add32(TrustedImm32(frameSize), StackPointerRegister); + as->pop(FramePointerRegister); + } #if OS(WINDOWS) || OS(QNX) || \ ((OS(LINUX) || OS(FREEBSD)) && (defined(__PIC__) || defined(__PIE__))) -#define RESTORE_EBX_ON_CALL - static JSC::MacroAssembler::Address ebxAddressOnStack() - { + static const int gotRegister = JSC::X86Registers::ebx; + static int savedGOTRegisterSlotOnStack() { static int ebxIdx = -1; if (ebxIdx == -1) { int calleeSaves = 0; @@ -146,34 +167,47 @@ public: Q_ASSERT(ebxIdx >= 0); ebxIdx += 1; } - return JSC::MacroAssembler::Address(FramePointerRegister, ebxIdx * -int(sizeof(void*))); + return ebxIdx * -int(sizeof(void*)); } +#else + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } #endif - -#endif // Windows on x86 +}; +#endif // x86 #if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X) || OS(FREEBSD) || OS(QNX) || defined(Q_OS_IOS)) +template <> +class TargetPlatform<JSC::MacroAssemblerX86_64, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerX86_64; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; + enum { RegAllocIsSupported = 1 }; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::r12; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::X86Registers::r14; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::r10; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::r12; + static const RegisterID EngineRegister = JSC::X86Registers::r14; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - static RegisterInformation getPlatformRegisterInfo() + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + // r11 is used as scratch register by the macro assembler << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) @@ -185,17 +219,16 @@ public: << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) ; + return info; } #define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 -#define VALUE_FITS_IN_REGISTER static const int RegisterSize = 8; -#define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 6; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::X86Registers::edi, JSC::X86Registers::esi, JSC::X86Registers::edx, @@ -210,51 +243,72 @@ public: static const int StackAlignment = 16; static const int StackShadowSpace = 0; static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(FramePointerRegister); } + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) + { + if (frameSize > 0) + as->add64(TrustedImm32(frameSize), StackPointerRegister); + as->pop(FramePointerRegister); + } + + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } +}; #endif // Linux/MacOS on x86_64 #if CPU(X86_64) && OS(WINDOWS) - // Register allocation is not (yet) supported on win64, because the ABI related stack handling - // is not completely implemented. Specifically, the saving of xmm registers, and the saving of - // incoming function parameters to the shadow space is missing. - enum { RegAllocIsSupported = 0 }; - - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::r12; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::X86Registers::r14; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::r10; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - - static RegisterInformation getPlatformRegisterInfo() +template <> +class TargetPlatform<JSC::MacroAssemblerX86_64, WindowsSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerX86_64; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; + + enum { RegAllocIsSupported = 1 }; + + static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::r12; + static const RegisterID EngineRegister = JSC::X86Registers::r14; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() - << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) - << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) - << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) + static RegisterInformation info = RegisterInformation() + << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + // r11 is used as scratch register by the macro assembler << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) ; + return info; } #define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 -#define VALUE_FITS_IN_REGISTER static const int RegisterSize = 8; -#define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 4; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::X86Registers::ecx, JSC::X86Registers::edx, JSC::X86Registers::r8, @@ -262,16 +316,34 @@ public: }; Q_ASSERT(index >= 0 && index < RegisterArgumentCount); return regs[index]; - }; + } static const int StackAlignment = 16; static const int StackShadowSpace = 32; static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(FramePointerRegister); } + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) + { + if (frameSize > 0) + as->add64(TrustedImm32(frameSize), StackPointerRegister); + as->pop(FramePointerRegister); + } + + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } +}; #endif // Windows on x86_64 -#if CPU(ARM) +#if CPU(ARM) || defined(V4_BOOTSTRAP) +template <> +class TargetPlatform<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerARMv7; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; + enum { RegAllocIsSupported = 1 }; // The AAPCS specifies that the platform ABI has to define the usage of r9. Known are: @@ -287,23 +359,25 @@ public: // 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) - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::ARMRegisters::r7; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARMRegisters::r11; + static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r11; #else // Thumbs down - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::ARMRegisters::r11; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARMRegisters::r7; + static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; #endif - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::ARMRegisters::r13; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::ARMRegisters::r5; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::ARMRegisters::r10; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::ARMRegisters::d1; - - static RegisterInformation getPlatformRegisterInfo() + static const RegisterID StackPointerRegister = JSC::ARMRegisters::r13; + static const RegisterID ScratchRegister = JSC::ARMRegisters::r5; + static const RegisterID EngineRegister = JSC::ARMRegisters::r10; + static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; + static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + static const FPRegisterID FPGpr1 = JSC::ARMRegisters::d1; + static const RegisterID LowReturnValueRegister = JSC::ARMRegisters::r0; + static const RegisterID HighReturnValueRegister = JSC::ARMRegisters::r1; + + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() << RI(JSC::ARMRegisters::r0, QStringLiteral("r0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) << RI(JSC::ARMRegisters::r1, QStringLiteral("r1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::ARMRegisters::r2, QStringLiteral("r2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) @@ -336,17 +410,16 @@ public: << RI(JSC::ARMRegisters::d14, QStringLiteral("d14"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::ARMRegisters::d15, QStringLiteral("d15"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) ; + return info; } #undef HAVE_ALU_OPS_WITH_MEM_OPERAND -#undef VALUE_FITS_IN_REGISTER static const int RegisterSize = 4; -#define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 4; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::ARMRegisters::r0, JSC::ARMRegisters::r1, JSC::ARMRegisters::r2, @@ -361,35 +434,54 @@ public: static const int StackShadowSpace = 0; static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(JSC::ARMRegisters::lr); as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) { + if (frameSize > 0) { + // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't + // work well for large immediates. + as->move(TrustedImm32(frameSize), JSC::ARMRegisters::r3); + as->add32(JSC::ARMRegisters::r3, StackPointerRegister); + } as->pop(FramePointerRegister); as->pop(JSC::ARMRegisters::lr); } + + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } +}; #endif // ARM (32 bit) -#if CPU(ARM64) +#if CPU(ARM64) || defined(V4_BOOTSTRAP) +template <> +class TargetPlatform<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerARM64; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; + enum { RegAllocIsSupported = 1 }; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::ARM64Registers::fp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARM64Registers::x28; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::ARM64Registers::sp; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::ARM64Registers::x9; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::ARM64Registers::x27; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::ARM64Registers::x0; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::ARM64Registers::q0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::ARM64Registers::q1; + static const RegisterID FramePointerRegister = JSC::ARM64Registers::fp; + static const RegisterID LocalsRegister = JSC::ARM64Registers::x28; + static const RegisterID StackPointerRegister = JSC::ARM64Registers::sp; + static const RegisterID ScratchRegister = JSC::ARM64Registers::x9; + static const RegisterID EngineRegister = JSC::ARM64Registers::x27; + static const RegisterID ReturnValueRegister = JSC::ARM64Registers::x0; + static const FPRegisterID FPGpr0 = JSC::ARM64Registers::q0; + static const FPRegisterID FPGpr1 = JSC::ARM64Registers::q1; - static RegisterInformation getPlatformRegisterInfo() + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() << RI(JSC::ARM64Registers::x0, QStringLiteral("x0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) << RI(JSC::ARM64Registers::x1, QStringLiteral("x1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::ARM64Registers::x2, QStringLiteral("x2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) @@ -447,17 +539,16 @@ public: << RI(JSC::ARM64Registers::q30, QStringLiteral("q30"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::ARM64Registers::q31, QStringLiteral("q31"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) ; + return info; } #undef HAVE_ALU_OPS_WITH_MEM_OPERAND -#define VALUE_FITS_IN_REGISTER static const int RegisterSize = 8; -#define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 8; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::ARM64Registers::x0, JSC::ARM64Registers::x1, JSC::ARM64Registers::x2, @@ -476,33 +567,49 @@ public: static const int StackShadowSpace = 0; static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->pushPair(FramePointerRegister, JSC::ARM64Registers::lr); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) { + if (frameSize > 0) + as->add64(TrustedImm32(frameSize), StackPointerRegister); as->popPair(FramePointerRegister, JSC::ARM64Registers::lr); } + + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } +}; #endif // ARM64 #if defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) +template <> +class TargetPlatform<JSC::MacroAssemblerMIPS, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerMIPS; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; enum { RegAllocIsSupported = 1 }; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::MIPSRegisters::fp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::MIPSRegisters::sp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::MIPSRegisters::s0; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::MIPSRegisters::s1; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::MIPSRegisters::v0; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::MIPSRegisters::s2; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::MIPSRegisters::f0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::MIPSRegisters::f2; - - static RegisterInformation getPlatformRegisterInfo() + static const RegisterID FramePointerRegister = JSC::MIPSRegisters::fp; + static const RegisterID StackPointerRegister = JSC::MIPSRegisters::sp; + static const RegisterID LocalsRegister = JSC::MIPSRegisters::s0; + static const RegisterID EngineRegister = JSC::MIPSRegisters::s1; + static const RegisterID ReturnValueRegister = JSC::MIPSRegisters::v0; + static const RegisterID ScratchRegister = JSC::MIPSRegisters::s2; + static const FPRegisterID FPGpr0 = JSC::MIPSRegisters::f0; + static const FPRegisterID FPGpr1 = JSC::MIPSRegisters::f2; + static const RegisterID LowReturnValueRegister = JSC::MIPSRegisters::v0; + static const RegisterID HighReturnValueRegister = JSC::MIPSRegisters::v1; + + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() // Note: t0, t1, t2, t3 and f16 are already used by MacroAssemblerMIPS. << RI(JSC::MIPSRegisters::t4, QStringLiteral("t4"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::MIPSRegisters::t5, QStringLiteral("t5"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) @@ -524,17 +631,16 @@ public: << RI(JSC::MIPSRegisters::f26, QStringLiteral("f26"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::MIPSRegisters::f28, QStringLiteral("f28"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) ; + return info; } #undef HAVE_ALU_OPS_WITH_MEM_OPERAND -#undef VALUE_FITS_IN_REGISTER static const int RegisterSize = 4; -#define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 4; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::MIPSRegisters::a0, JSC::MIPSRegisters::a1, JSC::MIPSRegisters::a2, @@ -549,27 +655,25 @@ public: static const int StackShadowSpace = 4 * RegisterSize; // Stack space for 4 argument registers. static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(JSC::MIPSRegisters::ra); as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) { + if (frameSize > 0) + as->add32(TrustedImm32(frameSize), StackPointerRegister); as->pop(FramePointerRegister); as->pop(JSC::MIPSRegisters::ra); } -#endif // Linux on MIPS (32 bit) -public: // utility functions - static const RegisterInformation getRegisterInfo() - { - static const RegisterInformation info = getPlatformRegisterInfo(); - return info; - } + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } }; +#endif // Linux on MIPS (32 bit) } // JIT namespace } // QV4 namespace diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp index 799103849b..76c6457d67 100644 --- a/src/qml/jit/qv4unop.cpp +++ b/src/qml/jit/qv4unop.cpp @@ -48,14 +48,15 @@ using namespace JIT; #define stringIfy(s) stringIfyx(s) #define setOp(operation) \ do { \ - call = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); \ + call = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); \ needsExceptionCheck = Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) -void Unop::generate(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generate(IR::Expr *source, IR::Expr *target) { bool needsExceptionCheck; - RuntimeCall call; + typename JITAssembler::RuntimeCall call; const char *name = 0; switch (op) { case IR::OpNot: @@ -75,17 +76,18 @@ void Unop::generate(IR::Expr *source, IR::Expr *target) } // switch Q_ASSERT(call.isValid()); - _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, Assembler::PointerToValue(source)); + _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, PointerToValue(source)); } -void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generateUMinus(IR::Expr *source, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::SInt32Type) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - Assembler::RegisterID sReg = _as->toInt32Register(source, tReg); + tReg = (typename JITAssembler::RegisterID) targetTemp->index; + typename JITAssembler::RegisterID sReg = _as->toInt32Register(source, tReg); _as->move(sReg, tReg); _as->neg32(tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) @@ -93,26 +95,27 @@ void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) return; } - generateRuntimeCall(target, uMinus, Assembler::PointerToValue(source)); + generateRuntimeCall(_as, target, uMinus, PointerToValue(source)); } -void Unop::generateNot(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generateNot(IR::Expr *source, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::BoolType) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - _as->xor32(Assembler::TrustedImm32(0x1), _as->toInt32Register(source, tReg), tReg); + tReg = (typename JITAssembler::RegisterID) targetTemp->index; + _as->xor32(TrustedImm32(0x1), _as->toInt32Register(source, tReg), tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) _as->storeBool(tReg, target); return; } else if (source->type == IR::SInt32Type) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - _as->compare32(Assembler::Equal, - _as->toInt32Register(source, Assembler::ScratchRegister), Assembler::TrustedImm32(0), + tReg = (typename JITAssembler::RegisterID) targetTemp->index; + _as->compare32(RelationalCondition::Equal, + _as->toInt32Register(source, JITAssembler::ScratchRegister), TrustedImm32(0), tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) _as->storeBool(tReg, target); @@ -122,22 +125,33 @@ void Unop::generateNot(IR::Expr *source, IR::Expr *target) } // ## generic implementation testing for int/bool - generateRuntimeCall(target, uNot, Assembler::PointerToValue(source)); + generateRuntimeCall(_as, target, uNot, PointerToValue(source)); } -void Unop::generateCompl(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generateCompl(IR::Expr *source, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::SInt32Type) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - _as->xor32(Assembler::TrustedImm32(0xffffffff), _as->toInt32Register(source, tReg), tReg); + tReg = (typename JITAssembler::RegisterID) targetTemp->index; + _as->xor32(TrustedImm32(0xffffffff), _as->toInt32Register(source, tReg), tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) _as->storeInt32(tReg, target); return; } - generateRuntimeCall(target, complement, Assembler::PointerToValue(source)); + generateRuntimeCall(_as, target, complement, PointerToValue(source)); } +template struct QV4::JIT::Unop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>; +#if defined(V4_BOOTSTRAP) +#if !CPU(ARM_THUMB2) +template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>; +#endif +#if !CPU(ARM64) +template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>; +#endif +#endif + #endif diff --git a/src/qml/jit/qv4unop_p.h b/src/qml/jit/qv4unop_p.h index 1141a84913..fb68f80eec 100644 --- a/src/qml/jit/qv4unop_p.h +++ b/src/qml/jit/qv4unop_p.h @@ -60,21 +60,25 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { -class Assembler; - +template <typename JITAssembler> struct Unop { - Unop(Assembler *assembler, IR::AluOp operation) + Unop(JITAssembler *assembler, IR::AluOp operation) : _as(assembler) , op(operation) {} + using RelationalCondition = typename JITAssembler::RelationalCondition; + using PointerToValue = typename JITAssembler::PointerToValue; + using RuntimeCall = typename JITAssembler::RuntimeCall; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + void generate(IR::Expr *source, IR::Expr *target); void generateUMinus(IR::Expr *source, IR::Expr *target); void generateNot(IR::Expr *source, IR::Expr *target); void generateCompl(IR::Expr *source, IR::Expr *target); - Assembler *_as; + JITAssembler *_as; IR::AluOp op; }; diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 919524d1ed..955cf585e4 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -35,7 +35,6 @@ SOURCES += \ $$PWD/qv4regexp.cpp \ $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ - $$PWD/qv4executableallocator.cpp \ $$PWD/qv4sequenceobject.cpp \ $$PWD/qv4include.cpp \ $$PWD/qv4qobjectwrapper.cpp \ @@ -113,7 +112,8 @@ HEADERS += \ SOURCES += \ $$PWD/qv4runtime.cpp \ $$PWD/qv4string.cpp \ - $$PWD/qv4value.cpp + $$PWD/qv4value.cpp \ + $$PWD/qv4executableallocator.cpp valgrind { DEFINES += V4_USE_VALGRIND diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 5a190d6690..7c1cc92a13 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -88,10 +88,12 @@ void ArgumentsObject::fullyCreate() Scope scope(engine()); Scoped<MemberData> md(scope, d()->mappedArguments); - d()->mappedArguments = md->allocate(engine(), numAccessors); - for (uint i = 0; i < numAccessors; ++i) { - d()->mappedArguments->data[i] = context()->callData->args[i]; - arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor); + if (numAccessors) { + d()->mappedArguments = md->allocate(engine(), numAccessors); + for (uint i = 0; i < numAccessors; ++i) { + d()->mappedArguments->data[i] = context()->callData->args[i]; + arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor); + } } arrayPut(numAccessors, context()->callData->args + numAccessors, argCount - numAccessors); for (uint i = numAccessors; i < argCount; ++i) @@ -164,18 +166,17 @@ ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *ha return Encode::undefined(); } -void ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value) +bool ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value) { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->callData->argc)) args->fullyCreate(); - if (args->fullyCreated()) { - Object::putIndexed(m, index, value); - return; - } + if (args->fullyCreated()) + return Object::putIndexed(m, index, value); args->context()->callData->args[index] = value; + return true; } bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index) diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 0a2ea3b42a..f80ade9611 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -128,7 +128,7 @@ struct ArgumentsObject: Object { bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void putIndexed(Managed *m, uint index, const Value &value); + 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); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 60b90e4bf0..740ebbe359 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -155,7 +155,7 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) ctx = ctx->d()->outer; } - if (activation->hasProperty(name)) + if (activation->hasOwnProperty(name)) return; ScopedProperty desc(scope); PropertyAttributes attrs(Attr_Data); diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index b90c335b1c..c56d007028 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -340,7 +340,9 @@ static inline double TimeClip(double t) { if (! qt_is_finite(t) || fabs(t) > 8.64e15) return qt_qnan(); - return Primitive::toInteger(t); + + // +0 looks weird, but is correct. See ES6 20.3.1.15. We must not return -0. + return Primitive::toInteger(t) + 0; } static inline double ParseString(const QString &s) @@ -724,7 +726,7 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(7)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(7)); LocalTZA = getLocalTZA(); ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1); @@ -774,8 +776,21 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("setYear"), method_setYear, 1); defineDefaultProperty(QStringLiteral("setFullYear"), method_setFullYear, 3); defineDefaultProperty(QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); - defineDefaultProperty(QStringLiteral("toUTCString"), method_toUTCString, 0); - defineDefaultProperty(QStringLiteral("toGMTString"), method_toUTCString, 0); + + // ES6: B.2.4.3 & 20.3.4.43: + // We have to use the *same object* for toUTCString and toGMTString + { + QString toUtcString(QStringLiteral("toUTCString")); + QString toGmtString(QStringLiteral("toGMTString")); + ScopedString us(scope, engine->newIdentifier(toUtcString)); + ScopedString gs(scope, engine->newIdentifier(toGmtString)); + ExecutionContext *global = engine->rootContext(); + ScopedFunctionObject toUtcGmtStringFn(scope, BuiltinFunction::create(global, us, method_toUTCString)); + toUtcGmtStringFn->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + defineDefaultProperty(us, toUtcGmtStringFn); + defineDefaultProperty(gs, toUtcGmtStringFn); + } + defineDefaultProperty(QStringLiteral("toISOString"), method_toISOString, 0); defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1); } @@ -1025,6 +1040,7 @@ void DatePrototype::method_setTime(const BuiltinFunction *, Scope &scope, CallDa THROW_TYPE_ERROR(); double t = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); self->setDate(TimeClip(t)); scope.result = Encode(self->date()); } @@ -1036,7 +1052,9 @@ void DatePrototype::method_setMilliseconds(const BuiltinFunction *, Scope &scope THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); scope.result = Encode(self->date()); } @@ -1048,7 +1066,9 @@ void DatePrototype::method_setUTCMilliseconds(const BuiltinFunction *, Scope &sc THROW_TYPE_ERROR(); double t = self->date(); + CHECK_EXCEPTION(); double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); scope.result = Encode(self->date()); } @@ -1060,8 +1080,11 @@ void DatePrototype::method_setSeconds(const BuiltinFunction *, Scope &scope, Cal THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1088,9 +1111,13 @@ void DatePrototype::method_setMinutes(const BuiltinFunction *, Scope &scope, Cal THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double min = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double sec = (callData->argc < 2) ? SecFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); double ms = (callData->argc < 3) ? msFromTime(t) : callData->args[2].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1118,10 +1145,15 @@ void DatePrototype::method_setHours(const BuiltinFunction *, Scope &scope, CallD THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double hour = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double min = (callData->argc < 2) ? MinFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); double sec = (callData->argc < 3) ? SecFromTime(t) : callData->args[2].toNumber(); + CHECK_EXCEPTION(); double ms = (callData->argc < 4) ? msFromTime(t) : callData->args[3].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1150,7 +1182,9 @@ void DatePrototype::method_setDate(const BuiltinFunction *, Scope &scope, CallDa THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1163,7 +1197,9 @@ void DatePrototype::method_setUTCDate(const BuiltinFunction *, Scope &scope, Cal THROW_TYPE_ERROR(); double t = self->date(); + CHECK_EXCEPTION(); double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))); self->setDate(t); scope.result = Encode(self->date()); @@ -1176,8 +1212,11 @@ void DatePrototype::method_setMonth(const BuiltinFunction *, Scope &scope, CallD THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double month = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1245,11 +1284,15 @@ void DatePrototype::method_setFullYear(const BuiltinFunction *, Scope &scope, Ca THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); if (std::isnan(t)) t = 0; double year = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double month = (callData->argc < 2) ? MonthFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); double date = (callData->argc < 3) ? DateFromTime(t) : callData->args[2].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); self->setDate(t); scope.result = Encode(self->date()); diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index a56d17f9b1..b32b2c3f66 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -112,7 +112,7 @@ struct DateCtor: FunctionObject static void call(const Managed *that, Scope &scope, CallData *); }; -struct DatePrototype: DateObject +struct DatePrototype: Object { void init(ExecutionEngine *engine, Object *ctor); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index a11f7f0875..39b433e5f9 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -91,7 +91,9 @@ #if USE(PTHREADS) # include <pthread.h> +#if !defined(Q_OS_INTEGRITY) # include <sys/resource.h> +#endif #if HAVE(PTHREAD_NP_H) # include <pthread_np.h> #endif @@ -168,7 +170,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) if (forceMoth) { factory = new Moth::ISelFactory; } else { - factory = new JIT::ISelFactory; + factory = new JIT::ISelFactory<>; jitDisabled = false; } #else // !V4_ENABLE_JIT @@ -256,6 +258,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) arrayClass = emptyClass->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(arrayClass, objectPrototype()); + jsObjects[PropertyListProto] = memoryManager->allocObject<PropertyListPrototype>(); InternalClass *argsClass = emptyClass->addMember(id_length(), Attr_NotEnumerable); argumentsObjectClass = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); @@ -358,6 +361,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor()); static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor()); static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor()); + static_cast<PropertyListPrototype *>(propertyListPrototype())->init(this); static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor()); static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor()); static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor()); @@ -400,7 +404,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor()); globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor()); - globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberCtor()); + FunctionObject *numberObject = numberCtor(); + globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberObject); globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor()); globalObject->defineDefaultProperty(QStringLiteral("Array"), *arrayCtor()); globalObject->defineDefaultProperty(QStringLiteral("Function"), *functionCtor()); @@ -430,8 +435,26 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) jsObjects[Eval_Function] = memoryManager->allocObject<EvalFunction>(global); globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction()); - globalObject->defineDefaultProperty(QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); - globalObject->defineDefaultProperty(QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); + // ES6: 20.1.2.12 & 20.1.2.13: + // parseInt and parseFloat must be the same FunctionObject on the global & + // Number object. + { + QString piString(QStringLiteral("parseInt")); + QString pfString(QStringLiteral("parseFloat")); + Scope scope(this); + ScopedString pi(scope, newIdentifier(piString)); + ScopedString pf(scope, newIdentifier(pfString)); + ExecutionContext *global = rootContext(); + ScopedFunctionObject parseIntFn(scope, BuiltinFunction::create(global, pi, GlobalFunctions::method_parseInt)); + ScopedFunctionObject parseFloatFn(scope, BuiltinFunction::create(global, pf, GlobalFunctions::method_parseFloat)); + parseIntFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(2)); + parseFloatFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(1)); + globalObject->defineDefaultProperty(piString, parseIntFn); + globalObject->defineDefaultProperty(pfString, parseFloatFn); + numberObject->defineDefaultProperty(piString, parseIntFn); + numberObject->defineDefaultProperty(pfString, parseFloatFn); + } + globalObject->defineDefaultProperty(QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); globalObject->defineDefaultProperty(QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); globalObject->defineDefaultProperty(QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); @@ -1109,7 +1132,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (typeHint == qMetaTypeId<QJSValue>()) return QVariant::fromValue(QJSValue(e, value.asReturnedValue())); - if (value.as<Object>()) { + if (value.as<QV4::Object>()) { QV4::ScopedObject object(scope, value); if (typeHint == QMetaType::QJsonObject && !value.as<ArrayObject>() && !value.as<FunctionObject>()) { @@ -1755,7 +1778,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) return false; } -static bool convertToNativeQObject(QV4::ExecutionEngine *e, const Value &value, const QByteArray &targetType, void **result) +static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value, const QByteArray &targetType, void **result) { if (!targetType.endsWith('*')) return false; @@ -1770,7 +1793,7 @@ static bool convertToNativeQObject(QV4::ExecutionEngine *e, const Value &value, return false; } -static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const Value &value) +static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value) { if (!value.isObject()) return 0; diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 1c20ad30aa..69aa389c44 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -157,6 +157,7 @@ public: IntegerNull, // Has to come after the RootContext to make the context stack safe ObjectProto, ArrayProto, + PropertyListProto, StringProto, NumberProto, BooleanProto, @@ -225,6 +226,7 @@ public: Object *objectPrototype() const { return reinterpret_cast<Object *>(jsObjects + ObjectProto); } Object *arrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayProto); } + Object *propertyListPrototype() const { return reinterpret_cast<Object *>(jsObjects + PropertyListProto); } Object *stringPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringProto); } Object *numberPrototype() const { return reinterpret_cast<Object *>(jsObjects + NumberProto); } Object *booleanPrototype() const { return reinterpret_cast<Object *>(jsObjects + BooleanProto); } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index b2d89220ea..e9431ed25e 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -131,7 +131,7 @@ void FunctionObject::init(String *n, bool createProto) } if (n) - defineReadonlyProperty(s.engine->id_name(), *n); + defineReadonlyConfigurableProperty(s.engine->id_name(), *n); } ReturnedValue FunctionObject::name() const @@ -258,10 +258,10 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(0)); + defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("apply"), method_apply, 2); @@ -557,7 +557,7 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject len -= boundArgs->size(); if (len < 0) len = 0; - f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(len)); + f->defineReadonlyConfigurableProperty(s.engine->id_length(), Primitive::fromInt32(len)); ScopedProperty pd(s); pd->value = s.engine->thrower(); diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 66861bf697..b0d14fc2b4 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -258,6 +258,7 @@ enum PropertyFlag { Attr_NotEnumerable = 0x4, Attr_NotConfigurable = 0x8, Attr_ReadOnly = Attr_NotWritable|Attr_NotEnumerable|Attr_NotConfigurable, + Attr_ReadOnly_ButConfigurable = Attr_NotWritable|Attr_NotEnumerable, Attr_Invalid = 0xff }; diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index dcda949c97..1d8ef4b0fb 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -234,7 +234,7 @@ struct InternalClassTransition { return id == other.id && flags == other.flags; } bool operator<(const InternalClassTransition &other) const - { return id < other.id; } + { return id < other.id || (id == other.id && flags < other.flags); } }; struct InternalClass : public QQmlJS::Managed { diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 1d571f53f3..0f021c8bd0 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -705,7 +705,7 @@ QString Stringify::Str(const QString &key, const Value &v) if (replacerFunction) { ScopedObject holder(scope, v4->newObject()); - holder->put(scope.engine, QString(), scope.result); + holder->put(scope.engine->id_empty(), scope.result); ScopedCallData callData(scope, 2); callData->args[0] = v4->newString(key); callData->args[1] = scope.result; diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 52f54e25f5..c5ee92fedd 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -54,8 +54,11 @@ #include "qv4runtime_p.h" #include "qv4engine_p.h" #include "qv4context_p.h" + +#if !defined(V4_BOOTSTRAP) #include "qv4object_p.h" #include "qv4internalclass_p.h" +#endif QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp index f2a24f8179..db45c77472 100644 --- a/src/qml/jsruntime/qv4memberdata.cpp +++ b/src/qml/jsruntime/qv4memberdata.cpp @@ -55,6 +55,7 @@ void MemberData::markObjects(Heap::Base *that, ExecutionEngine *e) Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberData *old) { Q_ASSERT(!old || old->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); diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 09644c161d..8cfa930888 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -102,6 +102,8 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) ctor->defineReadonlyProperty(QStringLiteral("POSITIVE_INFINITY"), Primitive::fromDouble(qInf())); ctor->defineReadonlyProperty(QStringLiteral("MAX_VALUE"), Primitive::fromDouble(1.7976931348623158e+308)); ctor->defineReadonlyProperty(QStringLiteral("EPSILON"), Primitive::fromDouble(std::numeric_limits<double>::epsilon())); + ctor->defineReadonlyProperty(QStringLiteral("MAX_SAFE_INTEGER"), Primitive::fromDouble(9007199254740991)); + ctor->defineReadonlyProperty(QStringLiteral("MIN_SAFE_INTEGER"), Primitive::fromDouble(-9007199254740991)); QT_WARNING_PUSH QT_WARNING_DISABLE_INTEL(239) @@ -109,15 +111,17 @@ QT_WARNING_DISABLE_INTEL(239) QT_WARNING_POP ctor->defineDefaultProperty(QStringLiteral("isFinite"), method_isFinite, 1); + ctor->defineDefaultProperty(QStringLiteral("isInteger"), method_isInteger, 1); + ctor->defineDefaultProperty(QStringLiteral("isSafeInteger"), method_isSafeInteger, 1); ctor->defineDefaultProperty(QStringLiteral("isNaN"), method_isNaN, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString(), method_toString); + defineDefaultProperty(engine->id_toString(), method_toString, 1); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); defineDefaultProperty(engine->id_valueOf(), method_valueOf); defineDefaultProperty(QStringLiteral("toFixed"), method_toFixed, 1); - defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential); - defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision); + defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential, 1); + defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision, 1); } inline ReturnedValue thisNumberValue(Scope &scope, CallData *callData) @@ -155,6 +159,52 @@ void NumberPrototype::method_isFinite(const BuiltinFunction *, Scope &scope, Cal scope.result = Encode(!std::isnan(v) && !qt_is_inf(v)); } +void NumberPrototype::method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + if (!callData->argc) { + scope.result = Encode(false); + return; + } + + const Value &v = callData->args[0]; + if (!v.isNumber()) { + scope.result = Encode(false); + return; + } + + double dv = v.toNumber(); + if (std::isnan(dv) || qt_is_inf(dv)) { + scope.result = Encode(false); + return; + } + + double iv = v.toInteger(); + scope.result = Encode(dv == iv); +} + +void NumberPrototype::method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + if (!callData->argc) { + scope.result = Encode(false); + return; + } + + const Value &v = callData->args[0]; + if (!v.isNumber()) { + scope.result = Encode(false); + return; + } + + double dv = v.toNumber(); + if (std::isnan(dv) || qt_is_inf(dv)) { + scope.result = Encode(false); + return; + } + + double iv = v.toInteger(); + scope.result = Encode(dv == iv && std::fabs(iv) <= (2^53)-1); +} + void NumberPrototype::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData) { if (!callData->argc) { diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 364b866a16..e18267c50c 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -88,6 +88,8 @@ struct NumberPrototype: NumberObject void init(ExecutionEngine *engine, Object *ctor); static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 5d6c479477..dd3bbccde3 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -61,7 +61,8 @@ DEFINE_OBJECT_VTABLE(Object); void Object::setInternalClass(InternalClass *ic) { d()->internalClass = ic; - if (!d()->memberData || (d()->memberData->size < ic->size)) + 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); } @@ -92,13 +93,6 @@ bool Object::setPrototype(Object *proto) return true; } -void Object::put(ExecutionEngine *engine, const QString &name, const Value &value) -{ - Scope scope(engine); - ScopedString n(scope, engine->newString(name)); - put(n, value); -} - ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs) { if (!attrs.isAccessor()) @@ -114,11 +108,11 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property return scope.result.asReturnedValue(); } -void Object::putValue(uint memberIndex, const Value &value) +bool Object::putValue(uint memberIndex, const Value &value) { QV4::InternalClass *ic = internalClass(); if (ic->engine->hasException) - return; + return false; PropertyAttributes attrs = ic->propertyData[memberIndex]; @@ -131,7 +125,7 @@ void Object::putValue(uint memberIndex, const Value &value) callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); - return; + return !ic->engine->hasException; } goto reject; } @@ -140,11 +134,12 @@ void Object::putValue(uint memberIndex, const Value &value) goto reject; *propertyData(memberIndex) = value; - return; + return true; reject: if (engine()->current->strictMode) engine()->throwTypeError(); + return false; } void Object::defineDefaultProperty(const QString &name, const Value &value) @@ -162,7 +157,7 @@ void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(Ca ScopedString s(scope, e->newIdentifier(name)); ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(s, function); } @@ -173,7 +168,7 @@ void Object::defineDefaultProperty(const QString &name, void (*code)(const Built ScopedString s(scope, e->newIdentifier(name)); ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(s, function); } @@ -183,7 +178,7 @@ void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallConte Scope scope(e); ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(name, function); } @@ -193,7 +188,7 @@ void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunct Scope scope(e); ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(name, function); } @@ -250,6 +245,19 @@ void Object::defineReadonlyProperty(String *name, const Value &value) insertMember(name, value, Attr_ReadOnly); } +void Object::defineReadonlyConfigurableProperty(const QString &name, const Value &value) +{ + QV4::ExecutionEngine *e = engine(); + Scope scope(e); + ScopedString s(scope, e->newIdentifier(name)); + defineReadonlyConfigurableProperty(s, value); +} + +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); @@ -440,14 +448,14 @@ ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty); } -void Object::put(Managed *m, String *name, const Value &value) +bool Object::put(Managed *m, String *name, const Value &value) { - static_cast<Object *>(m)->internalPut(name, value); + return static_cast<Object *>(m)->internalPut(name, value); } -void Object::putIndexed(Managed *m, uint index, const Value &value) +bool Object::putIndexed(Managed *m, uint index, const Value &value) { - static_cast<Object *>(m)->internalPutIndexed(index, value); + return static_cast<Object *>(m)->internalPutIndexed(index, value); } PropertyAttributes Object::query(const Managed *m, String *name) @@ -705,10 +713,10 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const // Section 8.12.5 -void Object::internalPut(String *name, const Value &value) +bool Object::internalPut(String *name, const Value &value) { if (internalClass()->engine->hasException) - return; + return false; uint idx = name->asArrayIndex(); if (idx != UINT_MAX) @@ -737,7 +745,7 @@ void Object::internalPut(String *name, const Value &value) uint l = value.asArrayLength(&ok); if (!ok) { engine()->throwRangeError(value); - return; + return false; } ok = setArrayLength(l); if (!ok) @@ -745,7 +753,7 @@ void Object::internalPut(String *name, const Value &value) } else { *v = value; } - return; + return true; } else if (!prototype()) { if (!isExtensible()) goto reject; @@ -776,24 +784,26 @@ void Object::internalPut(String *name, const Value &value) callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); - return; + return !internalClass()->engine->hasException; } insertMember(name, value); - return; + return true; reject: + // ### this should be removed once everything is ported to use Object::set() if (engine()->current->strictMode) { QString message = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); engine()->throwTypeError(message); } + return false; } -void Object::internalPutIndexed(uint index, const Value &value) +bool Object::internalPutIndexed(uint index, const Value &value) { if (internalClass()->engine->hasException) - return; + return false; PropertyAttributes attrs; @@ -815,7 +825,7 @@ void Object::internalPutIndexed(uint index, const Value &value) goto reject; else *v = value; - return; + return true; } else if (!prototype()) { if (!isExtensible()) goto reject; @@ -846,15 +856,17 @@ void Object::internalPutIndexed(uint index, const Value &value) callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); - return; + return !internalClass()->engine->hasException; } arraySet(index, value); - return; + return true; reject: + // ### this should be removed once everything is ported to use Object::setIndexed() if (engine()->current->strictMode) engine()->throwTypeError(); + return false; } // Section 8.12.7 @@ -1155,6 +1167,49 @@ uint Object::getLength(const Managed *m) return v->toUInt32(); } +// 'var' is 'V' in 15.3.5.3. +ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) +{ + QV4::ExecutionEngine *engine = typeObject->internalClass()->engine; + + // 15.3.5.3, Assume F is a Function object. + const FunctionObject *function = typeObject->as<FunctionObject>(); + if (!function) + return engine->throwTypeError(); + + Heap::FunctionObject *f = function->d(); + if (function->isBoundFunction()) + f = function->cast<BoundFunction>()->target(); + + // 15.3.5.3, 1: HasInstance can only be used on an object + const Object *lhs = var.as<Object>(); + if (!lhs) + return Encode(false); + + // 15.3.5.3, 2 + const Object *o = f->protoProperty(); + if (!o) // 15.3.5.3, 3 + return engine->throwTypeError(); + + Heap::Object *v = lhs->d(); + + // 15.3.5.3, 4 + while (v) { + // 15.3.5.3, 4, a + v = v->prototype; + + // 15.3.5.3, 4, b + if (!v) + break; // will return false + + // 15.3.5.3, 4, c + else if (o->d() == v) + return Encode(true); + } + + return Encode(false); +} + bool Object::setArrayLength(uint newLen) { Q_ASSERT(isArrayObject()); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 4a78690f47..0d17afbf41 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -130,8 +130,8 @@ struct ObjectVTable void (*construct)(const Managed *, Scope &scope, CallData *data); ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty); ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty); - void (*put)(Managed *, String *name, const Value &value); - void (*putIndexed)(Managed *, uint index, const Value &value); + bool (*put)(Managed *, String *name, const Value &value); + bool (*putIndexed)(Managed *, uint index, const Value &value); PropertyAttributes (*query)(const Managed *, String *name); PropertyAttributes (*queryIndexed)(const Managed *, uint index); bool (*deleteProperty)(Managed *m, String *name); @@ -140,6 +140,7 @@ struct ObjectVTable void (*setLookup)(Managed *m, Lookup *l, const Value &v); uint (*getLength)(const Managed *m); void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var); }; #define DEFINE_OBJECT_VTABLE_BASE(classname) \ @@ -159,7 +160,8 @@ const QV4::ObjectVTable classname::static_vtbl = \ getLookup, \ setLookup, \ getLength, \ - advanceIterator \ + advanceIterator, \ + instanceOf \ } #define DEFINE_OBJECT_VTABLE(classname) \ @@ -221,8 +223,6 @@ struct Q_QML_EXPORT Object: Managed { // // helpers // - void put(ExecutionEngine *engine, const QString &name, const Value &value); - static ReturnedValue getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs); ReturnedValue getValue(const Value &v, PropertyAttributes attrs) const { Scope scope(this->engine()); @@ -230,7 +230,7 @@ struct Q_QML_EXPORT Object: Managed { return getValue(t, v, attrs); } - void putValue(uint memberIndex, const Value &value); + bool putValue(uint memberIndex, const Value &value); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ void defineDefaultProperty(String *name, const Value &value) { @@ -251,6 +251,10 @@ struct Q_QML_EXPORT Object: Managed { void defineReadonlyProperty(const QString &name, const Value &value); void defineReadonlyProperty(String *name, const Value &value); + /* Fixed: Writable: false, Enumerable: false, Configurable: true */ + void defineReadonlyConfigurableProperty(const QString &name, const Value &value); + void defineReadonlyConfigurableProperty(String *name, const Value &value); + void insertMember(String *s, const Value &v, PropertyAttributes attributes = Attr_Data) { Scope scope(engine()); ScopedProperty p(scope); @@ -332,10 +336,47 @@ public: { return vtable()->get(this, name, hasProperty); } inline ReturnedValue getIndexed(uint idx, bool *hasProperty = 0) const { return vtable()->getIndexed(this, idx, hasProperty); } - inline void put(String *name, const Value &v) - { vtable()->put(this, name, v); } - inline void putIndexed(uint idx, const Value &v) - { vtable()->putIndexed(this, idx, v); } + + // use the set variants instead, to customize throw behavior + inline bool put(String *name, const Value &v) + { return vtable()->put(this, name, v); } + inline bool putIndexed(uint idx, const Value &v) + { return vtable()->putIndexed(this, idx, v); } + + enum ThrowOnFailure { + DoThrowOnRejection, + DoNotThrow + }; + + // ES6: 7.3.3 Set (O, P, V, Throw) + inline bool set(String *name, const Value &v, ThrowOnFailure shouldThrow) + { + bool ret = vtable()->put(this, name, v); + // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception. + if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) { + ExecutionEngine *e = engine(); + if (!e->hasException) { // allow a custom set impl to throw itself + QString message = QLatin1String("Cannot assign to read-only property \"") + + name->toQString() + QLatin1Char('\"'); + e->throwTypeError(message); + } + } + return ret; + } + + inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow) + { + bool ret = vtable()->putIndexed(this, idx, v); + if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) { + ExecutionEngine *e = engine(); + if (!e->hasException) { // allow a custom set impl to throw itself + e->throwTypeError(); + } + } + return ret; + } + + PropertyAttributes query(String *name) const { return vtable()->query(this, name); } PropertyAttributes queryIndexed(uint index) const @@ -351,6 +392,8 @@ public: void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { vtable()->advanceIterator(this, it, name, index, p, attributes); } uint getLength() const { return vtable()->getLength(this); } + ReturnedValue instanceOf(const Value &var) const + { return vtable()->instanceOf(this, var); } inline void construct(Scope &scope, CallData *d) const { return vtable()->construct(this, scope, d); } @@ -362,8 +405,8 @@ protected: static void call(const Managed *m, Scope &scope, CallData *); static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); - static void putIndexed(Managed *m, uint index, const Value &value); + static bool put(Managed *m, String *name, const Value &value); + static bool putIndexed(Managed *m, uint index, const Value &value); static PropertyAttributes query(const Managed *m, String *name); static PropertyAttributes queryIndexed(const Managed *m, uint index); static bool deleteProperty(Managed *m, String *name); @@ -372,12 +415,13 @@ protected: static void setLookup(Managed *m, Lookup *l, const Value &v); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static uint getLength(const Managed *m); + static ReturnedValue instanceOf(const Object *typeObject, const Value &var); private: ReturnedValue internalGet(String *name, bool *hasProperty) const; ReturnedValue internalGetIndexed(uint index, bool *hasProperty) const; - void internalPut(String *name, const Value &value); - void internalPutIndexed(uint index, const Value &value); + bool internalPut(String *name, const Value &value); + bool internalPutIndexed(uint index, const Value &value); bool internalDeleteProperty(String *name); bool internalDeleteIndexedProperty(uint index); diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 97dbe24339..f650ffc7b1 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2017 Crimson AS <info@crimson.no> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -89,10 +90,11 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) ScopedObject o(scope, this); ctor->defineReadonlyProperty(v4->id_prototype(), o); - ctor->defineReadonlyProperty(v4->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(v4->id_length(), Primitive::fromInt32(1)); ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor->defineDefaultProperty(QStringLiteral("assign"), method_assign, 2); ctor->defineDefaultProperty(QStringLiteral("create"), method_create, 2); ctor->defineDefaultProperty(QStringLiteral("defineProperty"), method_defineProperty, 3); ctor->defineDefaultProperty(QStringLiteral("defineProperties"), method_defineProperties, 2); @@ -123,9 +125,8 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); ScopedObject p(scope, o->prototype()); scope.result = !!p ? p->asReturnedValue() : Encode::null(); @@ -133,11 +134,8 @@ void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scop void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject O(scope, callData->argument(0)); - if (!O) { - scope.result = scope.engine->throwTypeError(); - return; - } + ScopedObject O(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); if (ArgumentsObject::isNonStrictArgumentsObject(O)) static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate(); @@ -154,13 +152,54 @@ void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, S void ObjectPrototype::method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject O(scope, callData->argument(0)); - if (!O) { - scope.result = scope.engine->throwTypeError(); + ScopedObject O(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); + + scope.result = getOwnPropertyNames(scope.engine, callData->args[0]); +} + +// 19.1.2.1 +void ObjectPrototype::method_assign(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + ScopedObject to(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); + + if (callData->argc == 1) { + scope.result = to; return; } - scope.result = getOwnPropertyNames(scope.engine, callData->args[0]); + for (int i = 1; i < callData->argc; ++i) { + if (callData->args[i].isUndefined() || callData->args[i].isNull()) + continue; + + ScopedObject from(scope, callData->args[i].toObject(scope.engine)); + CHECK_EXCEPTION(); + QV4::ScopedArrayObject keys(scope, QV4::ObjectPrototype::getOwnPropertyNames(scope.engine, from)); + quint32 length = keys->getLength(); + + ScopedString nextKey(scope); + ScopedValue propValue(scope); + for (quint32 i = 0; i < length; ++i) { + nextKey = Value::fromReturnedValue(keys->getIndexed(i)).toString(scope.engine); + + PropertyAttributes attrs; + ScopedProperty prop(scope); + from->getOwnProperty(nextKey, &attrs, prop); + + if (attrs == PropertyFlag::Attr_Invalid) + continue; + + if (!attrs.isEnumerable()) + continue; + + propValue = from->get(nextKey); + to->set(nextKey, propValue, Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } + } + + scope.result = to; } void ObjectPrototype::method_create(const BuiltinFunction *builtin, Scope &scope, CallData *callData) @@ -246,8 +285,11 @@ void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &sc void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallData *callData) { ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + if (!o) { + // 19.1.2.17, 1 + scope.result = callData->argument(0); + return; + } o->setInternalClass(o->internalClass()->sealed()); @@ -265,8 +307,11 @@ void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallDat void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData) { ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + if (!o) { + // 19.1.2.5, 1 + scope.result = callData->argument(0); + return; + } if (ArgumentsObject::isNonStrictArgumentsObject(o)) static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate(); @@ -287,9 +332,11 @@ void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallD void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = callData->argument(0); + return; + } o->setInternalClass(o->internalClass()->nonExtensible()); scope.result = o; @@ -297,9 +344,11 @@ void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &s void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = Encode(true); + return; + } if (o->isExtensible()) { scope.result = Encode(false); @@ -335,9 +384,11 @@ void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, Cal void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = Encode(true); + return; + } if (o->isExtensible()) { scope.result = Encode(false); @@ -373,18 +424,19 @@ void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, Cal void ObjectPrototype::method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = Encode(false); + return; + } scope.result = Encode((bool)o->isExtensible()); } void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); ScopedArrayObject a(scope, scope.engine->newArrayObject()); @@ -670,12 +722,12 @@ ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, c return o.asReturnedValue(); } - +// es6: GetOwnPropertyKeys Heap::ArrayObject *ObjectPrototype::getOwnPropertyNames(ExecutionEngine *v4, const Value &o) { Scope scope(v4); ScopedArrayObject array(scope, v4->newArrayObject()); - ScopedObject O(scope, o); + ScopedObject O(scope, o.toObject(v4)); if (O) { ObjectIterator it(scope, O, ObjectIterator::NoFlags); ScopedValue name(scope); diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index 1db8615511..44b54267f3 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -81,6 +81,7 @@ struct ObjectPrototype: Object static void method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_assign(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_create(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 889f4ea288..cdc29c8b9c 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -225,21 +225,19 @@ ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasPr return Encode::undefined(); } -void QmlContextWrapper::put(Managed *m, String *name, const Value &value) +bool QmlContextWrapper::put(Managed *m, String *name, const Value &value) { Q_ASSERT(m->as<QmlContextWrapper>()); QmlContextWrapper *resource = static_cast<QmlContextWrapper *>(m); ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); if (scope.hasException()) - return; + return false; QV4::Scoped<QmlContextWrapper> wrapper(scope, resource); uint member = wrapper->internalClass()->find(name); - if (member < UINT_MAX) { - wrapper->putValue(member, value); - return; - } + if (member < UINT_MAX) + return wrapper->putValue(member, value); if (wrapper->d()->isNullWrapper) { if (wrapper && wrapper->d()->readOnly) { @@ -247,11 +245,10 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) QLatin1Char('"'); ScopedString e(scope, v4->newString(error)); v4->throwError(e); - return; + return false; } - Object::put(m, name, value); - return; + return Object::put(m, name, value); } // Its possible we could delay the calculation of the "actual" context (in the case @@ -260,7 +257,7 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) QQmlContextData *expressionContext = context; if (!context) - return; + return false; // See QV8ContextWrapper::Getter for resolution order @@ -270,18 +267,18 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) const QV4::IdentifierHash<int> &properties = context->propertyNames(); // Search context properties if (properties.count() && properties.value(name) != -1) - return; + return false; // Search scope object if (scopeObject && QV4::QObjectWrapper::setQmlProperty(v4, context, scopeObject, name, QV4::QObjectWrapper::CheckRevision, value)) - return; + return true; scopeObject = 0; // Search context object if (context->contextObject && QV4::QObjectWrapper::setQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, value)) - return; + return true; context = context->parent; } @@ -292,10 +289,10 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + QLatin1Char('"'); v4->throwError(error); - return; + return false; } - Object::put(m, name, value); + return Object::put(m, name, value); } void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml) diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 9aec7467da..6e5e743609 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -100,7 +100,7 @@ struct Q_QML_EXPORT QmlContextWrapper : Object void setReadOnly(bool b) { d()->readOnly = b; } static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + static bool put(Managed *m, String *name, const Value &value); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 7260e71fab..c9b4b433bd 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -625,13 +625,13 @@ QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *has return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true); } -void QObjectWrapper::put(Managed *m, String *name, const Value &value) +bool QObjectWrapper::put(Managed *m, String *name, const Value &value) { QObjectWrapper *that = static_cast<QObjectWrapper*>(m); ExecutionEngine *v4 = that->engine(); if (v4->hasException || QQmlData::wasDeleted(that->d()->object())) - return; + return false; QQmlContextData *qmlContext = v4->callingQmlContext(); if (!setQmlProperty(v4, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) { @@ -642,10 +642,13 @@ void QObjectWrapper::put(Managed *m, String *name, const Value &value) QString error = QLatin1String("Cannot assign to non-existent property \"") + name->toQString() + QLatin1Char('\"'); v4->throwError(error); + return false; } else { - QV4::Object::put(m, name, value); + return QV4::Object::put(m, name, value); } } + + return true; } PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) @@ -673,18 +676,24 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name QObjectWrapper *that = static_cast<QObjectWrapper*>(m); - if (that->d()->object()) { - const QMetaObject *mo = that->d()->object()->metaObject(); + QObject *thatObject = that->d()->object(); + if (thatObject && !QQmlData::wasDeleted(thatObject)) { + const QMetaObject *mo = thatObject->metaObject(); // These indices don't apply to gadgets, so don't block them. const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject; const int propertyCount = mo->propertyCount(); if (it->arrayIndex < static_cast<uint>(propertyCount)) { - Scope scope(that->engine()); - ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name()))); + ExecutionEngine *thatEngine = that->engine(); + Scope scope(thatEngine); + const QMetaProperty property = mo->property(it->arrayIndex); + ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name()))); name->setM(propName->d()); ++it->arrayIndex; *attributes = QV4::Attr_Data; - p->value = that->get(propName); + + QQmlPropertyData local; + local.load(property); + p->value = that->getProperty(thatEngine, thatObject, &local); return; } const int methodCount = mo->methodCount(); @@ -694,11 +703,15 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name ++it->arrayIndex; if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2))) continue; - Scope scope(that->engine()); - ScopedString methodName(scope, that->engine()->newString(QString::fromUtf8(method.name()))); + ExecutionEngine *thatEngine = that->engine(); + Scope scope(thatEngine); + ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name()))); name->setM(methodName->d()); *attributes = QV4::Attr_Data; - p->value = that->get(methodName); + + QQmlPropertyData local; + local.load(method); + p->value = that->getProperty(thatEngine, thatObject, &local); return; } } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index b09e06cec5..d81ef2a680 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -192,7 +192,7 @@ protected: QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + 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); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 40682aaa4b..0894d0c25b 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -367,7 +367,7 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat RETURN_RESULT(Encode::null()); } - uint* matchOffsets = (uint*)alloca(r->value()->captureCount() * 2 * sizeof(uint)); + Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 * sizeof(uint)); const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets); Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 023a739e33..6590054bf3 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -343,35 +343,15 @@ ReturnedValue Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) return Encode(engine->currentContext->deleteProperty(name)); } -QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &left, const Value &right) +QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &lval, const Value &rval) { - const FunctionObject *function = right.as<FunctionObject>(); - if (!function) - return engine->throwTypeError(); - - Heap::FunctionObject *f = function->d(); - if (function->isBoundFunction()) - f = function->cast<BoundFunction>()->target(); - - const Object *o = left.as<Object>(); - if (!o) - return Encode(false); - Heap::Object *v = o->d(); - - o = f->protoProperty(); - if (!o) - return engine->throwTypeError(); - - while (v) { - v = v->prototype; - - if (!v) - break; - else if (o->d() == v) - return Encode(true); - } + // 11.8.6, 5: rval must be an Object + const Object *rhs = rval.as<Object>(); + if (!rhs) + return engine->throwTypeError(); - return Encode(false); + // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result. + return rhs->instanceOf(lval); } QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right) @@ -1284,12 +1264,16 @@ ReturnedValue Runtime::method_unwindException(ExecutionEngine *engine) */ void Runtime::method_pushWithScope(const Value &o, NoThrowEngine *engine) { - engine->pushContext(engine->currentContext->newWithContext(o.toObject(engine))); + QV4::Value *v = engine->jsAlloca(1); + Heap::Object *withObject = o.toObject(engine); + *v = withObject; + engine->pushContext(engine->currentContext->newWithContext(withObject)); Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); } void Runtime::method_pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex) { + engine->jsAlloca(1); // keep this symmetric with pushWithScope ExecutionContext *c = engine->currentContext; engine->pushContext(c->newCatchContext(c->d()->compilationUnit->runtimeStrings[exceptionVarNameIndex], engine->catchException(0))); Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); @@ -1299,7 +1283,7 @@ void Runtime::method_popScope(NoThrowEngine *engine) { Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); engine->popContext(); - engine->jsStackTop -= 2; + engine->jsStackTop -= 3; } void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 8ce10e326d..6d3110771e 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -274,20 +274,20 @@ public: return Encode::undefined(); } - void containerPutIndexed(uint index, const QV4::Value &value) + bool containerPutIndexed(uint index, const QV4::Value &value) { if (internalClass()->engine->hasException) - return; + return false; /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) { generateWarning(engine(), QLatin1String("Index out of range during indexed set")); - return; + return false; } if (d()->isReference) { if (!d()->object) - return; + return false; loadReference(); } @@ -313,6 +313,7 @@ public: if (d()->isReference) storeReference(); + return true; } QV4::PropertyAttributes containerQueryIndexed(uint index) const @@ -540,8 +541,8 @@ public: static QV4::ReturnedValue getIndexed(const QV4::Managed *that, uint index, bool *hasProperty) { return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); } - static void putIndexed(Managed *that, uint index, const QV4::Value &value) - { static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); } + static bool putIndexed(Managed *that, uint index, const QV4::Value &value) + { return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); } static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) { return static_cast<const QQmlSequence<Container> *>(that)->containerQueryIndexed(index); } static bool deleteIndexedProperty(QV4::Managed *that, uint index) diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 3c6a24e035..72be11eca0 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -200,6 +200,7 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1); defineDefaultProperty(QStringLiteral("match"), method_match, 1); + defineDefaultProperty(QStringLiteral("repeat"), method_repeat, 1); defineDefaultProperty(QStringLiteral("replace"), method_replace, 2); defineDefaultProperty(QStringLiteral("search"), method_search, 1); defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); @@ -458,6 +459,21 @@ void StringPrototype::method_match(const BuiltinFunction *, Scope &scope, CallDa scope.result = a; } +void StringPrototype::method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); + + double repeats = callData->args[0].toInteger(); + + if (repeats < 0 || qIsInf(repeats)) { + scope.result = scope.engine->throwRangeError(QLatin1String("Invalid count value")); + return; + } + + scope.result = scope.engine->newString(value.repeated(int(repeats))); +} + static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) { result->reserve(result->length() + replaceValue.length()); @@ -634,7 +650,7 @@ void StringPrototype::method_search(const BuiltinFunction *, Scope &scope, CallD Q_ASSERT(regExp); } Scoped<RegExp> re(scope, regExp->value()); - uint* matchOffsets = (uint*)alloca(regExp->value()->captureCount() * 2 * sizeof(uint)); + Q_ALLOCA_VAR(uint, matchOffsets, regExp->value()->captureCount() * 2 * sizeof(uint)); uint result = re->match(string, /*offset*/0, matchOffsets); if (result == JSC::Yarr::offsetNoMatch) scope.result = Encode(-1); @@ -705,7 +721,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa ScopedString s(scope); if (re) { uint offset = 0; - uint* matchOffsets = (uint*)alloca(re->value()->captureCount() * 2 * sizeof(uint)); + Q_ALLOCA_VAR(uint, matchOffsets, re->value()->captureCount() * 2 * sizeof(uint)); while (true) { Scoped<RegExp> regexp(scope, re->value()); uint result = regexp->match(text, offset, matchOffsets); diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 0ee7a6ece9..aed3bc1e28 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -121,6 +121,7 @@ struct StringPrototype: StringObject static void method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_localeCompare(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_match(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_replace(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_search(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_slice(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index cecd1e6958..5573a2e57f 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -398,11 +398,11 @@ ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProp return a->d()->type->read(a->d()->buffer->data->data(), byteOffset); } -void TypedArray::putIndexed(Managed *m, uint index, const Value &value) +bool TypedArray::putIndexed(Managed *m, uint index, const Value &value) { ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); if (v4->hasException) - return; + return false; Scope scope(v4); Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m)); @@ -413,11 +413,12 @@ void TypedArray::putIndexed(Managed *m, uint index, const Value &value) goto reject; a->d()->type->write(scope.engine, a->d()->buffer->data->data(), byteOffset, value); - return; + return true; reject: if (scope.engine->current->strictMode) scope.engine->throwTypeError(); + return false; } void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index eefed2db4b..fbf13c9815 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -133,7 +133,7 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object static void markObjects(Heap::Base *that, ExecutionEngine *e); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void putIndexed(Managed *m, uint index, const Value &value); + static bool putIndexed(Managed *m, uint index, const Value &value); }; struct TypedArrayCtor: FunctionObject diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 816b8fb11b..4ff0565f9b 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -254,34 +254,47 @@ public: Q_ASSERT(isDouble()); return Double_Type; } -#ifndef QV4_USE_64_BIT_VALUE_ENCODING + // Shared between 32-bit and 64-bit encoding + enum { + Tag_Shift = 32 + }; + + // Used only by 64-bit encoding + static const quint64 NaNEncodeMask = 0xfffc000000000000ll; + enum { + IsDouble_Shift = 64-14, + IsManagedOrUndefined_Shift = 64-15, + IsIntegerConvertible_Shift = 64-16, + IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, + Managed_Type_Internal_64 = 0 + }; + static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 + + // Used only by 32-bit encoding enum Masks { SilentNaNBit = 0x00040000, - NaN_Mask = 0x7ff80000, NotDouble_Mask = 0x7ffa0000, - Immediate_Mask = NotDouble_Mask | 0x00020000u | SilentNaNBit, - Tag_Shift = 32 }; + static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; enum { - Managed_Type_Internal = NotDouble_Mask + Managed_Type_Internal_32 = NotDouble_Mask }; -#else - static const quint64 NaNEncodeMask = 0xfffc000000000000ll; - static const quint64 Immediate_Mask = 0x00020000u; // bit 49 - enum Masks { - NaN_Mask = 0x7ff80000, +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + enum { + Managed_Type_Internal = Managed_Type_Internal_64 }; + static const quint64 Immediate_Mask = Immediate_Mask_64; +#else enum { - IsDouble_Shift = 64-14, - IsManagedOrUndefined_Shift = 64-15, - IsIntegerConvertible_Shift = 64-16, - Tag_Shift = 32, - IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, - Managed_Type_Internal = 0 + Managed_Type_Internal = Managed_Type_Internal_32 }; + static const quint64 Immediate_Mask = Immediate_Mask_32; #endif + enum { + NaN_Mask = 0x7ff80000, + }; enum ValueTypeInternal { Empty_Type_Internal = Immediate_Mask | 0, ConvertibleToInt = Immediate_Mask | 0x10000u, // bit 48 @@ -544,7 +557,7 @@ inline bool Value::asArrayIndex(uint &idx) const } double d = doubleValue(); idx = (uint)d; - return (idx == d); + return (idx == d && idx != UINT_MAX); } #endif diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index b9183313cd..be2772c23f 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -402,7 +402,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code } } - QV4::Value **scopes = static_cast<QV4::Value **>(alloca(sizeof(QV4::Value *)*(2 + 2*scopeDepth))); + Q_ALLOCA_VAR(QV4::Value*, scopes, sizeof(QV4::Value *)*(2 + 2*scopeDepth)); { scopes[0] = const_cast<QV4::Value *>(context->d()->compilationUnit->constants); // stack gets setup in push instruction @@ -957,8 +957,6 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code return QV4::Encode::undefined(); code = exceptionHandler; } - - } #ifdef MOTH_THREADED_INTERPRETER diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 6330ef6038..a829e902fb 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -328,40 +328,65 @@ void Chunk::freeAll() void Chunk::sortIntoBins(HeapItem **bins, uint nBins) { +// qDebug() << "sortIntoBins:"; HeapItem *base = realBase(); #if QT_POINTER_SIZE == 8 const int start = 0; #else const int start = 1; #endif +#ifndef QT_NO_DEBUG + uint freeSlots = 0; + uint allocatedSlots = 0; +#endif for (int i = start; i < EntriesInBitmap; ++i) { quintptr usedSlots = (objectBitmap[i]|extendsBitmap[i]); #if QT_POINTER_SIZE == 8 if (!i) usedSlots |= (static_cast<quintptr>(1) << (HeaderSize/SlotSize)) - 1; #endif - uint index = qCountTrailingZeroBits(usedSlots + 1); - if (index == Bits) - continue; - uint freeStart = i*Bits + index; - usedSlots &= ~((static_cast<quintptr>(1) << index) - 1); - while (i < EntriesInBitmap && !usedSlots) { - ++i; - usedSlots = (objectBitmap[i]|extendsBitmap[i]); +#ifndef QT_NO_DEBUG + allocatedSlots += qPopulationCount(usedSlots); +// qDebug() << hex << " i=" << i << "used=" << usedSlots; +#endif + while (1) { + uint index = qCountTrailingZeroBits(usedSlots + 1); + if (index == Bits) + break; + uint freeStart = i*Bits + index; + usedSlots &= ~((static_cast<quintptr>(1) << index) - 1); + while (!usedSlots) { + ++i; + if (i == EntriesInBitmap) { + usedSlots = (quintptr)-1; + break; + } + usedSlots = (objectBitmap[i]|extendsBitmap[i]); +#ifndef QT_NO_DEBUG + allocatedSlots += qPopulationCount(usedSlots); +// qDebug() << hex << " i=" << i << "used=" << usedSlots; +#endif + } + HeapItem *freeItem = base + freeStart; + + index = qCountTrailingZeroBits(usedSlots); + usedSlots |= (quintptr(1) << index) - 1; + uint freeEnd = i*Bits + index; + uint nSlots = freeEnd - freeStart; +#ifndef QT_NO_DEBUG +// qDebug() << hex << " got free slots from" << freeStart << "to" << freeEnd << "n=" << nSlots << "usedSlots=" << usedSlots; + freeSlots += nSlots; +#endif + Q_ASSERT(freeEnd > freeStart && freeEnd <= NumSlots); + freeItem->freeData.availableSlots = nSlots; + uint bin = qMin(nBins - 1, nSlots); + freeItem->freeData.next = bins[bin]; + bins[bin] = freeItem; } - if (i == EntriesInBitmap) - usedSlots = 1; - HeapItem *freeItem = base + freeStart; - - uint freeEnd = i*Bits + qCountTrailingZeroBits(usedSlots); - uint nSlots = freeEnd - freeStart; - Q_ASSERT(freeEnd > freeStart && freeEnd <= NumSlots); - freeItem->freeData.availableSlots = nSlots; - uint bin = qMin(nBins - 1, nSlots); - freeItem->freeData.next = bins[bin]; - bins[bin] = freeItem; - // DEBUG << "binnig item" << freeItem << nSlots << bin << freeItem->freeData.availableSlots; } +#ifndef QT_NO_DEBUG + Q_ASSERT(freeSlots + allocatedSlots == (EntriesInBitmap - start) * 8 * sizeof(quintptr)); +#endif } @@ -427,28 +452,7 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { goto done; } } -#if 0 - for (uint b = bin + 1; b < NumBins - 1; ++b) { - if ((m = freeBins[b])) { - Q_ASSERT(binForSlots(m->freeData.availableSlots) == b); - freeBins[b] = m->freeData.next; - // DEBUG << "looking for empty bin" << bin << "size" << size << "found" << b; - uint remainingSlots = m->freeData.availableSlots - slotsRequired; - // DEBUG << "found free slots of size" << m->freeData.availableSlots << m << "remaining" << remainingSlots; - if (remainingSlots < 2) { - // avoid too much fragmentation and rather mark the memory as used - size += remainingSlots*Chunk::SlotSize; - goto done; - } - HeapItem *remainder = m + slotsRequired; - remainder->freeData.availableSlots = remainingSlots; - uint binForRemainder = binForSlots(remainingSlots); - remainder->freeData.next = freeBins[binForRemainder]; - freeBins[binForRemainder] = remainder; - goto done; - } - } -#endif + if (nFree >= slotsRequired) { // use bump allocation Q_ASSERT(nextFree); @@ -467,13 +471,11 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { size_t remainingSlots = m->freeData.availableSlots - slotsRequired; // DEBUG << "found large free slots of size" << m->freeData.availableSlots << m << "remaining" << remainingSlots; - if (remainingSlots < 2) { - // avoid too much fragmentation and rather mark the memory as used - size += remainingSlots*Chunk::SlotSize; + if (remainingSlots == 0) goto done; - } + HeapItem *remainder = m + slotsRequired; - if (remainingSlots >= 2*NumBins) { + if (remainingSlots > nFree) { if (nFree) { size_t bin = binForSlots(nFree); nextFree->freeData.next = freeBins[bin]; @@ -493,6 +495,24 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { last = &m->freeData.next; } + if (slotsRequired < NumBins - 1) { + // check if we can split up another slot + for (size_t i = slotsRequired + 1; i < NumBins - 1; ++i) { + m = freeBins[i]; + if (m) { + freeBins[i] = m->freeData.next; // take it out of the list +// qDebug() << "got item" << slotsRequired << "from slot" << i; + size_t remainingSlots = i - slotsRequired; + Q_ASSERT(remainingSlots < NumBins - 1); + HeapItem *remainder = m + slotsRequired; + remainder->freeData.availableSlots = remainingSlots; + remainder->freeData.next = freeBins[remainingSlots]; + freeBins[remainingSlots] = remainder; + goto done; + } + } + } + if (!m) { if (!forceAllocation) return 0; @@ -622,14 +642,24 @@ MemoryManager::MemoryManager(ExecutionEngine *engine) #endif } +#ifndef QT_NO_DEBUG +static size_t lastAllocRequestedSlots = 0; +#endif + Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize) { - if (aggressiveGC) + const size_t stringSize = align(sizeof(Heap::String)); +#ifndef QT_NO_DEBUG + lastAllocRequestedSlots = stringSize >> Chunk::SlotSizeShift; +#endif + + bool didGCRun = false; + if (aggressiveGC) { runGC(); + didGCRun = true; + } - const size_t stringSize = align(sizeof(Heap::String)); unmanagedHeapSize += unmanagedSize; - bool didGCRun = false; if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) { runGC(); @@ -655,8 +685,15 @@ Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize) Heap::Base *MemoryManager::allocData(std::size_t size) { - if (aggressiveGC) +#ifndef QT_NO_DEBUG + lastAllocRequestedSlots = size >> Chunk::SlotSizeShift; +#endif + + bool didRunGC = false; + if (aggressiveGC) { runGC(); + didRunGC = true; + } #ifdef DETAILED_MM_STATS willAllocate(size); #endif // DETAILED_MM_STATS @@ -671,7 +708,7 @@ Heap::Base *MemoryManager::allocData(std::size_t size) HeapItem *m = blockAllocator.allocate(size); if (!m) { - if (shouldRunGC()) + if (!didRunGC && shouldRunGC()) runGC(); m = blockAllocator.allocate(size, true); } @@ -817,6 +854,26 @@ bool MemoryManager::shouldRunGC() const return false; } +size_t dumpBins(BlockAllocator *b, bool printOutput = true) +{ + size_t totalFragmentedSlots = 0; + if (printOutput) + qDebug() << "Fragmentation map:"; + for (uint i = 0; i < BlockAllocator::NumBins; ++i) { + uint nEntries = 0; + HeapItem *h = b->freeBins[i]; + while (h) { + ++nEntries; + totalFragmentedSlots += 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; +} void MemoryManager::runGC() { @@ -833,31 +890,56 @@ void MemoryManager::runGC() sweep(); // DEBUG << "RUN GC: allocated:" << allocator.allocatedMem() << "used before" << oldUsed << "used now" << allocator.usedMem(); } else { + bool triggeredByUnmanagedHeap = (unmanagedHeapSize > unmanagedHeapSizeGCLimit); + size_t oldUnmanagedSize = unmanagedHeapSize; const size_t totalMem = getAllocatedMem(); + const size_t usedBefore = getUsedMem(); + const size_t largeItemsBefore = getLargeItemsMem(); + + qDebug() << "========== GC =========="; +#ifndef QT_NO_DEBUG + qDebug() << " Triggered by alloc request of" << lastAllocRequestedSlots << "slots."; +#endif + qDebug() << "Allocated" << totalMem << "bytes in" << blockAllocator.chunks.size() << "chunks"; + qDebug() << "Fragmented memory before GC" << (totalMem - usedBefore); + dumpBins(&blockAllocator); QElapsedTimer t; t.start(); mark(); qint64 markTime = t.restart(); - const size_t usedBefore = getUsedMem(); - const size_t largeItemsBefore = getLargeItemsMem(); sweep(); const size_t usedAfter = getUsedMem(); const size_t largeItemsAfter = getLargeItemsMem(); qint64 sweepTime = t.elapsed(); - qDebug() << "========== GC =========="; + if (triggeredByUnmanagedHeap) { + qDebug() << "triggered by unmanaged heap:"; + qDebug() << " old unmanaged heap size:" << oldUnmanagedSize; + qDebug() << " new unmanaged heap:" << unmanagedHeapSize; + qDebug() << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit; + } + size_t memInBins = dumpBins(&blockAllocator); qDebug() << "Marked object in" << markTime << "ms."; qDebug() << "Sweeped object in" << sweepTime << "ms."; - qDebug() << "Allocated" << totalMem << "bytes"; qDebug() << "Used memory before GC:" << usedBefore; qDebug() << "Used memory after GC:" << usedAfter; qDebug() << "Freed up bytes:" << (usedBefore - usedAfter); - qDebug() << "Large item memory before GC:" << largeItemsBefore; - qDebug() << "Large item memory after GC:" << largeItemsAfter; - qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter); + size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter; + if (lost) + qDebug() << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; + if (largeItemsBefore || largeItemsAfter) { + qDebug() << "Large item memory before GC:" << largeItemsBefore; + qDebug() << "Large item memory after GC:" << largeItemsAfter; + qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter); + } qDebug() << "======== End GC ========"; } + + if (aggressiveGC) { + // ensure we don't 'loose' any memory + Q_ASSERT(blockAllocator.allocatedMem() == getUsedMem() + dumpBins(&blockAllocator, false)); + } } size_t MemoryManager::getUsedMem() const diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 9cd212015e..ca84e0c157 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -71,7 +71,7 @@ %token T_VAR "var" T_VOID "void" T_WHILE "while" %token T_WITH "with" T_XOR "^" T_XOR_EQ "^=" %token T_NULL "null" T_TRUE "true" T_FALSE "false" -%token T_CONST "const" +%token T_CONST "const" T_LET "let" %token T_DEBUGGER "debugger" %token T_RESERVED_WORD "reserved word" %token T_MULTILINE_STRING_LITERAL "multiline string literal" @@ -1622,6 +1622,7 @@ ReservedIdentifier: T_VAR ; ReservedIdentifier: T_VOID ; ReservedIdentifier: T_WHILE ; ReservedIdentifier: T_CONST ; +ReservedIdentifier: T_LET ; ReservedIdentifier: T_DEBUGGER ; ReservedIdentifier: T_RESERVED_WORD ; ReservedIdentifier: T_WITH ; @@ -2486,14 +2487,26 @@ VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_S VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ; /. case $rule_number: { - AST::VariableStatement *node = new (pool) AST::VariableStatement( - sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + if (sym(1).ival == T_LET) + s = AST::VariableDeclaration::BlockScope; + else if (sym(1).ival == T_CONST) + s = AST::VariableDeclaration::ReadOnlyBlockScope; + + AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(s)); node->declarationKindToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; ./ +VariableDeclarationKind: T_LET ; +/. +case $rule_number: { + sym(1).ival = T_LET; +} break; +./ + VariableDeclarationKind: T_CONST ; /. case $rule_number: { @@ -2542,7 +2555,8 @@ case $rule_number: { VariableDeclaration: JsIdentifier InitialiserOpt ; /. case $rule_number: { - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; @@ -2551,7 +2565,8 @@ case $rule_number: { VariableDeclarationNotIn: JsIdentifier InitialiserNotInOpt ; /. case $rule_number: { - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; @@ -2677,8 +2692,9 @@ case $rule_number: { IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; /. case $rule_number: { + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; AST::LocalForStatement *node = new (pool) AST::LocalForStatement( - sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(4).VariableDeclarationList->finish(s), sym(6).Expression, sym(8).Expression, sym(10).Statement); node->forToken = loc(1); node->lparenToken = loc(2); diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 9b06bf3d31..0de419d697 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -1315,10 +1315,18 @@ class QML_PARSER_EXPORT VariableDeclaration: public Node public: QQMLJS_DECLARE_AST_NODE(VariableDeclaration) - VariableDeclaration(const QStringRef &n, ExpressionNode *e): - name (n), expression (e), readOnly(false) + enum VariableScope { + FunctionScope, + BlockScope, // let + ReadOnlyBlockScope // const + }; + + VariableDeclaration(const QStringRef &n, ExpressionNode *e, VariableScope s): + name (n), expression (e), scope(s) { kind = K; } + bool isLexicallyScoped() const { return scope != FunctionScope; } + void accept0(Visitor *visitor) override; SourceLocation firstSourceLocation() const override @@ -1330,8 +1338,8 @@ public: // attributes QStringRef name; ExpressionNode *expression; - bool readOnly; SourceLocation identifierToken; + VariableScope scope; }; class QML_PARSER_EXPORT VariableDeclarationList: public Node @@ -1363,14 +1371,13 @@ public: return declaration->lastSourceLocation(); } - inline VariableDeclarationList *finish (bool readOnly) + inline VariableDeclarationList *finish(VariableDeclaration::VariableScope s) { VariableDeclarationList *front = next; next = 0; - if (readOnly) { - VariableDeclarationList *vdl; - for (vdl = front; vdl != 0; vdl = vdl->next) - vdl->declaration->readOnly = true; + VariableDeclarationList *vdl; + for (vdl = front; vdl != 0; vdl = vdl->next) { + vdl->declaration->scope = s; } return front; } diff --git a/src/qml/parser/qqmljsgrammar.cpp b/src/qml/parser/qqmljsgrammar.cpp index b27f4af080..ca5a4bbd85 100644 --- a/src/qml/parser/qqmljsgrammar.cpp +++ b/src/qml/parser/qqmljsgrammar.cpp @@ -51,48 +51,48 @@ const char *const QQmlJSGrammar::spell [] = { "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return", ")", ";", 0, "*", "*=", "string literal", "property", "signal", "readonly", "switch", "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^", - "^=", "null", "true", "false", "const", "debugger", "reserved word", "multiline string literal", "comment", 0, - "public", "import", "pragma", "as", "on", "get", "set", 0, 0, 0, - 0, 0, 0, 0, 0, 0}; + "^=", "null", "true", "false", "const", "let", "debugger", "reserved word", "multiline string literal", "comment", + 0, "public", "import", "pragma", "as", "on", "get", "set", 0, 0, + 0, 0, 0, 0, 0, 0, 0}; const short QQmlJSGrammar::lhs [] = { - 106, 106, 106, 106, 106, 106, 107, 113, 113, 116, - 116, 116, 116, 119, 121, 117, 117, 118, 118, 118, - 118, 118, 118, 118, 118, 122, 123, 115, 114, 126, - 126, 127, 127, 128, 128, 125, 111, 111, 111, 111, - 130, 130, 130, 130, 130, 130, 130, 111, 138, 138, - 138, 138, 139, 139, 140, 140, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 124, 124, 124, 124, - 124, 124, 124, 143, 143, 143, 143, 143, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, - 143, 129, 145, 145, 145, 145, 144, 144, 149, 149, - 149, 147, 147, 150, 150, 150, 150, 153, 153, 153, - 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, - 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, - 153, 153, 153, 153, 153, 153, 153, 153, 154, 154, - 120, 120, 120, 120, 120, 157, 157, 158, 158, 158, - 158, 156, 156, 159, 159, 160, 160, 161, 161, 161, - 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, - 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, - 165, 166, 166, 166, 166, 166, 166, 166, 167, 167, - 167, 167, 167, 167, 168, 168, 168, 168, 168, 169, - 169, 169, 169, 169, 170, 170, 171, 171, 172, 172, - 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, - 178, 178, 179, 179, 180, 180, 181, 181, 148, 148, - 182, 182, 183, 183, 183, 183, 183, 183, 183, 183, - 183, 183, 183, 183, 109, 109, 184, 184, 185, 185, - 186, 186, 108, 108, 108, 108, 108, 108, 108, 108, - 108, 108, 108, 108, 108, 108, 108, 131, 195, 195, - 194, 194, 142, 142, 196, 196, 197, 197, 199, 199, - 198, 200, 203, 201, 201, 204, 202, 202, 132, 133, - 133, 134, 134, 187, 187, 187, 187, 187, 187, 187, - 187, 188, 188, 188, 188, 189, 189, 189, 189, 190, - 190, 135, 136, 205, 205, 208, 208, 206, 206, 209, - 207, 191, 192, 192, 137, 137, 137, 210, 211, 193, - 193, 212, 141, 155, 155, 213, 213, 152, 152, 151, - 151, 214, 112, 112, 215, 215, 110, 110, 146, 146, - 216}; + 107, 107, 107, 107, 107, 107, 108, 114, 114, 117, + 117, 117, 117, 120, 122, 118, 118, 119, 119, 119, + 119, 119, 119, 119, 119, 123, 124, 116, 115, 127, + 127, 128, 128, 129, 129, 126, 112, 112, 112, 112, + 131, 131, 131, 131, 131, 131, 131, 112, 139, 139, + 139, 139, 140, 140, 141, 141, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 125, 125, 125, 125, + 125, 125, 125, 144, 144, 144, 144, 144, 144, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 144, 130, 146, 146, 146, 146, 145, 145, 150, 150, + 150, 148, 148, 151, 151, 151, 151, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 155, + 155, 121, 121, 121, 121, 121, 158, 158, 159, 159, + 159, 159, 157, 157, 160, 160, 161, 161, 162, 162, + 162, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 164, 164, 164, 164, 165, 165, 165, 166, 166, + 166, 166, 167, 167, 167, 167, 167, 167, 167, 168, + 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, + 170, 170, 170, 170, 170, 171, 171, 172, 172, 173, + 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, + 178, 179, 179, 180, 180, 181, 181, 182, 182, 149, + 149, 183, 183, 184, 184, 184, 184, 184, 184, 184, + 184, 184, 184, 184, 184, 110, 110, 185, 185, 186, + 186, 187, 187, 109, 109, 109, 109, 109, 109, 109, + 109, 109, 109, 109, 109, 109, 109, 109, 132, 196, + 196, 195, 195, 143, 143, 197, 197, 197, 198, 198, + 200, 200, 199, 201, 204, 202, 202, 205, 203, 203, + 133, 134, 134, 135, 135, 188, 188, 188, 188, 188, + 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, + 190, 191, 191, 136, 137, 206, 206, 209, 209, 207, + 207, 210, 208, 192, 193, 193, 138, 138, 138, 211, + 212, 194, 194, 213, 142, 156, 156, 214, 214, 153, + 153, 152, 152, 215, 113, 113, 216, 216, 111, 111, + 147, 147, 217}; const short QQmlJSGrammar::rhs [] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, @@ -110,777 +110,798 @@ const short QQmlJSGrammar::rhs [] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 4, 3, 5, 1, 2, 4, 4, 4, - 3, 0, 1, 1, 3, 1, 1, 1, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 3, 3, 3, 1, 3, 3, 1, 3, 3, - 3, 1, 3, 3, 3, 3, 3, 3, 1, 3, - 3, 3, 3, 3, 1, 3, 3, 3, 3, 1, - 3, 3, 3, 3, 1, 3, 1, 3, 1, 3, - 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, - 1, 3, 1, 3, 1, 5, 1, 5, 1, 3, - 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 3, 0, 1, 1, 3, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 3, 1, 2, - 0, 1, 3, 3, 1, 1, 1, 3, 1, 3, - 2, 2, 2, 0, 1, 2, 0, 1, 1, 2, - 2, 7, 5, 7, 7, 7, 5, 9, 10, 7, - 8, 2, 2, 3, 3, 2, 2, 3, 3, 3, - 3, 5, 5, 3, 5, 1, 2, 0, 1, 4, - 3, 3, 3, 3, 3, 3, 4, 5, 2, 2, - 2, 1, 8, 8, 7, 1, 3, 0, 1, 0, - 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, - 2}; + 1, 1, 1, 4, 3, 5, 1, 2, 4, 4, + 4, 3, 0, 1, 1, 3, 1, 1, 1, 2, + 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 3, 3, 3, 1, 3, 3, 1, 3, + 3, 3, 1, 3, 3, 3, 3, 3, 3, 1, + 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, + 1, 3, 3, 3, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 5, 1, 5, 1, + 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 0, 1, 1, + 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, + 2, 0, 1, 3, 3, 1, 1, 1, 1, 3, + 1, 3, 2, 2, 2, 0, 1, 2, 0, 1, + 1, 2, 2, 7, 5, 7, 7, 7, 5, 9, + 10, 7, 8, 2, 2, 3, 3, 2, 2, 3, + 3, 3, 3, 5, 5, 3, 5, 1, 2, 0, + 1, 4, 3, 3, 3, 3, 3, 3, 4, 5, + 2, 2, 2, 1, 8, 8, 7, 1, 3, 0, + 1, 0, 1, 1, 1, 1, 1, 2, 1, 1, + 0, 1, 2}; const short QQmlJSGrammar::action_default [] = { - 0, 0, 28, 0, 0, 0, 28, 0, 188, 255, - 219, 227, 223, 167, 239, 215, 3, 152, 85, 168, - 231, 235, 156, 185, 166, 171, 151, 205, 192, 0, - 92, 93, 88, 0, 82, 77, 359, 0, 0, 0, + 0, 0, 28, 0, 0, 0, 28, 0, 189, 256, + 220, 228, 224, 168, 240, 216, 3, 153, 85, 169, + 232, 236, 157, 186, 167, 172, 152, 206, 193, 0, + 92, 93, 88, 0, 82, 77, 361, 0, 0, 0, 0, 90, 0, 0, 86, 89, 81, 0, 0, 78, - 80, 83, 79, 91, 84, 0, 87, 0, 0, 181, - 0, 0, 168, 187, 170, 169, 0, 0, 0, 183, - 184, 182, 186, 0, 216, 0, 0, 0, 0, 206, - 0, 0, 0, 0, 0, 0, 196, 0, 0, 0, - 190, 191, 189, 194, 198, 197, 195, 193, 208, 207, - 209, 0, 224, 0, 220, 0, 0, 162, 149, 161, - 150, 118, 119, 120, 145, 121, 146, 122, 123, 124, - 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, - 147, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 148, 0, 0, 160, 256, 163, 0, 164, 0, - 165, 159, 0, 252, 245, 243, 250, 251, 249, 248, - 254, 247, 246, 244, 253, 240, 0, 228, 0, 0, - 232, 0, 0, 236, 0, 0, 162, 154, 0, 153, - 0, 158, 172, 0, 348, 348, 349, 0, 346, 0, - 347, 0, 350, 263, 270, 269, 277, 265, 0, 266, - 0, 351, 0, 358, 267, 268, 85, 273, 271, 355, - 352, 357, 274, 0, 285, 0, 0, 0, 0, 342, - 0, 359, 257, 299, 0, 0, 0, 286, 0, 0, - 275, 276, 0, 264, 272, 300, 301, 0, 348, 0, - 0, 350, 0, 343, 344, 0, 332, 356, 0, 316, - 317, 318, 319, 0, 312, 313, 314, 315, 340, 341, - 0, 0, 0, 0, 0, 304, 305, 306, 261, 259, - 221, 229, 225, 241, 217, 262, 0, 168, 233, 237, - 210, 199, 0, 0, 218, 0, 0, 0, 0, 211, - 0, 0, 0, 0, 0, 203, 201, 204, 202, 200, - 213, 212, 214, 0, 226, 0, 222, 0, 260, 168, - 0, 242, 257, 258, 0, 257, 0, 0, 308, 0, - 0, 0, 310, 0, 230, 0, 0, 234, 0, 0, - 238, 297, 0, 289, 298, 292, 0, 296, 0, 257, - 290, 0, 257, 0, 0, 309, 0, 0, 0, 311, - 0, 0, 0, 303, 0, 302, 85, 112, 360, 0, - 0, 117, 279, 282, 0, 118, 285, 121, 146, 123, - 124, 88, 128, 129, 82, 130, 133, 86, 89, 257, - 83, 91, 136, 84, 138, 87, 140, 141, 286, 143, - 144, 148, 0, 114, 113, 116, 100, 115, 99, 0, - 109, 280, 278, 0, 0, 0, 350, 0, 110, 156, - 157, 162, 0, 155, 0, 320, 321, 0, 348, 0, - 0, 350, 0, 111, 0, 0, 0, 323, 328, 326, - 329, 0, 0, 327, 328, 0, 324, 0, 325, 281, - 331, 0, 281, 330, 0, 333, 334, 0, 281, 335, - 336, 0, 0, 337, 0, 0, 0, 338, 339, 174, - 173, 0, 0, 0, 307, 0, 0, 0, 322, 294, - 287, 0, 295, 291, 0, 293, 283, 0, 284, 288, - 0, 0, 350, 0, 345, 103, 0, 0, 107, 94, - 0, 96, 105, 0, 97, 106, 108, 98, 104, 95, - 0, 101, 178, 176, 180, 177, 175, 179, 353, 6, - 354, 4, 2, 75, 102, 0, 0, 78, 80, 79, - 37, 5, 0, 76, 0, 51, 50, 49, 0, 0, - 51, 0, 0, 0, 52, 0, 67, 68, 0, 65, - 0, 66, 41, 42, 43, 44, 46, 47, 71, 45, - 0, 51, 0, 0, 0, 0, 0, 61, 0, 62, - 0, 0, 32, 0, 0, 72, 33, 0, 36, 34, - 30, 0, 35, 31, 0, 63, 0, 64, 156, 0, - 69, 73, 0, 0, 0, 0, 156, 281, 0, 70, - 85, 118, 285, 121, 146, 123, 124, 88, 128, 129, - 130, 133, 86, 89, 257, 91, 136, 84, 138, 87, - 140, 141, 286, 143, 144, 148, 74, 0, 59, 53, - 60, 54, 0, 0, 0, 0, 56, 0, 57, 58, - 55, 0, 0, 0, 0, 48, 0, 38, 39, 0, - 40, 8, 0, 0, 9, 0, 11, 0, 10, 0, - 1, 27, 15, 14, 26, 13, 12, 29, 7, 0, - 18, 0, 19, 0, 24, 25, 0, 20, 21, 0, - 22, 23, 16, 17, 361}; + 80, 83, 79, 91, 84, 0, 87, 0, 0, 182, + 0, 0, 169, 188, 171, 170, 0, 0, 0, 184, + 185, 183, 187, 0, 217, 0, 0, 0, 0, 207, + 0, 0, 0, 0, 0, 0, 197, 0, 0, 0, + 191, 192, 190, 195, 199, 198, 196, 194, 209, 208, + 210, 0, 225, 0, 221, 0, 0, 163, 150, 162, + 151, 118, 119, 120, 145, 121, 147, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 146, 133, + 134, 148, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 149, 0, 0, 161, 257, 164, 0, 165, + 0, 166, 160, 0, 253, 246, 244, 251, 252, 250, + 249, 255, 248, 247, 245, 254, 241, 0, 229, 0, + 0, 233, 0, 0, 237, 0, 0, 163, 155, 0, + 154, 0, 159, 173, 0, 350, 350, 351, 0, 348, + 0, 349, 0, 352, 264, 271, 270, 278, 266, 0, + 267, 0, 353, 0, 360, 268, 269, 85, 274, 272, + 357, 354, 359, 275, 0, 287, 0, 0, 0, 0, + 344, 0, 361, 286, 258, 301, 0, 0, 0, 288, + 0, 0, 276, 277, 0, 265, 273, 302, 303, 0, + 350, 0, 0, 352, 0, 345, 346, 0, 334, 358, + 0, 318, 319, 320, 321, 0, 314, 315, 316, 317, + 342, 343, 0, 0, 0, 0, 0, 306, 307, 308, + 262, 260, 222, 230, 226, 242, 218, 263, 0, 169, + 234, 238, 211, 200, 0, 0, 219, 0, 0, 0, + 0, 212, 0, 0, 0, 0, 0, 204, 202, 205, + 203, 201, 214, 213, 215, 0, 227, 0, 223, 0, + 261, 169, 0, 243, 258, 259, 0, 258, 0, 0, + 310, 0, 0, 0, 312, 0, 231, 0, 0, 235, + 0, 0, 239, 299, 0, 291, 300, 294, 0, 298, + 0, 258, 292, 0, 258, 0, 0, 311, 0, 0, + 0, 313, 0, 0, 0, 305, 0, 304, 85, 112, + 362, 0, 0, 117, 280, 283, 0, 118, 287, 121, + 147, 123, 124, 88, 128, 129, 82, 130, 286, 133, + 86, 89, 258, 83, 91, 136, 84, 138, 87, 140, + 141, 288, 143, 144, 149, 0, 114, 113, 116, 100, + 115, 99, 0, 109, 281, 279, 0, 0, 0, 352, + 0, 110, 157, 158, 163, 0, 156, 0, 322, 323, + 0, 350, 0, 0, 352, 0, 111, 0, 0, 0, + 325, 330, 328, 331, 0, 0, 329, 330, 0, 326, + 0, 327, 282, 333, 0, 282, 332, 0, 335, 336, + 0, 282, 337, 338, 0, 0, 339, 0, 0, 0, + 340, 341, 175, 174, 0, 0, 0, 309, 0, 0, + 0, 324, 296, 289, 0, 297, 293, 0, 295, 284, + 0, 285, 290, 0, 0, 352, 0, 347, 103, 0, + 0, 107, 94, 0, 96, 105, 0, 97, 106, 108, + 98, 104, 95, 0, 101, 179, 177, 181, 178, 176, + 180, 355, 6, 356, 4, 2, 75, 102, 0, 0, + 78, 80, 79, 37, 5, 0, 76, 0, 51, 50, + 49, 0, 0, 51, 0, 0, 0, 52, 0, 67, + 68, 0, 65, 0, 66, 41, 42, 43, 44, 46, + 47, 71, 45, 0, 51, 0, 0, 0, 0, 0, + 61, 0, 62, 0, 0, 32, 0, 0, 72, 33, + 0, 36, 34, 30, 0, 35, 31, 0, 63, 0, + 64, 157, 0, 69, 73, 0, 0, 0, 0, 157, + 282, 0, 70, 85, 118, 287, 121, 147, 123, 124, + 88, 128, 129, 130, 286, 133, 86, 89, 258, 91, + 136, 84, 138, 87, 140, 141, 288, 143, 144, 149, + 74, 0, 59, 53, 60, 54, 0, 0, 0, 0, + 56, 0, 57, 58, 55, 0, 0, 0, 0, 48, + 0, 38, 39, 0, 40, 8, 0, 0, 9, 0, + 11, 0, 10, 0, 1, 27, 15, 14, 26, 13, + 12, 29, 7, 0, 18, 0, 19, 0, 24, 25, + 0, 20, 21, 0, 22, 23, 16, 17, 363}; const short QQmlJSGrammar::goto_default [] = { - 7, 650, 211, 198, 209, 521, 509, 645, 658, 508, - 644, 648, 646, 654, 22, 651, 649, 647, 18, 520, - 571, 561, 568, 563, 548, 193, 197, 199, 204, 234, - 212, 231, 552, 622, 621, 203, 233, 26, 487, 486, - 359, 358, 9, 357, 360, 202, 480, 361, 109, 17, - 147, 24, 13, 146, 19, 25, 59, 23, 8, 28, - 27, 280, 15, 274, 10, 270, 12, 272, 11, 271, - 20, 278, 21, 279, 14, 273, 269, 310, 414, 275, - 276, 205, 195, 194, 208, 207, 230, 196, 364, 363, - 232, 471, 470, 332, 333, 473, 335, 472, 334, 427, - 431, 434, 430, 429, 449, 450, 200, 186, 201, 210, + 7, 654, 212, 199, 210, 524, 512, 649, 662, 511, + 648, 652, 650, 658, 22, 655, 653, 651, 18, 523, + 574, 564, 571, 566, 551, 194, 198, 200, 205, 236, + 213, 233, 555, 626, 625, 204, 235, 26, 490, 489, + 361, 360, 9, 359, 362, 203, 483, 363, 109, 17, + 148, 24, 13, 147, 19, 25, 59, 23, 8, 28, + 27, 282, 15, 276, 10, 272, 12, 274, 11, 273, + 20, 280, 21, 281, 14, 275, 271, 312, 417, 277, + 278, 206, 196, 195, 209, 208, 232, 197, 366, 365, + 234, 474, 473, 334, 335, 476, 337, 475, 336, 430, + 434, 437, 433, 432, 452, 453, 201, 187, 202, 211, 0}; const short QQmlJSGrammar::action_index [] = { - 246, 1285, 2768, 2768, 2666, 998, 98, 198, 86, -106, - 26, -15, -39, 234, -106, 314, 54, -106, -106, 714, - 89, 151, 212, 219, -106, -106, -106, 412, 279, 1285, - -106, -106, -106, 525, -106, -106, 2360, 1675, 1285, 1285, - 1285, -106, 902, 1285, -106, -106, -106, 1285, 1285, -106, - -106, -106, -106, -106, -106, 1285, -106, 1285, 1285, -106, - 1285, 1285, 103, 197, -106, -106, 1285, 1285, 1285, -106, - -106, -106, 214, 1285, 297, 1285, 1285, 1285, 1285, 392, - 1285, 1285, 1285, 1285, 1285, 1285, 213, 1285, 1285, 1285, - 96, 100, 101, 279, 279, 195, 190, 181, 402, 372, - 382, 1285, -10, 1285, 106, 2258, 1285, 1285, -106, -106, - -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, - -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, - -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, - -106, -106, 136, 1285, -106, -106, 65, 29, -106, 1285, - -106, -106, 1285, -106, -106, -106, -106, -106, -106, -106, - -106, -106, -106, -106, -106, -106, 1285, -46, 1285, 1285, - 30, 27, 1285, -106, 2258, 1285, 1285, -106, 130, -106, - -31, -106, -106, -16, 520, 520, 71, 21, -106, 421, - -106, 38, 2768, -106, -106, -106, -106, -106, 237, -106, - 520, -106, -52, -106, -106, -106, 23, -106, -106, -106, - 2768, -106, -106, 596, -106, 588, 141, 2666, 2, 1, - -1, 2972, 1285, -106, 13, 1285, 28, -106, -28, -30, - -106, -106, 435, -106, -106, -106, -106, 60, 520, 52, - 67, 2768, 64, -106, -106, 2666, -106, -106, 126, -106, - -106, -106, -106, 73, -106, -106, -106, -106, -106, -106, - -18, 34, 1285, 156, 168, -106, -106, -106, 1479, -106, - 79, 40, 48, -106, 312, 75, 42, 672, 80, 143, - 331, 279, 442, 1285, 316, 1285, 1285, 1285, 1285, 341, - 1285, 1285, 1285, 1285, 1285, 279, 360, 360, 196, 225, - 443, 357, 351, 1285, -4, 1285, 63, 1285, -106, 714, - 1285, -106, 1285, 102, 68, 1285, 62, 2666, -106, 1285, - 147, 2666, -106, 1285, 56, 1285, 1285, 91, 87, 1285, - -106, 81, 149, 74, -106, -106, 1285, -106, 439, 1285, - -106, -44, 1285, -42, 2666, -106, 1285, 153, 2666, -106, - 1285, 154, 2666, 10, 2666, -106, 0, -106, 15, -54, - 92, -106, -106, 2666, -50, 539, -7, 536, 121, 1285, - 2666, 5, -8, 445, 2462, 24, 902, 51, 50, 1384, - 2462, 47, 20, 46, 1285, 44, 19, 1285, 41, 1285, - 3, -5, 2564, -106, -106, -106, -106, -106, -106, 1285, - -106, -106, -106, 6, -17, 11, 2768, -9, -106, 249, - -106, 1285, -13, -106, 105, -106, -106, -12, 520, -41, - -20, 2768, -45, -106, 1285, 115, 12, -106, 36, -106, - 31, 122, 1285, -106, 58, 4, -106, -51, -106, 2666, - -106, 146, 2666, -106, 235, -106, -106, 140, 2666, 93, - -106, 84, 76, -106, 520, 17, 33, -106, -106, -106, - -106, 1285, 134, 2666, -106, 1285, 125, 2666, -106, 55, - -106, 163, -106, -106, 1285, -106, -106, 520, -106, -106, - 7, 45, 2768, 32, -106, -106, 120, 1773, -106, -106, - 1577, -106, -106, 1871, -106, -106, -106, -106, -106, -106, - 113, -106, -106, -106, -106, -106, -106, -106, -106, -106, - 2768, -106, -106, -106, 109, 35, 808, 206, 49, 61, - -106, -106, 229, -106, 203, 37, -106, -106, 611, 183, - -106, 124, 39, 389, -106, 97, -106, -106, 252, -106, - 2061, -106, -106, -106, -106, -106, -106, -106, -106, -106, - 269, -23, 611, 243, 180, 424, 232, -106, 16, -106, - 808, 162, -106, 22, 808, -106, -106, 1190, -106, -106, - -106, 1094, -106, -106, 248, -106, 2061, -106, 305, -24, - -106, -106, 215, 457, 18, 2156, 292, 2870, -11, -106, - 14, 599, 9, 528, 119, 1285, 2666, 8, 70, 514, - 72, 902, 95, 90, 1384, 85, 59, 77, 1285, 110, - 83, 1285, 104, 1285, 82, 78, -106, 205, -106, 236, - -106, 57, 25, 611, 167, 517, -106, 107, -106, -106, - -106, 1966, 808, 1675, 43, -106, 155, -106, -106, 53, - -106, -106, 808, 808, 108, 808, -106, 289, -106, 117, - -106, -106, 150, 157, -106, -106, -106, -106, -106, 364, - -106, 226, -106, 69, -106, -106, 432, -106, -106, 88, - -106, -106, -106, -106, -106, + 308, 1392, 2787, 2787, 2890, 1102, 71, 6, 103, -107, + 10, -35, -64, 287, -107, 310, 11, -107, -107, 815, + 30, 112, 183, 214, -107, -107, -107, 463, 203, 1392, + -107, -107, -107, 536, -107, -107, 2478, 1786, 1392, 1392, + 1392, -107, 1005, 1392, -107, -107, -107, 1392, 1392, -107, + -107, -107, -107, -107, -107, 1392, -107, 1392, 1392, -107, + 1392, 1392, 75, 204, -107, -107, 1392, 1392, 1392, -107, + -107, -107, 221, 1392, 306, 1392, 1392, 1392, 1392, 463, + 1392, 1392, 1392, 1392, 1392, 1392, 200, 1392, 1392, 1392, + 149, 145, 108, 231, 241, 295, 379, 379, 463, 463, + 463, 1392, -70, 1392, 4, 2375, 1392, 1392, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, 105, 1392, -107, -107, -5, -58, -107, + 1392, -107, -107, 1392, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, 1392, -44, 1392, + 1392, 5, 7, 1392, -107, 2375, 1392, 1392, -107, 134, + -107, -43, -107, -107, -16, 541, 541, 15, -36, -107, + 462, -107, -4, 2787, -107, -107, -107, -107, -107, 213, + -107, 449, -107, -20, -107, -107, -107, 31, -107, -107, + -107, 2787, -107, -107, 616, -107, 711, 144, 2890, 21, + 42, 43, 3096, -107, 1392, -107, 62, 1392, 101, -107, + 102, 99, -107, -107, 417, -107, -107, -107, -107, 93, + 441, 56, 92, 2787, 34, -107, -107, 2890, -107, -107, + 118, -107, -107, -107, -107, 125, -107, -107, -107, -107, + -107, -107, -14, 33, 1392, 137, 193, -107, -107, -107, + 1488, -107, 44, -1, -42, -107, 316, -8, -60, 718, + 97, 87, 368, 222, 359, 1392, 313, 1392, 1392, 1392, + 1392, 342, 1392, 1392, 1392, 1392, 1392, 271, 270, 263, + 262, 225, 346, 352, 362, 1392, -51, 1392, 29, 1392, + -107, 815, 1392, -107, 1392, 28, -22, 1392, -19, 2890, + -107, 1392, 160, 2890, -107, 1392, 0, 1392, 1392, 97, + 45, 1392, -107, 37, 142, 25, -107, -107, 1392, -107, + 541, 1392, -107, 9, 1392, 12, 2890, -107, 1392, 128, + 2890, -107, 1392, 124, 2890, 61, 2890, -107, 60, -107, + 67, 26, 73, -107, -107, 2890, 49, 544, 80, 556, + 114, 1392, 2890, 85, 58, 482, 2581, 64, 88, 1005, + 90, 94, 1588, 2581, 96, 70, 197, 1392, 100, 76, + 1392, 104, 1392, 82, 84, 2684, -107, -107, -107, -107, + -107, -107, 1392, -107, -107, -107, 95, 63, 91, 2787, + 53, -107, 217, -107, 1392, 50, -107, 120, -107, -107, + 40, 372, 8, 27, 2787, 3, -107, 1392, 141, 20, + -107, 46, -107, 41, 147, 1392, -107, 39, 36, -107, + -15, -107, 2890, -107, 297, 2890, -107, 175, -107, -107, + 187, 2890, 14, -107, -3, -2, -107, 459, -34, -6, + -107, -107, -107, -107, 1392, 139, 2890, -107, 1392, 132, + 2890, -107, 1, -107, 251, -107, -107, 1392, -107, -107, + 541, -107, -107, -48, -23, 2787, -47, -107, -107, 113, + 1984, -107, -107, 1885, -107, -107, 1687, -107, -107, -107, + -107, -107, -107, 107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, 2787, -107, -107, -107, 131, -50, 910, + 243, -45, -7, -107, -107, 232, -107, 206, -12, -107, + -107, 633, 189, -107, 198, 13, 385, -107, 153, -107, + -107, 184, -107, 2080, -107, -107, -107, -107, -107, -107, + -107, -107, -107, 208, 18, 633, 219, 129, 353, 292, + -107, 48, -107, 910, 122, -107, 81, 910, -107, -107, + 1296, -107, -107, -107, 1199, -107, -107, 224, -107, 2080, + -107, 311, 81, -107, -107, 205, 633, 98, 2176, 304, + 2993, 69, -107, 89, 613, 86, 597, 109, 1392, 2890, + 83, 55, 467, 52, 79, 804, 78, 77, 1588, 66, + 47, 59, 1392, 57, 32, 1392, 54, 1392, 38, 35, + -107, 255, -107, 228, -107, 51, 2, 524, 195, 532, + -107, 133, -107, -107, -107, 2272, 910, 1786, 17, -107, + 152, -107, -107, 16, -107, -107, 910, 910, 119, 910, + -107, 302, -107, 148, -107, -107, 143, 140, -107, -107, + -107, -107, -107, 369, -107, 249, -107, 111, -107, -107, + 364, -107, -107, 65, -107, -107, -107, -107, -107, - -111, 8, 65, 83, 84, 317, 1, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -77, - -111, -111, -111, -111, -111, -111, -111, -111, -111, 96, - -111, -111, -111, 2, -111, -111, -5, -28, 12, 106, - 95, -111, 61, 55, -111, -111, -111, 63, 70, -111, - -111, -111, -111, -111, -111, 54, -111, 172, 177, -111, - 180, 191, -111, -111, -111, -111, 197, 202, 203, -111, - -111, -111, -111, 210, -111, 209, 195, 109, 116, -111, - 146, 125, 126, 127, 135, 138, -111, 141, 144, 110, + -111, 55, 62, 77, 71, 279, -7, -111, -111, -111, + -111, -111, -111, -111, -111, -111, -111, -111, -111, -74, + -111, -111, -111, -111, -111, -111, -111, -111, -111, 70, + -111, -111, -111, -8, -111, -111, -6, -28, 12, 84, + 85, -111, 93, 100, -111, -111, -111, 101, 104, -111, + -111, -111, -111, -111, -111, 107, -111, 112, 118, -111, + 182, 184, -111, -111, -111, -111, 218, 215, 209, -111, + -111, -111, -111, 202, -111, 195, 193, 192, 191, -111, + 189, 183, 181, 175, 168, 155, -111, 170, 153, 150, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, 150, -111, 155, -111, 192, 4, -33, -111, -111, + -111, 151, -111, 142, -111, 172, 30, -4, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -9, -111, -111, -111, -111, -111, 7, - -111, -111, 40, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, 99, -111, 52, 31, - -111, -111, 30, -111, 253, 44, 87, -111, -111, -111, - -111, -111, -111, -111, 45, 86, -111, -111, -111, 41, - -111, -111, 5, -111, -111, -111, -111, -111, -111, -111, - 50, -111, -111, -111, -111, -111, -111, -111, -111, -111, - 154, -111, -111, 26, -111, 49, -111, 330, -111, 28, - -111, 248, 27, -111, -111, 124, 51, -111, -111, -111, - -111, -111, 46, -111, -111, -111, -111, -111, 196, -111, - -111, 185, -111, -111, -111, 194, -111, -111, -111, -111, + -111, -111, -111, -111, -2, -111, -111, -111, -111, -111, + 0, -111, -111, 9, -111, -111, -111, -111, -111, -111, + -111, -111, -111, -111, -111, -111, -111, 125, -111, 122, + 10, -111, -111, 22, -111, 236, 46, 127, -111, -111, + -111, -111, -111, -111, -111, 37, 124, -111, -111, -111, + 39, -111, -111, 42, -111, -111, -111, -111, -111, -111, + -111, 44, -111, -111, -111, -111, -111, -111, -111, -111, + -111, 94, -111, -111, 47, -111, 48, -111, 128, -111, + 50, -111, 91, -111, -3, -111, -111, 66, 53, -111, + -111, -111, -111, -111, 57, -111, -111, -111, -111, -111, + 79, -111, -111, 78, -111, -111, -111, 82, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, 15, -111, -111, -111, -111, -111, 74, -111, + -111, -111, -111, -111, 67, -111, -111, -111, -111, -111, + 61, -111, -111, -111, -111, -111, -111, -111, -111, -111, + -111, -111, -111, -111, 59, 258, -111, 259, 268, 269, + 272, -111, 60, 63, 73, 74, 75, -111, -111, -111, + -111, -111, -111, -111, -111, 252, -111, 242, -111, 233, + -111, -111, 232, -111, 87, -111, -111, 89, -111, 133, + -111, 51, -111, 135, -111, 231, -111, 223, 222, -111, + -111, 221, -111, -111, -111, -111, -111, -111, 219, -111, + 92, 102, -111, -111, 110, -111, 171, -111, 40, -111, + 173, -111, 38, -111, 176, -111, 179, -111, -111, -111, + -111, -111, -111, -111, -111, 180, -111, 19, -111, 18, + -111, 145, 185, -111, -111, 17, 166, -111, -111, 65, + -111, -111, 29, 177, -111, -111, -111, 25, -111, 5, + 159, -111, 164, -111, -111, 207, -111, -111, -111, -111, + -111, -111, -18, -111, -111, -111, -111, -111, -111, 212, + -111, -111, -111, -111, 216, -111, -111, -111, -111, -111, + -111, 213, -111, -111, 86, -111, -111, 16, -111, -111, + -111, -111, -111, -85, -111, 14, -111, -84, -111, -111, + -111, -111, 286, -111, -111, 287, -111, -111, -111, -111, + -111, 214, -94, -111, -111, -16, -111, -10, -111, -19, + -111, -111, -111, -111, 2, -111, 83, -111, 105, -111, + 81, -111, -111, -111, -111, -111, -111, -41, -111, -111, + 131, -111, -111, -111, -111, 76, -111, -111, -111, -111, + -35, -111, -111, 64, -111, -111, -29, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -16, 230, -111, 233, 269, 251, 265, -111, - 80, 81, 82, 88, 89, -111, -111, -111, -111, -111, - -111, -111, -111, 216, -111, 255, -111, 259, -111, -111, - 223, -111, 68, -111, -111, 217, -111, 236, -111, 24, - -111, 244, -111, 227, -111, 226, 257, -111, -111, 263, - -111, -111, -111, -111, -111, -111, 249, -111, 113, 163, - -111, -111, 212, -111, 173, -111, 48, -111, 211, -111, - 53, -111, 206, -111, 204, -111, -111, -111, -111, -111, - -111, -111, -111, 199, -111, 23, -111, 25, -111, 134, - 175, -111, -111, 37, 200, -111, 222, -111, -111, 57, - 56, -111, -111, -111, 124, -111, 32, 43, -111, 105, - -111, -111, 139, -111, -111, -111, -111, -111, -111, 38, - -111, -111, -111, -111, -111, -111, 75, -111, -111, -111, - -111, 71, -111, -111, -111, -111, -111, -111, 72, -111, - -111, 85, -111, -111, 59, -111, -111, -111, -111, -111, - -37, -111, 62, -111, -58, -111, -111, -111, -111, 286, - -111, -111, 250, -111, -111, -111, -111, -111, 153, -57, - -111, -111, 33, -111, 34, -111, 36, -111, -111, -111, - -111, 47, -111, 77, -111, 29, -111, 67, -111, -111, - -111, -111, -111, -111, 42, -111, -111, 214, -111, -111, - -111, -111, 229, -111, -111, -111, -111, 35, -111, -111, - 145, -111, -111, 3, -111, -111, -111, -111, -111, -111, + -111, -111, -111, 208, -111, -111, -111, -111, -111, 20, + -111, -111, -111, -111, -111, -111, -111, 13, -111, -111, + -111, 26, 15, -111, -111, -111, 32, -111, -111, -111, + -111, -111, -111, 329, -111, -111, -111, -111, -111, -111, + -111, -111, -111, -111, -111, 54, 56, -111, 58, -111, + -111, -111, -111, 68, -111, -111, -111, 72, -111, -111, + 330, -111, -111, -111, 327, -111, -111, -111, -111, 389, + -111, -111, 52, -111, -111, 31, 49, -111, 371, -111, + 134, 34, -111, -111, 43, -111, 41, -111, 108, 141, + -111, -111, 35, -111, -111, 97, -111, -111, 45, -111, + -111, -111, 36, -111, 21, 129, -111, 146, -111, -111, + -111, -111, -111, -1, -111, -111, -111, 11, -5, 7, + -111, -111, -111, -111, -111, 353, 311, 408, 4, -111, + -111, -111, -111, 1, -111, -111, 8, 6, 249, 248, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - 207, -111, -111, -111, -111, -111, 39, -111, -111, -111, - -111, -111, -111, -111, -24, -111, -111, -111, -12, -27, - -111, -111, -111, -14, -111, -111, -111, -111, -111, -111, - 333, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, 6, 22, -111, 20, -111, -111, -111, -111, - 159, -111, -111, -111, 246, -111, -111, 332, -111, -111, - -111, 436, -111, -111, -111, -111, 388, -111, -111, 18, - -111, -111, -6, 19, -111, 352, -111, 225, 14, -111, - -111, 13, -111, 11, -111, 167, 136, -111, -111, 10, - -111, 64, -111, -111, 9, -111, -111, -111, 124, -111, - 0, 69, -111, 60, -111, -111, -111, -111, -111, -10, - -111, -111, -111, -1, -11, -2, -111, -111, -111, -111, - -111, 370, 142, 315, -3, -111, -111, -111, -111, 17, - -111, -111, -13, 21, 133, 221, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, 16, - -111, -111, -111, -111, -111, -111, -15, -111, -111, -111, - -111, -111, -111, -111, -111}; + -111, -111, -111, 3, -111, -111, -111, -111, -111, -111, + -14, -111, -111, -111, -111, -111, -111, -111, -111}; const short QQmlJSGrammar::action_info [] = { - -145, 398, 101, 244, 438, 402, 465, 245, 461, 567, - 423, 439, -126, 421, 553, -126, -145, 342, 344, 420, - 185, 245, 567, 392, 418, 585, 354, 73, 268, 181, - 245, 465, 166, 101, 172, 350, 432, 184, 268, 461, - 103, 432, 404, 405, 406, 428, 408, 413, -142, 424, - 560, -139, 448, -137, -115, 567, 424, -116, -134, 261, - 350, 448, 143, 432, 283, 624, 448, 481, 534, 103, - 262, 192, 474, 149, 529, 305, 567, 456, 482, 189, - 283, 191, 323, 307, -137, 627, 567, 484, 303, 151, - 617, 166, -115, 323, 329, 424, 238, -116, 336, 399, - 241, 524, -134, 312, 303, 346, 268, 73, 350, 448, - 143, -142, 240, 452, 465, 582, 448, -139, 461, 243, - 454, 143, 317, 143, 174, 0, 60, 305, 490, 315, - 665, 664, 435, 143, 257, 256, 60, 61, 143, 532, - 60, 60, 143, 175, 143, 64, 451, 61, 533, 671, - 670, 61, 61, 442, 143, 143, 65, 338, 537, 536, - 452, 143, 143, 564, 143, 174, 416, 415, 629, 628, - 564, 477, 174, 501, 0, 426, 491, 436, 673, 672, - 259, 258, 259, 258, 175, 467, 179, 252, 251, 642, - 643, 175, 144, 325, 463, 532, 530, 326, 674, 642, - 643, 168, 259, 258, 555, 169, 87, 321, 88, 66, - 339, 637, 530, 348, 352, 87, 264, 88, 565, 89, - 87, 87, 88, 88, 478, 476, 66, 174, 89, 267, - 265, 66, 525, 89, 89, 551, 631, 0, 87, 558, - 88, 619, 527, 143, 530, 143, 175, 0, 176, 105, - 87, 89, 88, 526, 67, 576, 0, 266, 527, 540, - 68, 0, 567, 89, 174, 530, 620, 618, 106, 526, - 107, 67, 530, 0, 0, 0, 67, 68, 527, 0, - 0, 527, 68, 175, 174, 411, 0, 668, 667, 526, - 527, 0, 526, 559, 557, 0, 446, 445, 236, 235, - 0, 526, 0, 175, 87, 411, 88, 174, 0, 577, - 575, 527, 0, 541, 539, 75, 76, 89, 527, 666, - 174, 0, 526, 632, 0, -102, 175, 0, 176, 526, - 285, 286, 75, 76, 285, 286, 661, 0, -102, 175, - 0, 176, 77, 78, 6, 5, 4, 1, 3, 2, - 662, 660, 0, 0, 290, 291, 0, 287, 288, 77, - 78, 287, 288, 292, 290, 291, 293, 0, 294, 0, - 0, 0, 0, 292, 290, 291, 293, 0, 294, 0, - 290, 291, 659, 292, 0, 87, 293, 88, 294, 292, - 0, 0, 293, 35, 294, 80, 81, 0, 89, 0, - 0, 0, 0, 82, 83, 80, 81, 84, 0, 85, - 0, 0, 0, 82, 83, 80, 81, 84, 35, 85, - 0, 0, 0, 82, 83, 80, 81, 84, 0, 85, - 49, 52, 50, 82, 83, 80, 81, 84, 0, 85, - 0, 0, 0, 82, 83, 0, 0, 84, 0, 85, - 35, 0, 0, 35, 0, 49, 52, 50, 46, 34, - 51, 35, 0, 0, 35, 0, 290, 291, 35, 0, - 0, 35, 532, 0, 35, 292, 0, 0, 293, 0, - 294, 184, 0, 46, 34, 51, 35, 49, 52, 50, + 309, 314, 152, 150, 101, 73, 678, 167, 487, 103, + 485, 73, 484, 101, 173, 103, 527, 182, 477, 144, + 186, 585, 621, 190, 192, 532, 459, 451, 307, 193, + 285, 451, 167, 457, 455, 246, 144, 307, 247, 317, + 441, 319, 537, 442, 435, 285, 435, 305, 305, 570, + 570, 435, 331, 431, 338, 556, 348, 270, 426, 628, + 424, -142, 631, 263, -139, 451, -137, 247, 423, 264, + 344, 468, 346, -115, 464, 395, 421, 356, 185, 352, + 402, 401, 563, 427, -116, -134, -146, -145, 352, 245, + -126, 270, -126, -145, 270, -146, 247, -134, 427, 325, + 352, -116, 570, -115, 405, 588, 427, -139, 411, 451, + 416, -142, 0, 144, 570, 144, 242, 64, 464, 0, + 468, 493, 0, 408, 409, 243, 675, 674, 65, 240, + 567, 407, 144, 0, 451, 468, 144, 327, 464, 0, + 144, 328, 144, 60, 535, 144, 175, 144, 60, 144, + 340, 0, 0, 558, 61, 175, 0, 438, 175, 61, + 567, 145, 169, 646, 647, 176, 170, 504, 144, 494, + 261, 260, 669, 668, 176, 261, 260, 176, 568, 254, + 253, 419, 418, 144, 354, 60, 259, 258, 350, 60, + 180, 543, 470, 454, 633, 632, 61, 266, 175, 466, + 61, 429, 439, 341, -137, 261, 260, 455, 641, 677, + 676, 646, 647, 535, 540, 539, 66, 176, 533, 177, + 323, 144, 536, 175, 533, 87, 66, 88, 87, 0, + 88, 579, 175, 66, 533, 528, 449, 448, 89, 635, + 0, 89, 176, 0, 414, 544, 542, 87, 533, 88, + 87, 176, 88, 414, 269, 267, 87, 533, 88, 480, + 89, 67, 0, 89, 530, 570, 87, 68, 88, 89, + 530, 67, 554, 0, 238, 237, 529, 68, 67, 89, + 530, 530, 529, 268, 68, 580, 578, 87, 87, 88, + 88, 623, 529, 529, 530, 87, 87, 88, 88, 561, + 89, 89, 105, 530, 445, 144, 529, 0, 89, 89, + 672, 671, 481, 479, 0, 529, 624, 622, 530, 175, + 87, 106, 88, 107, 75, 76, 175, 636, 75, 76, + 529, 287, 288, 89, 287, 288, 0, -102, 176, 0, + 177, 0, 0, 670, -102, 176, 0, 177, 0, 665, + 0, 77, 78, 562, 560, 77, 78, 0, 289, 290, + 0, 289, 290, 666, 664, 292, 293, 0, 0, 292, + 293, 0, 0, 0, 294, 292, 293, 295, 294, 296, + 0, 295, 35, 296, 294, 292, 293, 295, 35, 296, + 0, 292, 293, 35, 294, 0, 663, 295, 35, 296, + 294, 35, 0, 295, 87, 296, 88, 6, 5, 4, + 1, 3, 2, 0, 35, 0, 0, 89, 0, 49, + 52, 50, 0, 0, 0, 49, 52, 50, 0, 0, + 49, 52, 50, 0, 0, 49, 52, 50, 49, 52, + 50, 0, 0, 0, 0, 0, 35, 0, 46, 34, + 51, 49, 52, 50, 46, 34, 51, 0, 0, 46, + 34, 51, 0, 0, 46, 34, 51, 46, 34, 51, + 35, 0, 0, 0, 0, 0, 0, 0, 35, 0, + 46, 34, 51, 49, 52, 50, 80, 81, 35, 0, + 0, 35, 0, 0, 82, 83, 35, 0, 84, 0, + 85, 0, 0, 185, 0, 0, 0, 49, 52, 50, + 0, 35, 46, 34, 51, 49, 52, 50, 185, 0, + 0, 0, 0, 0, 0, 49, 52, 50, 49, 52, + 50, 0, 0, 49, 52, 50, 46, 34, 51, 535, + 0, 0, 0, 0, 46, 34, 51, 535, 49, 52, + 50, 0, 0, 35, 46, 34, 51, 46, 34, 51, + 0, 35, 46, 34, 51, 35, 0, 0, 0, 0, + 35, 0, 185, 35, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 49, 52, 50, 0, 0, 0, 0, 0, 49, 52, - 50, 49, 52, 50, 0, 49, 52, 50, 49, 52, - 50, 49, 52, 50, 0, 46, 34, 51, 46, 34, - 51, 0, 0, 49, 52, 50, 46, 34, 51, 46, - 34, 51, 532, 46, 34, 51, 46, 34, 51, 46, - 34, 51, 0, 35, 0, 0, 35, 0, 0, 35, - 184, 46, 34, 51, 35, 0, 0, 35, 0, 0, - 0, 184, 0, 0, 0, 35, 0, 0, 35, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 49, 52, 50, 49, 52, 50, 49, 52, 50, 255, - 254, 49, 52, 50, 49, 52, 50, 255, 254, 0, - 250, 249, 49, 52, 50, 49, 52, 50, 46, 34, - 51, 46, 34, 51, 46, 34, 51, 35, 0, 46, - 34, 51, 46, 34, 51, 35, 532, 0, 35, 0, - 46, 34, 51, 46, 34, 51, 0, 0, 0, 0, - 35, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 254, 0, 0, 0, 49, 52, 50, 250, 249, 0, - 250, 249, 49, 52, 50, 49, 52, 50, 0, 0, - 0, 0, 0, 0, 0, 153, 0, 49, 52, 50, - 0, 0, 46, 34, 51, 154, 0, 0, 0, 155, - 46, 34, 51, 46, 34, 51, 0, 0, 156, 0, - 157, 0, 0, 319, 0, 46, 34, 51, 0, 0, - 0, 158, 0, 159, 64, 0, 0, 153, 0, 0, - 0, 160, 0, 0, 161, 65, 0, 154, 0, 0, - 162, 155, 0, 0, 0, 0, 163, 0, 0, 0, - 156, 0, 157, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 164, 158, 0, 159, 64, 0, 0, 0, - 0, 0, 0, 160, 0, 0, 161, 65, 0, 0, - 0, 0, 162, 0, 0, 0, 0, 0, 163, 0, + 50, 0, 49, 52, 50, 252, 251, 49, 52, 50, + 49, 52, 50, 0, 0, 0, 0, 257, 256, 46, + 34, 51, 49, 52, 50, 0, 35, 46, 34, 51, + 0, 46, 34, 51, 0, 0, 46, 34, 51, 46, + 34, 51, 35, 0, 0, 35, 0, 0, 535, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 257, 256, + 0, 0, 35, 49, 52, 50, 0, 0, 0, 0, + 0, 0, 0, 0, 252, 251, 0, 252, 251, 49, + 52, 50, 49, 52, 50, 0, 0, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 49, + 52, 50, 0, 0, 0, 0, 0, 0, 46, 34, + 51, 46, 34, 51, 0, 0, 0, 0, 0, 0, + 0, 154, 0, 0, 0, 0, 0, 0, 46, 34, + 51, 155, 0, 0, 0, 156, 0, 0, 0, 0, + 35, 0, 0, 0, 157, 0, 158, 0, 0, 321, + 0, 0, 0, 0, 0, 0, 0, 159, 0, 160, + 64, 0, 0, 0, 0, 0, 0, 161, 0, 0, + 162, 65, 257, 256, 0, 0, 163, 49, 52, 50, + 0, 0, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 165, 0, + 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, + 0, 0, 0, 0, 0, 0, 30, 31, 154, 0, + 0, 0, 0, 0, 0, 0, 33, 0, 155, 0, + 0, 0, 156, 35, 0, 0, 0, 36, 37, 0, + 38, 157, 0, 158, 0, 0, 0, 42, 0, 0, + 0, 45, 0, 0, 159, 0, 160, 64, 0, 0, + 0, 0, 0, 0, 161, 0, 0, 162, 65, 53, + 49, 52, 50, 163, 54, 0, 0, 0, 0, 164, + 0, 0, 0, 0, 0, 44, 56, 32, 0, 0, + 0, 0, 41, 0, 0, 165, 0, 0, 0, 46, + 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 164, 0, 0, 0, 0, 0, + 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, + 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, + 0, 0, 0, 519, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, + 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, + 0, 38, 0, 0, 0, 0, 0, 0, 42, 0, + 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, - 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, - 0, 36, 37, 0, 38, 0, 0, 0, 0, 0, - 0, 516, 0, 0, 0, 45, 0, 0, 0, 0, + 53, 49, 52, 50, 0, 54, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 44, 56, 32, 0, + 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, + 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 518, 0, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, + 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, + 0, 0, 0, 0, 0, 519, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, - 56, 32, 0, 0, 0, 41, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 53, 520, 522, + 521, 0, 54, 0, 0, 0, 0, 229, 0, 0, + 0, 0, 0, 44, 56, 32, 215, 223, 0, 0, + 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 518, + 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 220, 0, 0, 0, 0, 0, 0, 35, 0, + 0, 0, 36, 37, 0, 38, 0, 0, 0, 0, + 0, 0, 519, 0, 0, 0, 45, 0, 0, 0, + 0, 0, 0, 0, 575, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 520, 522, 521, 0, 54, + 0, 0, 0, 0, 229, 0, 0, 0, 0, 0, + 44, 56, 32, 215, 223, 0, 0, 41, 0, 0, + 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 518, 0, 30, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, + 37, 0, 38, 0, 0, 0, 0, 0, 0, 519, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 572, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 520, 522, 521, 0, 54, 0, 0, 0, + 0, 229, 0, 0, 0, 0, 0, 44, 56, 32, + 215, 223, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, - 0, 0, 0, 0, 0, 42, 0, 0, 0, 45, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, + 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, - 50, 0, 54, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 44, 56, 32, 0, 0, 0, 41, - 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 515, 0, + 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, + 0, 0, 0, 44, 56, 32, 0, 0, 0, 0, + 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, - 219, 0, 0, 0, 0, 0, 0, 35, 0, 0, - 0, 36, 37, 0, 38, 0, 0, 0, 0, 0, - 0, 516, 0, 0, 0, 45, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 53, 517, 519, 518, 0, 54, 0, - 0, 0, 0, 227, 0, 0, 0, 0, 0, 44, - 56, 32, 214, 0, 0, 41, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 515, 0, 30, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 219, 0, 0, 0, - 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, - 38, 0, 0, 0, 0, 0, 0, 516, 0, 0, - 0, 45, 0, 0, 0, 0, 0, 0, 0, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, - 517, 519, 518, 0, 54, 0, 0, 0, 0, 227, - 0, 0, 0, 0, 0, 44, 56, 32, 214, 0, - 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, - 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 515, 0, 30, 31, 0, 0, 0, 0, 0, 0, - 0, 0, 219, 0, 0, 0, 0, 0, 0, 35, - 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, - 0, 0, 0, 516, 0, 0, 0, 45, 0, 0, - 0, 0, 0, 0, 0, 569, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 53, 517, 519, 518, 0, - 54, 0, 0, 0, 0, 227, 0, 0, 0, 0, - 0, 44, 56, 32, 214, 0, 0, 41, 0, 0, - 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, - 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, - 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, - 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, - 0, 0, 45, 0, 0, 0, 47, 0, 48, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, - 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, - 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, - 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, - 0, -135, 0, 0, 0, 29, 30, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, - 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, - 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, - 0, 45, 0, 0, 0, 47, 0, 48, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, - 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, - 58, 0, 0, 0, 0, 44, 56, 32, 0, 0, - 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, - 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, - 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, - 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, - 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, - 0, 55, 0, 57, 282, 58, 0, 0, 0, 0, - 44, 56, 32, 0, 0, 0, 41, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, + 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, + 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, + 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, + 55, 0, 57, 284, 58, 0, 0, 0, 0, 44, + 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, + 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -135, 0, 0, 0, 29, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, + 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, + 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, + 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, + 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, + 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 488, 0, 0, 29, 30, + 0, 0, 0, 0, 0, 499, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, - 48, 0, 0, 494, 0, 0, 0, 0, 0, 0, + 48, 0, 0, 500, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, - 32, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 491, 0, 0, 29, 30, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, + 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, + 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, + 0, 0, 492, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, + 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, + 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 488, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 491, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, 0, - 0, 489, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 497, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, - 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, - 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 496, 0, 0, 29, 30, 31, 0, 0, 0, - 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, - 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, - 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, - 45, 0, 0, 0, 47, 0, 48, 0, 0, 499, - 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, - 52, 50, 0, 54, 0, 55, 0, 57, 0, 58, - 0, 0, 0, 0, 44, 56, 32, 0, 0, 0, - 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 496, - 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, - 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, - 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, - 0, 0, 47, 0, 48, 0, 0, 497, 0, 0, - 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, - 0, 54, 0, 55, 0, 57, 0, 58, 0, 0, - 0, 0, 44, 56, 32, 0, 0, 0, 41, 0, - 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 220, 0, 0, 587, - 633, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, - 0, 53, 49, 52, 50, 224, 54, 0, 55, 226, - 57, 0, 58, 0, 229, 0, 0, 44, 56, 32, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, - 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 35, 220, 0, 0, 221, 37, 0, 38, 0, 0, - 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, - 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, - 0, 0, 223, 0, 0, 0, 53, 49, 52, 50, - 224, 54, 0, 55, 226, 57, 0, 58, 0, 229, - 0, 0, 44, 56, 32, 0, 0, 0, 41, 0, + 0, 0, 499, 0, 0, 29, 30, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, + 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, + 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, + 0, 45, 0, 0, 0, 47, 0, 48, 0, 0, + 502, 0, 0, 0, 0, 0, 0, 0, 0, 53, + 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, + 58, 0, 0, 0, 0, 44, 56, 32, 0, 0, + 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, + 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, + 221, 0, 0, 222, 37, 0, 38, 0, 0, 0, + 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, + 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, + 0, 225, 0, 0, 0, 53, 49, 52, 50, 226, + 54, 0, 55, 228, 57, 0, 58, 0, 231, 0, + 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 220, 0, 0, 587, + 0, 0, 0, 0, 0, 35, 221, 0, 0, 590, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, - 0, 53, 49, 52, 50, 224, 54, 0, 55, 226, - 57, 0, 58, 0, 229, 0, 0, 44, 56, 32, - 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 225, 0, 0, + 0, 53, 49, 52, 50, 226, 54, 0, 55, 228, + 57, 0, 58, 0, 231, 0, 0, 44, 56, 32, + 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, + 0, 35, 221, 0, 0, 590, 637, 0, 38, 0, + 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, + 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, + 0, 0, 0, 225, 0, 0, 0, 53, 49, 52, + 50, 226, 54, 0, 55, 228, 57, 0, 58, 0, + 231, 0, 0, 44, 56, 32, 0, 0, 0, 0, + 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, + 112, 113, 0, 0, 115, 117, 118, 0, 0, 119, + 0, 120, 0, 0, 0, 122, 123, 124, 0, 0, + 0, 0, 0, 0, 35, 125, 126, 127, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 129, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 132, 0, 0, 0, 0, 0, + 0, 49, 52, 50, 133, 134, 135, 0, 137, 138, + 139, 140, 141, 142, 0, 0, 130, 136, 121, 114, + 128, 116, 131, 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 112, 113, 0, 0, 115, 117, 118, 0, 0, 119, 0, 120, 0, 0, 0, 122, 123, 124, 0, 0, 0, 0, 0, 0, 35, 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 131, 0, 0, - 0, 0, 0, 0, 49, 52, 50, 132, 133, 134, - 0, 136, 137, 138, 139, 140, 141, 0, 0, 129, - 135, 121, 114, 116, 130, 0, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 112, 113, 0, 0, 115, - 117, 118, 0, 0, 119, 0, 120, 0, 0, 0, - 122, 123, 124, 0, 0, 0, 0, 0, 0, 35, - 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 128, 0, 0, 0, 395, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, - 0, 0, 0, 0, 0, 397, 49, 52, 50, 132, - 133, 134, 0, 136, 137, 138, 139, 140, 141, 0, - 0, 129, 135, 121, 114, 116, 130, 0, 0, 0, - 0, 0, 0, 0, 46, 374, 380, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 112, 113, 0, - 0, 115, 117, 118, 0, 0, 119, 0, 120, 0, - 0, 0, 122, 123, 124, 0, 0, 0, 0, 0, - 0, 35, 125, 126, 127, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 128, 0, 0, 0, 395, + 0, 129, 0, 0, 0, 398, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 132, 0, 0, + 0, 0, 0, 400, 49, 52, 50, 133, 134, 135, + 0, 137, 138, 139, 140, 141, 142, 0, 0, 130, + 136, 121, 114, 128, 116, 131, 0, 0, 0, 0, + 0, 0, 0, 46, 376, 383, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 111, 112, 113, 0, 0, + 115, 117, 118, 0, 0, 119, 0, 120, 0, 0, + 0, 122, 123, 124, 0, 0, 0, 0, 0, 0, + 35, 125, 126, 127, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 129, 0, 0, 0, 398, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 131, 0, 0, 0, 0, 0, 397, 49, 52, - 50, 132, 133, 134, 0, 136, 137, 138, 139, 140, - 141, 0, 0, 129, 135, 121, 114, 116, 130, 0, + 132, 0, 0, 0, 0, 0, 400, 49, 52, 50, + 133, 134, 135, 0, 137, 138, 139, 140, 141, 142, + 0, 0, 130, 136, 121, 114, 128, 116, 131, 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 112, 113, 0, 0, 115, 117, 118, 0, 0, 119, 0, 120, 0, 0, 0, 122, 123, 124, 0, 0, 0, 0, 0, 0, 35, 125, 126, 127, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, - 0, 395, 0, 0, 0, 0, 0, 0, 0, 396, - 0, 0, 0, 131, 0, 0, 0, 0, 0, 397, - 49, 52, 50, 132, 133, 134, 0, 136, 137, 138, - 139, 140, 141, 0, 0, 129, 135, 121, 114, 116, - 130, 0, 0, 0, 0, 0, 0, 0, 46, 374, - 380, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 213, 0, 0, 0, 0, 215, 0, 29, 30, 31, - 217, 0, 0, 0, 0, 0, 0, 218, 33, 0, - 0, 0, 0, 0, 0, 35, 220, 0, 0, 221, - 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 0, 0, 0, 222, 0, 223, 0, 0, - 0, 53, 49, 52, 50, 224, 54, 225, 55, 226, - 57, 227, 58, 228, 229, 0, 0, 44, 56, 32, - 214, 216, 0, 41, 0, 0, 0, 0, 0, 0, - 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 213, 0, 0, 0, 0, 215, 0, 29, - 30, 31, 217, 0, 0, 0, 0, 0, 0, 218, - 219, 0, 0, 0, 0, 0, 0, 35, 220, 0, - 0, 221, 37, 0, 38, 0, 0, 0, 39, 0, - 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, - 0, 48, 0, 0, 0, 0, 0, 222, 0, 223, - 0, 0, 0, 53, 49, 52, 50, 224, 54, 225, - 55, 226, 57, 227, 58, 228, 229, 0, 0, 44, - 56, 32, 214, 216, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 129, 0, 0, + 0, 398, 0, 0, 0, 0, 0, 0, 0, 399, + 0, 0, 0, 132, 0, 0, 0, 0, 0, 400, + 49, 52, 50, 133, 134, 135, 0, 137, 138, 139, + 140, 141, 142, 0, 0, 130, 136, 121, 114, 128, + 116, 131, 0, 0, 0, 0, 0, 0, 0, 46, + 376, 383, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 214, 0, 0, 0, 0, 216, 0, 29, 30, + 31, 218, 0, 0, 0, 0, 0, 0, 219, 220, + 0, 0, 0, 0, 0, 0, 35, 221, 0, 0, + 222, 37, 0, 38, 0, 0, 0, 39, 0, 40, + 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, + 48, 0, 0, 0, 0, 0, 224, 0, 225, 0, + 0, 0, 53, 49, 52, 50, 226, 54, 227, 55, + 228, 57, 229, 58, 230, 231, 0, 0, 44, 56, + 32, 215, 223, 217, 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 591, 112, 113, 0, 0, 593, - 117, 595, 30, 31, 596, 0, 120, 0, 0, 0, - 122, 598, 599, 0, 0, 0, 0, 0, 0, 35, - 600, 126, 127, 221, 37, 0, 38, 0, 0, 0, - 39, 0, 40, 601, 43, 0, 0, 603, 0, 0, - 0, 47, 0, 48, 0, 0, 0, 0, 0, 604, - 0, 223, 0, 0, 0, 605, 49, 52, 50, 606, - 607, 608, 55, 610, 611, 612, 613, 614, 615, 0, - 0, 602, 609, 597, 592, 594, 130, 41, 0, 0, - 0, 0, 0, 0, 46, 374, 380, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 365, 112, 113, 0, - 0, 367, 117, 369, 30, 31, 370, 0, 120, 0, - 0, 0, 122, 372, 373, 0, 0, 0, 0, 0, - 0, 35, 375, 126, 127, 221, 37, 0, 38, 0, - 0, 0, 39, 0, 40, 376, 43, 0, 0, 378, - 0, 0, 0, 47, 0, 48, 0, -281, 0, 0, - 0, 379, 0, 223, 0, 0, 0, 381, 49, 52, - 50, 382, 383, 384, 55, 386, 387, 388, 389, 390, - 391, 0, 0, 377, 385, 371, 366, 368, 130, 41, - 0, 0, 0, 0, 0, 0, 46, 374, 380, 0, - 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 214, 0, 0, 0, 0, 216, + 0, 29, 30, 31, 218, 0, 0, 0, 0, 0, + 0, 219, 33, 0, 0, 0, 0, 0, 0, 35, + 221, 0, 0, 222, 37, 0, 38, 0, 0, 0, + 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, + 0, 47, 0, 48, 0, 0, 0, 0, 0, 224, + 0, 225, 0, 0, 0, 53, 49, 52, 50, 226, + 54, 227, 55, 228, 57, 229, 58, 230, 231, 0, + 0, 44, 56, 32, 215, 223, 217, 0, 41, 0, + 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 594, 112, 113, + 0, 0, 596, 117, 598, 30, 31, 599, 0, 120, + 0, 0, 0, 122, 601, 602, 0, 0, 0, 0, + 0, 0, 35, 603, 126, 127, 222, 37, 0, 38, + 0, 0, 0, 39, 0, 40, 605, 43, 0, 0, + 607, 0, 0, 0, 47, 0, 48, 0, 0, 0, + 0, 0, 608, 0, 225, 0, 0, 0, 609, 49, + 52, 50, 610, 611, 612, 55, 614, 615, 616, 617, + 618, 619, 0, 0, 606, 613, 600, 595, 604, 597, + 131, 41, 0, 0, 0, 0, 0, 0, 46, 376, + 383, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 367, 112, 113, 0, 0, 369, 117, 371, 30, 31, + 372, 0, 120, 0, 0, 0, 122, 374, 375, 0, + 0, 0, 0, 0, 0, 35, 377, 126, 127, 222, + 37, 0, 38, 0, 0, 0, 39, 0, 40, 379, + 43, 0, 0, 381, 0, 0, 0, 47, 0, 48, + 0, -282, 0, 0, 0, 382, 0, 225, 0, 0, + 0, 384, 49, 52, 50, 385, 386, 387, 55, 389, + 390, 391, 392, 393, 394, 0, 0, 380, 388, 373, + 368, 378, 370, 131, 41, 0, 0, 0, 0, 0, + 0, 46, 376, 383, 0, 0, 0, 0, 0, 0, + 0, 0, 0, - 152, 652, 331, 669, 535, 531, 538, 142, 528, 148, - 641, 16, 313, 393, 485, 500, 626, 630, 263, 638, - 183, 625, 623, 206, 574, 447, 583, 320, 183, 253, - 313, 248, 466, 145, 663, 653, 616, 584, 556, 640, - 581, 248, 437, 253, 248, 495, 183, 178, 453, 150, - 462, 347, 455, 550, 554, 183, 351, 447, 458, 190, - 313, 457, 425, 188, 469, 441, 433, 253, 237, 468, - 0, 313, 173, 171, 393, 409, 447, 498, 409, 464, - 400, 0, 165, 206, 475, 206, 512, 511, 0, 0, - 188, 0, 0, 206, 0, 206, 0, 62, 0, 459, - 417, 206, 206, 206, 188, 0, 62, 0, 62, 62, - 507, 504, 410, 148, 62, 410, 460, 62, 419, 505, - 407, 412, 170, 62, 62, 459, 506, 444, 277, 148, - 422, 331, 187, 281, 62, 62, 62, 180, 260, 295, - 296, 297, 62, 62, 656, 655, 314, 298, 299, 62, - 62, 503, 182, 62, 206, 362, 514, 393, 247, 62, - 62, 460, 502, 62, 62, 639, 313, 167, 92, 99, - 62, 206, 206, 514, 510, 345, 100, 260, 562, 62, - 62, 62, 394, 493, 93, 94, 95, 492, 62, 62, - 182, 206, 62, 206, 96, 62, 246, 97, 62, 90, - 62, 401, 91, 206, 62, 86, 355, 340, 353, 62, - 108, 247, 206, 349, 188, 313, 102, 206, 393, 104, - 313, 62, 206, 182, 206, 206, 62, 362, 459, 206, - 242, 62, 469, 460, 62, 514, 409, 63, 318, 110, - 657, 341, 239, 590, 403, 62, 322, 206, 72, 62, - 362, 62, 362, 69, 206, 98, 62, 62, 70, 71, - 514, 0, 206, 62, 62, 566, 356, 0, 206, 79, - 62, 108, 74, 410, 483, 281, 0, 309, 0, 0, - 62, 62, 281, 304, 62, 281, 281, 62, 362, 281, - 343, 0, 281, 284, 289, 316, 324, 327, 0, 311, - 110, 177, 0, 309, 206, 62, 479, 0, 281, 62, - 281, 309, 301, 309, 281, 0, 281, 309, 281, 62, - 306, 0, 281, 62, 281, 337, 302, 0, 281, 578, - 300, 514, 260, 328, 562, 308, 636, 570, 443, 330, - 522, 0, 0, 0, 0, 0, 514, 0, 206, 0, - 0, 0, 513, 523, 0, 522, 0, 485, 542, 543, - 544, 545, 549, 546, 547, 0, 586, 513, 523, 0, - 0, 0, 0, 0, 440, 588, 589, 542, 543, 544, - 545, 549, 546, 547, 586, 0, 0, 0, 0, 0, - 0, 0, 0, 634, 635, 542, 543, 544, 545, 549, - 546, 547, 578, 0, 0, 0, 0, 0, 0, 0, - 0, 579, 580, 542, 543, 544, 545, 549, 546, 547, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 573, 0, 0, 0, 0, 0, 0, 0, 0, - 514, 0, 0, 0, 0, 0, 0, 0, 0, 522, + 315, 478, 645, 153, 673, 465, 460, 501, 458, 461, + 184, 456, 396, 498, 488, 503, 440, 444, 436, 428, + 657, 667, 656, 644, 403, 630, 642, 629, 447, 634, + 450, 627, 315, 143, 553, 184, 255, 250, 149, 447, + 146, 353, 151, 349, 541, 531, 450, 534, 315, 179, + 538, 166, 172, 184, 322, 189, 620, 191, 16, 255, + 207, 250, 239, 586, 174, 250, 255, 587, 184, 447, + 265, 0, 577, 515, 584, 472, 559, 333, 450, 412, + 207, 514, 517, 471, 248, 467, 517, 565, 557, 207, + 315, 569, 315, 364, 207, 207, 207, 189, 249, 207, + 207, 207, 496, 0, 207, 315, 495, 412, 469, 358, + 333, 412, 207, 315, 62, 279, 413, 62, 0, 297, + 283, 486, 298, 244, 62, 241, 183, 62, 62, 62, + 262, 425, 299, 300, 301, 320, 364, 324, 62, 62, + 505, 506, 189, 262, 413, 0, 207, 0, 413, 472, + 0, 207, 593, 207, 62, 62, 507, 508, 62, 207, + 509, 62, 62, 510, 183, 316, 62, 318, 462, 149, + 188, 513, 62, 347, 463, 351, 62, 181, 355, 62, + 343, 357, 404, 62, 396, 462, 342, 262, 345, 207, + 108, 207, 171, 168, 207, 396, 62, 207, 207, 62, + 62, 183, 463, 207, 62, 62, 104, 62, 92, 62, + 406, 91, 249, 62, 97, 462, 364, 102, 62, 110, + 463, 420, 62, 482, 62, 396, 207, 96, 90, 62, + 207, 189, 207, 0, 95, 62, 62, 62, 62, 63, + 94, 72, 93, 62, 0, 62, 62, 62, 86, 62, + 397, 100, 99, 98, 108, 79, 62, 410, 149, 422, + 660, 659, 517, 62, 74, 71, 415, 661, 0, 62, + 0, 70, 62, 311, 69, 311, 311, 62, 283, 0, + 283, 283, 283, 110, 178, 62, 311, 311, 364, 364, + 283, 283, 283, 517, 329, 339, 62, 332, 330, 0, + 326, 283, 525, 0, 207, 207, 62, 308, 313, 310, + 0, 283, 62, 62, 516, 526, 0, 283, 283, 306, + 291, 286, 62, 62, 0, 517, 62, 283, 283, 302, + 303, 283, 576, 304, 643, 573, 0, 0, 0, 0, + 0, 517, 0, 0, 517, 0, 0, 0, 0, 0, + 525, 0, 0, 525, 545, 546, 547, 548, 552, 549, + 550, 0, 516, 526, 0, 516, 526, 589, 0, 0, + 0, 0, 0, 0, 443, 446, 638, 639, 545, 546, + 547, 548, 552, 549, 550, 589, 0, 0, 0, 0, + 0, 0, 0, 0, 591, 592, 545, 546, 547, 548, + 552, 549, 550, 581, 0, 0, 0, 0, 0, 0, + 0, 0, 582, 583, 545, 546, 547, 548, 552, 549, + 550, 0, 581, 0, 0, 0, 0, 565, 0, 640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 513, 523, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 488, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0}; + 0, 0, 0, 0, 0, 0, 0, 0, 0}; const short QQmlJSGrammar::action_check [] = { - 7, 55, 48, 55, 55, 55, 36, 7, 36, 33, - 55, 7, 7, 33, 37, 7, 7, 61, 60, 60, - 36, 7, 33, 8, 36, 7, 16, 1, 36, 60, - 7, 36, 2, 48, 7, 36, 5, 36, 36, 36, - 79, 5, 36, 60, 33, 33, 55, 60, 7, 36, - 34, 7, 33, 7, 7, 33, 36, 7, 7, 77, - 36, 33, 8, 5, 1, 8, 33, 60, 29, 79, - 36, 33, 17, 8, 37, 79, 33, 60, 33, 8, - 1, 60, 2, 8, 7, 60, 33, 55, 48, 60, - 29, 2, 7, 2, 7, 36, 36, 7, 17, 7, - 33, 66, 7, 61, 48, 31, 36, 1, 36, 33, - 8, 7, 60, 20, 36, 66, 33, 7, 36, 55, - 36, 8, 60, 8, 15, -1, 40, 79, 8, 61, - 61, 62, 10, 8, 61, 62, 40, 51, 8, 15, - 40, 40, 8, 34, 8, 42, 6, 51, 24, 61, - 62, 51, 51, 7, 8, 8, 53, 8, 61, 62, - 20, 8, 8, 8, 8, 15, 61, 62, 61, 62, - 8, 8, 15, 60, -1, 60, 56, 55, 61, 62, - 61, 62, 61, 62, 34, 60, 56, 61, 62, 91, - 92, 34, 56, 50, 60, 15, 29, 54, 0, 91, - 92, 50, 61, 62, 24, 54, 25, 60, 27, 12, - 61, 56, 29, 60, 60, 25, 60, 27, 56, 38, - 25, 25, 27, 27, 61, 62, 12, 15, 38, 61, - 62, 12, 29, 38, 38, 29, 7, -1, 25, 7, - 27, 36, 75, 8, 29, 8, 34, -1, 36, 15, - 25, 38, 27, 86, 57, 7, -1, 89, 75, 7, - 63, -1, 33, 38, 15, 29, 61, 62, 34, 86, - 36, 57, 29, -1, -1, -1, 57, 63, 75, -1, - -1, 75, 63, 34, 15, 36, -1, 61, 62, 86, - 75, -1, 86, 61, 62, -1, 61, 62, 61, 62, - -1, 86, -1, 34, 25, 36, 27, 15, -1, 61, - 62, 75, -1, 61, 62, 18, 19, 38, 75, 93, - 15, -1, 86, 94, -1, 33, 34, -1, 36, 86, - 18, 19, 18, 19, 18, 19, 47, -1, 33, 34, - -1, 36, 45, 46, 98, 99, 100, 101, 102, 103, - 61, 62, -1, -1, 23, 24, -1, 45, 46, 45, - 46, 45, 46, 32, 23, 24, 35, -1, 37, -1, - -1, -1, -1, 32, 23, 24, 35, -1, 37, -1, - 23, 24, 93, 32, -1, 25, 35, 27, 37, 32, - -1, -1, 35, 29, 37, 23, 24, -1, 38, -1, - -1, -1, -1, 31, 32, 23, 24, 35, -1, 37, - -1, -1, -1, 31, 32, 23, 24, 35, 29, 37, - -1, -1, -1, 31, 32, 23, 24, 35, -1, 37, - 66, 67, 68, 31, 32, 23, 24, 35, -1, 37, - -1, -1, -1, 31, 32, -1, -1, 35, -1, 37, - 29, -1, -1, 29, -1, 66, 67, 68, 94, 95, - 96, 29, -1, -1, 29, -1, 23, 24, 29, -1, - -1, 29, 15, -1, 29, 32, -1, -1, 35, -1, - 37, 36, -1, 94, 95, 96, 29, 66, 67, 68, + 8, 61, 60, 8, 48, 1, 0, 2, 55, 79, + 33, 1, 60, 48, 7, 79, 66, 60, 17, 8, + 36, 66, 29, 8, 60, 37, 60, 33, 79, 33, + 1, 33, 2, 36, 20, 55, 8, 79, 7, 61, + 55, 60, 29, 7, 5, 1, 5, 48, 48, 33, + 33, 5, 7, 33, 17, 37, 31, 36, 55, 8, + 33, 7, 60, 77, 7, 33, 7, 7, 60, 36, + 61, 36, 60, 7, 36, 8, 36, 16, 36, 36, + 7, 55, 34, 36, 7, 7, 7, 7, 36, 55, + 7, 36, 7, 7, 36, 7, 7, 7, 36, 2, + 36, 7, 33, 7, 55, 7, 36, 7, 55, 33, + 60, 7, -1, 8, 33, 8, 60, 42, 36, -1, + 36, 8, -1, 60, 33, 33, 61, 62, 53, 36, + 8, 36, 8, -1, 33, 36, 8, 50, 36, -1, + 8, 54, 8, 40, 15, 8, 15, 8, 40, 8, + 8, -1, -1, 24, 51, 15, -1, 10, 15, 51, + 8, 56, 50, 92, 93, 34, 54, 60, 8, 56, + 61, 62, 61, 62, 34, 61, 62, 34, 56, 61, + 62, 61, 62, 8, 60, 40, 61, 62, 60, 40, + 56, 7, 60, 6, 61, 62, 51, 60, 15, 60, + 51, 60, 55, 61, 7, 61, 62, 20, 56, 61, + 62, 92, 93, 15, 61, 62, 12, 34, 29, 36, + 60, 8, 24, 15, 29, 25, 12, 27, 25, -1, + 27, 7, 15, 12, 29, 29, 61, 62, 38, 7, + -1, 38, 34, -1, 36, 61, 62, 25, 29, 27, + 25, 34, 27, 36, 61, 62, 25, 29, 27, 8, + 38, 57, -1, 38, 75, 33, 25, 63, 27, 38, + 75, 57, 29, -1, 61, 62, 87, 63, 57, 38, + 75, 75, 87, 90, 63, 61, 62, 25, 25, 27, + 27, 36, 87, 87, 75, 25, 25, 27, 27, 7, + 38, 38, 15, 75, 7, 8, 87, -1, 38, 38, + 61, 62, 61, 62, -1, 87, 61, 62, 75, 15, + 25, 34, 27, 36, 18, 19, 15, 95, 18, 19, + 87, 18, 19, 38, 18, 19, -1, 33, 34, -1, + 36, -1, -1, 94, 33, 34, -1, 36, -1, 47, + -1, 45, 46, 61, 62, 45, 46, -1, 45, 46, + -1, 45, 46, 61, 62, 23, 24, -1, -1, 23, + 24, -1, -1, -1, 32, 23, 24, 35, 32, 37, + -1, 35, 29, 37, 32, 23, 24, 35, 29, 37, + -1, 23, 24, 29, 32, -1, 94, 35, 29, 37, + 32, 29, -1, 35, 25, 37, 27, 99, 100, 101, + 102, 103, 104, -1, 29, -1, -1, 38, -1, 66, + 67, 68, -1, -1, -1, 66, 67, 68, -1, -1, + 66, 67, 68, -1, -1, 66, 67, 68, 66, 67, + 68, -1, -1, -1, -1, -1, 29, -1, 95, 96, + 97, 66, 67, 68, 95, 96, 97, -1, -1, 95, + 96, 97, -1, -1, 95, 96, 97, 95, 96, 97, + 29, -1, -1, -1, -1, -1, -1, -1, 29, -1, + 95, 96, 97, 66, 67, 68, 23, 24, 29, -1, + -1, 29, -1, -1, 31, 32, 29, -1, 35, -1, + 37, -1, -1, 36, -1, -1, -1, 66, 67, 68, + -1, 29, 95, 96, 97, 66, 67, 68, 36, -1, + -1, -1, -1, -1, -1, 66, 67, 68, 66, 67, + 68, -1, -1, 66, 67, 68, 95, 96, 97, 15, + -1, -1, -1, -1, 95, 96, 97, 15, 66, 67, + 68, -1, -1, 29, 95, 96, 97, 95, 96, 97, + -1, 29, 95, 96, 97, 29, -1, -1, -1, -1, + 29, -1, 36, 29, -1, -1, -1, 95, 96, 97, + -1, -1, -1, -1, -1, 29, -1, -1, -1, -1, 66, 67, 68, -1, -1, -1, -1, -1, 66, 67, - 68, 66, 67, 68, -1, 66, 67, 68, 66, 67, - 68, 66, 67, 68, -1, 94, 95, 96, 94, 95, - 96, -1, -1, 66, 67, 68, 94, 95, 96, 94, - 95, 96, 15, 94, 95, 96, 94, 95, 96, 94, - 95, 96, -1, 29, -1, -1, 29, -1, -1, 29, - 36, 94, 95, 96, 29, -1, -1, 29, -1, -1, - -1, 36, -1, -1, -1, 29, -1, -1, 29, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 66, 67, 68, 66, 67, 68, 66, 67, 68, 61, - 62, 66, 67, 68, 66, 67, 68, 61, 62, -1, - 61, 62, 66, 67, 68, 66, 67, 68, 94, 95, - 96, 94, 95, 96, 94, 95, 96, 29, -1, 94, - 95, 96, 94, 95, 96, 29, 15, -1, 29, -1, - 94, 95, 96, 94, 95, 96, -1, -1, -1, -1, - 29, -1, -1, -1, -1, -1, -1, -1, -1, 61, - 62, -1, -1, -1, 66, 67, 68, 61, 62, -1, - 61, 62, 66, 67, 68, 66, 67, 68, -1, -1, - -1, -1, -1, -1, -1, 3, -1, 66, 67, 68, - -1, -1, 94, 95, 96, 13, -1, -1, -1, 17, - 94, 95, 96, 94, 95, 96, -1, -1, 26, -1, - 28, -1, -1, 31, -1, 94, 95, 96, -1, -1, - -1, 39, -1, 41, 42, -1, -1, 3, -1, -1, - -1, 49, -1, -1, 52, 53, -1, 13, -1, -1, - 58, 17, -1, -1, -1, -1, 64, -1, -1, -1, - 26, -1, 28, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 80, 39, -1, 41, 42, -1, -1, -1, - -1, -1, -1, 49, -1, -1, 52, 53, -1, -1, - -1, -1, 58, -1, -1, -1, -1, -1, 64, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 80, -1, -1, -1, -1, -1, + 68, -1, 66, 67, 68, 61, 62, 66, 67, 68, + 66, 67, 68, -1, -1, -1, -1, 61, 62, 95, + 96, 97, 66, 67, 68, -1, 29, 95, 96, 97, + -1, 95, 96, 97, -1, -1, 95, 96, 97, 95, + 96, 97, 29, -1, -1, 29, -1, -1, 15, -1, + -1, 95, 96, 97, -1, -1, -1, -1, 61, 62, + -1, -1, 29, 66, 67, 68, -1, -1, -1, -1, + -1, -1, -1, -1, 61, 62, -1, 61, 62, 66, + 67, 68, 66, 67, 68, -1, -1, -1, -1, -1, + -1, -1, 95, 96, 97, -1, -1, -1, -1, 66, + 67, 68, -1, -1, -1, -1, -1, -1, 95, 96, + 97, 95, 96, 97, -1, -1, -1, -1, -1, -1, + -1, 3, -1, -1, -1, -1, -1, -1, 95, 96, + 97, 13, -1, -1, -1, 17, -1, -1, -1, -1, + 29, -1, -1, -1, 26, -1, 28, -1, -1, 31, + -1, -1, -1, -1, -1, -1, -1, 39, -1, 41, + 42, -1, -1, -1, -1, -1, -1, 49, -1, -1, + 52, 53, 61, 62, -1, -1, 58, 66, 67, 68, + -1, -1, 64, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 80, -1, + -1, -1, -1, -1, -1, -1, 95, 96, 97, -1, + -1, -1, -1, -1, -1, -1, 12, 13, 3, -1, + -1, -1, -1, -1, -1, -1, 22, -1, 13, -1, + -1, -1, 17, 29, -1, -1, -1, 33, 34, -1, + 36, 26, -1, 28, -1, -1, -1, 43, -1, -1, + -1, 47, -1, -1, 39, -1, 41, 42, -1, -1, + -1, -1, -1, -1, 49, -1, -1, 52, 53, 65, + 66, 67, 68, 58, 70, -1, -1, -1, -1, 64, + -1, -1, -1, -1, -1, 81, 82, 83, -1, -1, + -1, -1, 88, -1, -1, 80, -1, -1, -1, 95, + 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, - 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, - -1, 33, 34, -1, 36, -1, -1, -1, -1, -1, - -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, + 70, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, -1, 88, -1, + -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, -1, -1, -1, 43, -1, + -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 81, - 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, - -1, -1, 94, 95, 96, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 12, 13, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, -1, 88, -1, -1, -1, -1, -1, -1, + 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 10, -1, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, - 68, -1, 70, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, - -1, -1, -1, -1, -1, -1, 94, 95, 96, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 10, -1, + 68, -1, 70, -1, -1, -1, -1, 75, -1, -1, + -1, -1, -1, 81, 82, 83, 84, 85, -1, -1, + 88, -1, -1, -1, -1, -1, -1, 95, 96, 97, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, + -1, 12, 13, -1, -1, -1, -1, -1, -1, -1, + -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, + -1, -1, 33, 34, -1, 36, -1, -1, -1, -1, + -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, + -1, -1, -1, -1, 55, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, + -1, -1, -1, -1, 75, -1, -1, -1, -1, -1, + 81, 82, 83, 84, 85, -1, -1, 88, -1, -1, + -1, -1, -1, -1, 95, 96, 97, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 10, -1, 12, 13, + -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, + -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, + 34, -1, 36, -1, -1, -1, -1, -1, -1, 43, + -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, + -1, 55, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, -1, -1, + -1, 75, -1, -1, -1, -1, -1, 81, 82, 83, + 84, 85, -1, -1, 88, -1, -1, -1, -1, -1, + -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, + -1, -1, -1, 81, 82, 83, -1, -1, -1, -1, + 88, -1, -1, -1, -1, -1, -1, 95, 96, 97, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, - -1, 33, 34, -1, 36, -1, -1, -1, -1, -1, - -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, - -1, -1, -1, 75, -1, -1, -1, -1, -1, 81, - 82, 83, 84, -1, -1, 87, -1, -1, -1, -1, - -1, -1, 94, 95, 96, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 10, -1, 12, 13, -1, -1, - -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, - -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, - 36, -1, -1, -1, -1, -1, -1, 43, -1, -1, - -1, 47, -1, -1, -1, -1, -1, -1, -1, 55, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, - 66, 67, 68, -1, 70, -1, -1, -1, -1, 75, - -1, -1, -1, -1, -1, 81, 82, 83, 84, -1, - -1, 87, -1, -1, -1, -1, -1, -1, 94, 95, - 96, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 10, -1, 12, 13, -1, -1, -1, -1, -1, -1, - -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, - -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, - -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, - -1, -1, -1, -1, -1, 55, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, - 70, -1, -1, -1, -1, 75, -1, -1, -1, -1, - -1, 81, 82, 83, 84, -1, -1, 87, -1, -1, - -1, -1, -1, -1, 94, 95, 96, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, - -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, - -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, - -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, - -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, - -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, - -1, -1, 87, -1, -1, -1, -1, -1, -1, 94, - 95, 96, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 7, -1, -1, -1, 11, 12, 13, -1, -1, - -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, - -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, - 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, - -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, - 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, - 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, - -1, 87, -1, -1, -1, -1, -1, -1, 94, 95, - 96, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, - -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, - -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, - -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, - 51, -1, 53, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, - -1, 72, -1, 74, 75, 76, -1, -1, -1, -1, - 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, - -1, -1, -1, 94, 95, 96, -1, -1, -1, -1, + 72, -1, 74, 75, 76, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, -1, 88, -1, -1, -1, + -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7, -1, -1, -1, 11, + 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, + 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, -1, 88, -1, -1, -1, + -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, @@ -889,8 +910,18 @@ const short QQmlJSGrammar::action_check [] = { 53, -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, - 83, -1, -1, -1, 87, -1, -1, -1, -1, -1, - -1, 94, 95, 96, -1, -1, -1, -1, -1, -1, + 83, -1, -1, -1, -1, 88, -1, -1, -1, -1, + -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, + -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, + -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, + 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, + -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, + -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, @@ -899,46 +930,27 @@ const short QQmlJSGrammar::action_check [] = { -1, 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, - -1, -1, 87, -1, -1, -1, -1, -1, -1, 94, - 95, 96, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 8, -1, -1, 11, 12, 13, -1, -1, -1, - -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, - -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, - -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, - 47, -1, -1, -1, 51, -1, 53, -1, -1, 56, - -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, - 67, 68, -1, 70, -1, 72, -1, 74, -1, 76, - -1, -1, -1, -1, 81, 82, 83, -1, -1, -1, - 87, -1, -1, -1, -1, -1, -1, 94, 95, 96, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, - -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, - -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, - 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, - -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, - -1, -1, 51, -1, 53, -1, -1, 56, -1, -1, - -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, - -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, - -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, - -1, -1, -1, -1, -1, 94, 95, 96, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 11, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, 30, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, -1, -1, -1, -1, -1, 61, -1, -1, - -1, 65, 66, 67, 68, 69, 70, -1, 72, 73, - 74, -1, 76, -1, 78, -1, -1, 81, 82, 83, - -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, - 94, 95, 96, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, - -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, - 29, 30, -1, -1, 33, 34, -1, 36, -1, -1, - -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, - -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, - -1, -1, 61, -1, -1, -1, 65, 66, 67, 68, - 69, 70, -1, 72, 73, 74, -1, 76, -1, 78, - -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, - -1, -1, -1, -1, -1, 94, 95, 96, -1, -1, + -1, -1, -1, 88, -1, -1, -1, -1, -1, -1, + 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, + -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, + -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, + 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, + -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, + 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, + 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, + 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, + -1, -1, 88, -1, -1, -1, -1, -1, -1, 95, + 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + 30, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, + -1, 51, -1, 53, -1, -1, -1, -1, -1, -1, + -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, + 70, -1, 72, 73, 74, -1, 76, -1, 78, -1, + -1, 81, 82, 83, -1, -1, -1, -1, 88, -1, + -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, 33, @@ -947,38 +959,48 @@ const short QQmlJSGrammar::action_check [] = { -1, -1, -1, -1, -1, -1, -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, -1, 72, 73, 74, -1, 76, -1, 78, -1, -1, 81, 82, 83, - -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, - 94, 95, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, + -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, 30, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, + -1, -1, -1, 61, -1, -1, -1, 65, 66, 67, + 68, 69, 70, -1, 72, 73, 74, -1, 76, -1, + 78, -1, -1, 81, 82, 83, -1, -1, -1, -1, + 88, -1, -1, -1, -1, -1, -1, 95, 96, 97, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, + 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, + -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, + -1, -1, -1, -1, 29, 30, 31, 32, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, 66, 67, 68, 69, 70, 71, -1, 73, 74, + 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, + 85, 86, 87, -1, -1, -1, -1, -1, -1, -1, + 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 43, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, -1, -1, - -1, -1, -1, -1, 66, 67, 68, 69, 70, 71, + -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, -1, -1, 81, - 82, 83, 84, 85, 86, -1, -1, -1, -1, -1, - -1, -1, 94, 95, 96, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, - 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, - 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, - 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, - -1, -1, -1, -1, -1, 65, 66, 67, 68, 69, - 70, 71, -1, 73, 74, 75, 76, 77, 78, -1, - -1, 81, 82, 83, 84, 85, 86, -1, -1, -1, - -1, -1, -1, -1, 94, 95, 96, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 4, 5, 6, -1, - -1, 9, 10, 11, -1, -1, 14, -1, 16, -1, - -1, -1, 20, 21, 22, -1, -1, -1, -1, -1, - -1, 29, 30, 31, 32, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 43, -1, -1, -1, 47, + 82, 83, 84, 85, 86, 87, -1, -1, -1, -1, + -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, + 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, + -1, 20, 21, 22, -1, -1, -1, -1, -1, -1, + 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 59, -1, -1, -1, -1, -1, 65, 66, 67, - 68, 69, 70, 71, -1, 73, 74, 75, 76, 77, - 78, -1, -1, 81, 82, 83, 84, 85, 86, -1, - -1, -1, -1, -1, -1, -1, 94, 95, 96, -1, + 59, -1, -1, -1, -1, -1, 65, 66, 67, 68, + 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, + -1, -1, 81, 82, 83, 84, 85, 86, 87, -1, + -1, -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, -1, @@ -988,104 +1010,102 @@ const short QQmlJSGrammar::action_check [] = { -1, -1, -1, 59, -1, -1, -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, 85, - 86, -1, -1, -1, -1, -1, -1, -1, 94, 95, - 96, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 4, -1, -1, -1, -1, 9, -1, 11, 12, 13, - 14, -1, -1, -1, -1, -1, -1, 21, 22, -1, - -1, -1, -1, -1, -1, 29, 30, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, -1, -1, -1, 59, -1, 61, -1, -1, - -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, - 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, - 84, 85, -1, 87, -1, -1, -1, -1, -1, -1, - 94, 95, 96, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 4, -1, -1, -1, -1, 9, -1, 11, - 12, 13, 14, -1, -1, -1, -1, -1, -1, 21, - 22, -1, -1, -1, -1, -1, -1, 29, 30, -1, - -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, - 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, - -1, 53, -1, -1, -1, -1, -1, 59, -1, 61, - -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, -1, -1, 81, - 82, 83, 84, 85, -1, 87, -1, -1, -1, -1, - -1, -1, 94, 95, 96, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, - 10, 11, 12, 13, 14, -1, 16, -1, -1, -1, - 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, - 30, 31, 32, 33, 34, -1, 36, -1, -1, -1, + 86, 87, -1, -1, -1, -1, -1, -1, -1, 95, + 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 4, -1, -1, -1, -1, 9, -1, 11, 12, + 13, 14, -1, -1, -1, -1, -1, -1, 21, 22, + -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, -1, -1, -1, 59, -1, 61, -1, + -1, -1, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, + 83, 84, 85, 86, -1, 88, -1, -1, -1, -1, + -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 4, -1, -1, -1, -1, 9, + -1, 11, 12, 13, 14, -1, -1, -1, -1, -1, + -1, 21, 22, -1, -1, -1, -1, -1, -1, 29, + 30, -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, 59, -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, -1, - -1, 81, 82, 83, 84, 85, 86, 87, -1, -1, - -1, -1, -1, -1, 94, 95, 96, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 4, 5, 6, -1, - -1, 9, 10, 11, 12, 13, 14, -1, 16, -1, - -1, -1, 20, 21, 22, -1, -1, -1, -1, -1, - -1, 29, 30, 31, 32, 33, 34, -1, 36, -1, - -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, - -1, -1, -1, 51, -1, 53, -1, 55, -1, -1, - -1, 59, -1, 61, -1, -1, -1, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, -1, -1, 81, 82, 83, 84, 85, 86, 87, - -1, -1, -1, -1, -1, -1, 94, 95, 96, -1, - -1, -1, -1, -1, -1, -1, -1, -1, + -1, 81, 82, 83, 84, 85, 86, -1, 88, -1, + -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, + -1, -1, 9, 10, 11, 12, 13, 14, -1, 16, + -1, -1, -1, 20, 21, 22, -1, -1, -1, -1, + -1, -1, 29, 30, 31, 32, 33, 34, -1, 36, + -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, + 47, -1, -1, -1, 51, -1, 53, -1, -1, -1, + -1, -1, 59, -1, 61, -1, -1, -1, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, -1, -1, 81, 82, 83, 84, 85, 86, + 87, 88, -1, -1, -1, -1, -1, -1, 95, 96, + 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 4, 5, 6, -1, -1, 9, 10, 11, 12, 13, + 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, + -1, -1, -1, -1, -1, 29, 30, 31, 32, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, 55, -1, -1, -1, 59, -1, 61, -1, -1, + -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, + 84, 85, 86, 87, 88, -1, -1, -1, -1, -1, + -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, + -1, -1, -1, - 77, 14, 18, 18, 18, 32, 18, 3, 32, 42, - 9, 3, 3, 18, 42, 3, 18, 18, 3, 22, - 18, 32, 32, 18, 18, 25, 32, 3, 18, 18, - 3, 18, 3, 42, 18, 14, 22, 18, 18, 22, - 22, 18, 100, 18, 18, 42, 18, 3, 105, 42, - 3, 3, 18, 14, 32, 18, 3, 25, 25, 18, - 3, 25, 3, 18, 18, 3, 103, 18, 18, 2, - -1, 3, 42, 42, 18, 14, 25, 42, 14, 2, - 42, -1, 42, 18, 42, 18, 2, 4, -1, -1, - 18, -1, -1, 18, -1, 18, -1, 54, -1, 56, - 44, 18, 18, 18, 18, -1, 54, -1, 54, 54, - 56, 56, 51, 42, 54, 51, 56, 54, 46, 56, - 45, 50, 70, 54, 54, 56, 56, 3, 54, 42, - 45, 18, 46, 59, 54, 54, 54, 50, 2, 59, - 59, 59, 54, 54, 11, 12, 78, 59, 59, 54, - 54, 56, 56, 54, 18, 2, 14, 18, 4, 54, - 54, 56, 56, 54, 54, 23, 3, 68, 58, 60, - 54, 18, 18, 14, 109, 2, 60, 2, 19, 54, - 54, 54, 43, 38, 59, 59, 59, 42, 54, 54, - 56, 18, 54, 18, 59, 54, 2, 59, 54, 58, - 54, 2, 58, 18, 54, 59, 2, 94, 2, 54, - 18, 4, 18, 2, 18, 3, 66, 18, 18, 64, - 3, 54, 18, 56, 18, 18, 54, 2, 56, 18, - 45, 54, 18, 56, 54, 14, 14, 57, 2, 47, - 19, 78, 46, 18, 44, 54, 2, 18, 57, 54, - 2, 54, 2, 56, 18, 60, 54, 54, 56, 56, - 14, -1, 18, 54, 54, 19, 18, -1, 18, 60, - 54, 18, 62, 51, 45, 59, -1, 54, -1, -1, - 54, 54, 59, 67, 54, 59, 59, 54, 2, 59, - 78, -1, 59, 63, 61, 78, 69, 71, -1, 76, - 47, 48, -1, 54, 18, 54, 92, -1, 59, 54, - 59, 54, 61, 54, 59, -1, 59, 54, 59, 54, - 65, -1, 59, 54, 59, 76, 61, -1, 59, 14, - 61, 14, 2, 76, 19, 76, 21, 5, 88, 76, - 23, -1, -1, -1, -1, -1, 14, -1, 18, -1, - -1, -1, 35, 36, -1, 23, -1, 42, 25, 26, - 27, 28, 29, 30, 31, -1, 14, 35, 36, -1, - -1, -1, -1, -1, 88, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 14, -1, -1, -1, -1, -1, - -1, -1, -1, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 14, -1, -1, -1, -1, -1, -1, -1, - -1, 23, 24, 25, 26, 27, 28, 29, 30, 31, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, - 14, -1, -1, -1, -1, -1, -1, -1, -1, 23, + 3, 42, 9, 77, 18, 3, 25, 42, 18, 25, + 18, 105, 18, 42, 42, 3, 100, 3, 103, 3, + 14, 18, 14, 22, 42, 18, 22, 32, 3, 18, + 25, 32, 3, 3, 14, 18, 18, 18, 42, 3, + 42, 3, 42, 3, 18, 32, 25, 32, 3, 3, + 18, 42, 42, 18, 3, 18, 22, 18, 3, 18, + 18, 18, 18, 32, 42, 18, 18, 18, 18, 3, + 3, -1, 18, 2, 22, 18, 18, 18, 25, 14, + 18, 4, 14, 2, 2, 2, 14, 19, 32, 18, + 3, 19, 3, 2, 18, 18, 18, 18, 4, 18, + 18, 18, 38, -1, 18, 3, 42, 14, 3, 18, + 18, 14, 18, 3, 54, 54, 51, 54, -1, 59, + 59, 45, 59, 45, 54, 46, 56, 54, 54, 54, + 2, 45, 59, 59, 59, 2, 2, 2, 54, 54, + 56, 56, 18, 2, 51, -1, 18, -1, 51, 18, + -1, 18, 18, 18, 54, 54, 56, 56, 54, 18, + 56, 54, 54, 56, 56, 78, 54, 78, 56, 42, + 46, 109, 54, 2, 56, 2, 54, 50, 2, 54, + 78, 2, 2, 54, 18, 56, 94, 2, 78, 18, + 18, 18, 70, 68, 18, 18, 54, 18, 18, 54, + 54, 56, 56, 18, 54, 54, 64, 54, 58, 54, + 44, 58, 4, 54, 59, 56, 2, 66, 54, 47, + 56, 44, 54, 92, 54, 18, 18, 59, 58, 54, + 18, 18, 18, -1, 59, 54, 54, 54, 54, 57, + 59, 57, 59, 54, -1, 54, 54, 54, 59, 54, + 43, 60, 60, 60, 18, 60, 54, 45, 42, 46, + 11, 12, 14, 54, 62, 56, 50, 19, -1, 54, + -1, 56, 54, 54, 56, 54, 54, 54, 59, -1, + 59, 59, 59, 47, 48, 54, 54, 54, 2, 2, + 59, 59, 59, 14, 71, 76, 54, 76, 76, -1, + 69, 59, 23, -1, 18, 18, 54, 65, 76, 76, + -1, 59, 54, 54, 35, 36, -1, 59, 59, 67, + 61, 63, 54, 54, -1, 14, 54, 59, 59, 61, + 61, 59, 5, 61, 23, 5, -1, -1, -1, -1, + -1, 14, -1, -1, 14, -1, -1, -1, -1, -1, + 23, -1, -1, 23, 25, 26, 27, 28, 29, 30, + 31, -1, 35, 36, -1, 35, 36, 14, -1, -1, + -1, -1, -1, -1, 88, 88, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 14, -1, -1, -1, -1, + -1, -1, -1, -1, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 14, -1, -1, -1, -1, -1, -1, + -1, -1, 23, 24, 25, 26, 27, 28, 29, 30, + 31, -1, 14, -1, -1, -1, -1, 19, -1, 21, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 35, 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 42, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1}; + -1, -1, -1, -1, -1, -1, -1, -1, -1}; QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsgrammar_p.h b/src/qml/parser/qqmljsgrammar_p.h index 050ef6c288..b4f762d28b 100644 --- a/src/qml/parser/qqmljsgrammar_p.h +++ b/src/qml/parser/qqmljsgrammar_p.h @@ -61,23 +61,23 @@ class QQmlJSGrammar public: enum VariousConstants { EOF_SYMBOL = 0, - REDUCE_HERE = 105, - SHIFT_THERE = 104, + REDUCE_HERE = 106, + SHIFT_THERE = 105, T_AND = 1, T_AND_AND = 2, T_AND_EQ = 3, - T_AS = 93, + T_AS = 94, T_AUTOMATIC_SEMICOLON = 62, T_BREAK = 4, T_CASE = 5, T_CATCH = 6, T_COLON = 7, T_COMMA = 8, - T_COMMENT = 88, - T_COMPATIBILITY_SEMICOLON = 89, + T_COMMENT = 89, + T_COMPATIBILITY_SEMICOLON = 90, T_CONST = 84, T_CONTINUE = 9, - T_DEBUGGER = 85, + T_DEBUGGER = 86, T_DEFAULT = 10, T_DELETE = 11, T_DIVIDE_ = 12, @@ -88,19 +88,19 @@ public: T_EQ = 17, T_EQ_EQ = 18, T_EQ_EQ_EQ = 19, - T_ERROR = 97, + T_ERROR = 98, T_FALSE = 83, - T_FEED_JS_EXPRESSION = 101, - T_FEED_JS_PROGRAM = 103, - T_FEED_JS_SOURCE_ELEMENT = 102, - T_FEED_JS_STATEMENT = 100, - T_FEED_UI_OBJECT_MEMBER = 99, - T_FEED_UI_PROGRAM = 98, + T_FEED_JS_EXPRESSION = 102, + T_FEED_JS_PROGRAM = 104, + T_FEED_JS_SOURCE_ELEMENT = 103, + T_FEED_JS_STATEMENT = 101, + T_FEED_UI_OBJECT_MEMBER = 100, + T_FEED_UI_PROGRAM = 99, T_FINALLY = 20, T_FOR = 21, T_FUNCTION = 22, T_GE = 23, - T_GET = 95, + T_GET = 96, T_GT = 24, T_GT_GT = 25, T_GT_GT_EQ = 26, @@ -108,12 +108,13 @@ public: T_GT_GT_GT_EQ = 28, T_IDENTIFIER = 29, T_IF = 30, - T_IMPORT = 91, + T_IMPORT = 92, T_IN = 31, T_INSTANCEOF = 32, T_LBRACE = 33, T_LBRACKET = 34, T_LE = 35, + T_LET = 85, T_LPAREN = 36, T_LT = 37, T_LT_LT = 38, @@ -121,34 +122,34 @@ public: T_MINUS = 40, T_MINUS_EQ = 41, T_MINUS_MINUS = 42, - T_MULTILINE_STRING_LITERAL = 87, + T_MULTILINE_STRING_LITERAL = 88, T_NEW = 43, T_NOT = 44, T_NOT_EQ = 45, T_NOT_EQ_EQ = 46, T_NULL = 81, T_NUMERIC_LITERAL = 47, - T_ON = 94, + T_ON = 95, T_OR = 48, T_OR_EQ = 49, T_OR_OR = 50, T_PLUS = 51, T_PLUS_EQ = 52, T_PLUS_PLUS = 53, - T_PRAGMA = 92, + T_PRAGMA = 93, T_PROPERTY = 66, - T_PUBLIC = 90, + T_PUBLIC = 91, T_QUESTION = 54, T_RBRACE = 55, T_RBRACKET = 56, T_READONLY = 68, T_REMAINDER = 57, T_REMAINDER_EQ = 58, - T_RESERVED_WORD = 86, + T_RESERVED_WORD = 87, T_RETURN = 59, T_RPAREN = 60, T_SEMICOLON = 61, - T_SET = 96, + T_SET = 97, T_SIGNAL = 67, T_STAR = 63, T_STAR_EQ = 64, @@ -167,15 +168,15 @@ public: T_XOR = 79, T_XOR_EQ = 80, - ACCEPT_STATE = 674, - RULE_COUNT = 361, - STATE_COUNT = 675, - TERMINAL_COUNT = 106, + ACCEPT_STATE = 678, + RULE_COUNT = 363, + STATE_COUNT = 679, + TERMINAL_COUNT = 107, NON_TERMINAL_COUNT = 111, - GOTO_INDEX_OFFSET = 675, - GOTO_INFO_OFFSET = 3078, - GOTO_CHECK_OFFSET = 3078 + GOTO_INDEX_OFFSET = 679, + GOTO_INFO_OFFSET = 3203, + GOTO_CHECK_OFFSET = 3203 }; static const char *const spell []; diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h index 84ebe5f210..8b789526a5 100644 --- a/src/qml/parser/qqmljskeywords_p.h +++ b/src/qml/parser/qqmljskeywords_p.h @@ -106,6 +106,13 @@ static inline int classify3(const QChar *s, bool qmlMode) { } } } + else if (s[0].unicode() == 'l') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 't') { + return int(Lexer::T_LET); + } + } + } else if (s[0].unicode() == 'n') { if (s[1].unicode() == 'e') { if (s[2].unicode() == 'w') { @@ -278,7 +285,7 @@ static inline int classify5(const QChar *s, bool qmlMode) { if (s[2].unicode() == 'n') { if (s[3].unicode() == 's') { if (s[4].unicode() == 't') { - return qmlMode ? int(Lexer::T_CONST) : int(Lexer::T_RESERVED_WORD); + return int(Lexer::T_CONST); } } } diff --git a/src/qml/parser/qqmljsparser.cpp b/src/qml/parser/qqmljsparser.cpp index 50518a92ee..636b959097 100644 --- a/src/qml/parser/qqmljsparser.cpp +++ b/src/qml/parser/qqmljsparser.cpp @@ -914,21 +914,21 @@ case 116: { sym(1).Node = node; } break; -case 152: { +case 153: { AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); node->lbracketToken = loc(2); node->rbracketToken = loc(4); sym(1).Node = node; } break; -case 153: { +case 154: { AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); node->dotToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 154: { +case 155: { AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); node->newToken = loc(1); node->lparenToken = loc(3); @@ -936,384 +936,384 @@ case 154: { sym(1).Node = node; } break; -case 156: { +case 157: { AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); node->newToken = loc(1); sym(1).Node = node; } break; -case 157: { +case 158: { AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); node->lparenToken = loc(2); node->rparenToken = loc(4); sym(1).Node = node; } break; -case 158: { +case 159: { AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); node->lparenToken = loc(2); node->rparenToken = loc(4); sym(1).Node = node; } break; -case 159: { +case 160: { AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); node->lbracketToken = loc(2); node->rbracketToken = loc(4); sym(1).Node = node; } break; -case 160: { +case 161: { AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); node->dotToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 161: { +case 162: { sym(1).Node = 0; } break; -case 162: { +case 163: { sym(1).Node = sym(1).ArgumentList->finish(); } break; -case 163: { +case 164: { sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); } break; -case 164: { +case 165: { AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 168: { +case 169: { AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); node->incrementToken = loc(2); sym(1).Node = node; } break; -case 169: { +case 170: { AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); node->decrementToken = loc(2); sym(1).Node = node; } break; -case 171: { +case 172: { AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); node->deleteToken = loc(1); sym(1).Node = node; } break; -case 172: { +case 173: { AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); node->voidToken = loc(1); sym(1).Node = node; } break; -case 173: { +case 174: { AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); node->typeofToken = loc(1); sym(1).Node = node; } break; -case 174: { +case 175: { AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); node->incrementToken = loc(1); sym(1).Node = node; } break; -case 175: { +case 176: { AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); node->decrementToken = loc(1); sym(1).Node = node; } break; -case 176: { +case 177: { AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); node->plusToken = loc(1); sym(1).Node = node; } break; -case 177: { +case 178: { AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); node->minusToken = loc(1); sym(1).Node = node; } break; -case 178: { +case 179: { AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); node->tildeToken = loc(1); sym(1).Node = node; } break; -case 179: { +case 180: { AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); node->notToken = loc(1); sym(1).Node = node; } break; -case 181: { +case 182: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Mul, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 182: { +case 183: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Div, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 183: { +case 184: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Mod, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 185: { +case 186: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Add, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 186: { +case 187: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Sub, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 188: { +case 189: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::LShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 189: { +case 190: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::RShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 190: { +case 191: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::URShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 192: { +case 193: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Lt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 193: { +case 194: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Gt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 194: { +case 195: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Le, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 195: { +case 196: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Ge, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 196: { +case 197: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::InstanceOf, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 197: { +case 198: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::In, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 199: { +case 200: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Lt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 200: { +case 201: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Gt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 201: { +case 202: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Le, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 202: { +case 203: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Ge, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 203: { +case 204: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::InstanceOf, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 205: { +case 206: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Equal, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 206: { +case 207: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::NotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 207: { +case 208: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 208: { +case 209: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictNotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 210: { +case 211: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Equal, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 211: { +case 212: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::NotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 212: { +case 213: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 213: { +case 214: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictNotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 215: { +case 216: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 217: { +case 218: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 219: { +case 220: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 221: { +case 222: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 223: { +case 224: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 225: { +case 226: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 227: { +case 228: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 229: { +case 230: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 231: { +case 232: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 233: { +case 234: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 235: { +case 236: { AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); node->questionToken = loc(2); @@ -1321,7 +1321,7 @@ case 235: { sym(1).Node = node; } break; -case 237: { +case 238: { AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); node->questionToken = loc(2); @@ -1329,189 +1329,200 @@ case 237: { sym(1).Node = node; } break; -case 239: { +case 240: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 241: { +case 242: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 242: { +case 243: { sym(1).ival = QSOperator::Assign; } break; -case 243: { +case 244: { sym(1).ival = QSOperator::InplaceMul; } break; -case 244: { +case 245: { sym(1).ival = QSOperator::InplaceDiv; } break; -case 245: { +case 246: { sym(1).ival = QSOperator::InplaceMod; } break; -case 246: { +case 247: { sym(1).ival = QSOperator::InplaceAdd; } break; -case 247: { +case 248: { sym(1).ival = QSOperator::InplaceSub; } break; -case 248: { +case 249: { sym(1).ival = QSOperator::InplaceLeftShift; } break; -case 249: { +case 250: { sym(1).ival = QSOperator::InplaceRightShift; } break; -case 250: { +case 251: { sym(1).ival = QSOperator::InplaceURightShift; } break; -case 251: { +case 252: { sym(1).ival = QSOperator::InplaceAnd; } break; -case 252: { +case 253: { sym(1).ival = QSOperator::InplaceXor; } break; -case 253: { +case 254: { sym(1).ival = QSOperator::InplaceOr; } break; -case 255: { +case 256: { AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 256: { +case 257: { sym(1).Node = 0; } break; -case 259: { +case 260: { AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 260: { +case 261: { sym(1).Node = 0; } break; -case 277: { +case 278: { AST::Block *node = new (pool) AST::Block(sym(2).StatementList); node->lbraceToken = loc(1); node->rbraceToken = loc(3); sym(1).Node = node; } break; -case 278: { +case 279: { sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); } break; -case 279: { +case 280: { sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); } break; -case 280: { +case 281: { sym(1).Node = 0; } break; -case 281: { +case 282: { sym(1).Node = sym(1).StatementList->finish (); } break; -case 283: { - AST::VariableStatement *node = new (pool) AST::VariableStatement( - sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); +case 284: { + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + if (sym(1).ival == T_LET) + s = AST::VariableDeclaration::BlockScope; + else if (sym(1).ival == T_CONST) + s = AST::VariableDeclaration::ReadOnlyBlockScope; + + AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(s)); node->declarationKindToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 284: { +case 285: { + sym(1).ival = T_LET; +} break; + +case 286: { sym(1).ival = T_CONST; } break; -case 285: { +case 287: { sym(1).ival = T_VAR; } break; -case 286: { +case 288: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); } break; -case 287: { +case 289: { AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( sym(1).VariableDeclarationList, sym(3).VariableDeclaration); node->commaToken = loc(2); sym(1).Node = node; } break; -case 288: { +case 290: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); } break; -case 289: { +case 291: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); } break; -case 290: { - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); +case 292: { + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 291: { - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); +case 293: { + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 292: { +case 294: { // ### TODO: AST for initializer sym(1) = sym(2); } break; -case 293: { +case 295: { sym(1).Node = 0; } break; -case 295: { +case 297: { // ### TODO: AST for initializer sym(1) = sym(2); } break; -case 296: { +case 298: { sym(1).Node = 0; } break; -case 298: { +case 300: { AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); node->semicolonToken = loc(1); sym(1).Node = node; } break; -case 300: { +case 302: { AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 301: { +case 303: { AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); node->ifToken = loc(1); node->lparenToken = loc(2); @@ -1520,7 +1531,7 @@ case 301: { sym(1).Node = node; } break; -case 302: { +case 304: { AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); node->ifToken = loc(1); node->lparenToken = loc(2); @@ -1528,7 +1539,7 @@ case 302: { sym(1).Node = node; } break; -case 305: { +case 307: { AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); node->doToken = loc(1); node->whileToken = loc(3); @@ -1538,7 +1549,7 @@ case 305: { sym(1).Node = node; } break; -case 306: { +case 308: { AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); node->whileToken = loc(1); node->lparenToken = loc(2); @@ -1546,7 +1557,7 @@ case 306: { sym(1).Node = node; } break; -case 307: { +case 309: { AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, sym(5).Expression, sym(7).Expression, sym(9).Statement); node->forToken = loc(1); @@ -1557,9 +1568,10 @@ case 307: { sym(1).Node = node; } break; -case 308: { +case 310: { + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; AST::LocalForStatement *node = new (pool) AST::LocalForStatement( - sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(4).VariableDeclarationList->finish(s), sym(6).Expression, sym(8).Expression, sym(10).Statement); node->forToken = loc(1); node->lparenToken = loc(2); @@ -1570,7 +1582,7 @@ case 308: { sym(1).Node = node; } break; -case 309: { +case 311: { AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, sym(5).Expression, sym(7).Statement); node->forToken = loc(1); @@ -1580,7 +1592,7 @@ case 309: { sym(1).Node = node; } break; -case 310: { +case 312: { AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); node->forToken = loc(1); @@ -1591,14 +1603,14 @@ case 310: { sym(1).Node = node; } break; -case 312: { +case 314: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); node->continueToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 314: { +case 316: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); node->continueToken = loc(1); node->identifierToken = loc(2); @@ -1606,14 +1618,14 @@ case 314: { sym(1).Node = node; } break; -case 316: { +case 318: { AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); node->breakToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 318: { +case 320: { AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); node->breakToken = loc(1); node->identifierToken = loc(2); @@ -1621,14 +1633,14 @@ case 318: { sym(1).Node = node; } break; -case 320: { +case 322: { AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); node->returnToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 321: { +case 323: { AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); node->withToken = loc(1); node->lparenToken = loc(2); @@ -1636,7 +1648,7 @@ case 321: { sym(1).Node = node; } break; -case 322: { +case 324: { AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); node->switchToken = loc(1); node->lparenToken = loc(2); @@ -1644,83 +1656,83 @@ case 322: { sym(1).Node = node; } break; -case 323: { +case 325: { AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); node->lbraceToken = loc(1); node->rbraceToken = loc(3); sym(1).Node = node; } break; -case 324: { +case 326: { AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); node->lbraceToken = loc(1); node->rbraceToken = loc(5); sym(1).Node = node; } break; -case 325: { +case 327: { sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); } break; -case 326: { +case 328: { sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); } break; -case 327: { +case 329: { sym(1).Node = 0; } break; -case 328: { +case 330: { sym(1).Node = sym(1).CaseClauses->finish (); } break; -case 329: { +case 331: { AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); node->caseToken = loc(1); node->colonToken = loc(3); sym(1).Node = node; } break; -case 330: { +case 332: { AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); node->defaultToken = loc(1); node->colonToken = loc(2); sym(1).Node = node; } break; -case 331: { +case 333: { AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); node->identifierToken = loc(1); node->colonToken = loc(2); sym(1).Node = node; } break; -case 333: { +case 335: { AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); node->throwToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 334: { +case 336: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); node->tryToken = loc(1); sym(1).Node = node; } break; -case 335: { +case 337: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); node->tryToken = loc(1); sym(1).Node = node; } break; -case 336: { +case 338: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); node->tryToken = loc(1); sym(1).Node = node; } break; -case 337: { +case 339: { AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); node->catchToken = loc(1); node->lparenToken = loc(2); @@ -1729,20 +1741,20 @@ case 337: { sym(1).Node = node; } break; -case 338: { +case 340: { AST::Finally *node = new (pool) AST::Finally(sym(2).Block); node->finallyToken = loc(1); sym(1).Node = node; } break; -case 340: { +case 342: { AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); node->debuggerToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 342: { +case 344: { AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); node->functionToken = loc(1); node->identifierToken = loc(2); @@ -1753,7 +1765,7 @@ case 342: { sym(1).Node = node; } break; -case 343: { +case 345: { AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); node->functionToken = loc(1); if (! stringRef(2).isNull()) @@ -1765,7 +1777,7 @@ case 343: { sym(1).Node = node; } break; -case 344: { +case 346: { AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody); node->functionToken = loc(1); node->lparenToken = loc(2); @@ -1775,56 +1787,56 @@ case 344: { sym(1).Node = node; } break; -case 345: { +case 347: { AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 346: { +case 348: { AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); node->commaToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 347: { +case 349: { sym(1).Node = 0; } break; -case 348: { +case 350: { sym(1).Node = sym(1).FormalParameterList->finish (); } break; -case 349: { +case 351: { sym(1).Node = 0; } break; -case 351: { +case 353: { sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); } break; -case 353: { +case 355: { sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); } break; -case 354: { +case 356: { sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); } break; -case 355: { +case 357: { sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); } break; -case 356: { +case 358: { sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); } break; -case 357: { +case 359: { sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); } break; -case 358: { +case 360: { sym(1).Node = 0; } break; diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index f0973338ea..ddb4af0b81 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -238,6 +238,44 @@ int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int ve return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); } +template<typename T, typename E, int metaObjectRevision> +int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason) +{ + QML_GETTYPENAMES + + QQmlAttachedPropertiesFunc attached = QQmlPrivate::attachedPropertiesFunc<E>(); + const QMetaObject * attachedMetaObject = QQmlPrivate::attachedPropertiesMetaObject<E>(); + if (!attached) { + attached = QQmlPrivate::attachedPropertiesFunc<T>(); + attachedMetaObject = QQmlPrivate::attachedPropertiesMetaObject<T>(); + } + + QQmlPrivate::RegisterType type = { + 1, + + qRegisterNormalizedMetaType<T *>(pointerName.constData()), + qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()), + 0, + Q_NULLPTR, + reason, + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + attached, + attachedMetaObject, + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + QQmlPrivate::createParent<E>, &E::staticMetaObject, + + Q_NULLPTR, + metaObjectRevision + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} Q_QML_EXPORT int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason); diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 2a96d96302..a10dda166c 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -66,8 +66,6 @@ void QQmlApplicationEnginePrivate::cleanUp() void QQmlApplicationEnginePrivate::init() { Q_Q(QQmlApplicationEngine); - q->connect(&statusMapper, SIGNAL(mapped(QObject*)), - q, SLOT(_q_finishLoad(QObject*))); q->connect(q, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit); #if QT_CONFIG(translation) @@ -113,20 +111,15 @@ void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray & c->loadUrl(url); if (!c->isLoading()) { - _q_finishLoad(c); + finishLoad(c); return; } - statusMapper.setMapping(c, c); - q->connect(c, SIGNAL(statusChanged(QQmlComponent::Status)), - &statusMapper, SLOT(map())); + QObject::connect(c, &QQmlComponent::statusChanged, q, [this, c] { this->finishLoad(c); }); } -void QQmlApplicationEnginePrivate::_q_finishLoad(QObject *o) +void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) { Q_Q(QQmlApplicationEngine); - QQmlComponent *c = qobject_cast<QQmlComponent *>(o); - if (!c) - return; switch (c->status()) { case QQmlComponent::Error: qWarning() << "QQmlApplicationEngine failed to load component"; diff --git a/src/qml/qml/qqmlapplicationengine.h b/src/qml/qml/qqmlapplicationengine.h index e64d7495cd..6c57f46c72 100644 --- a/src/qml/qml/qqmlapplicationengine.h +++ b/src/qml/qml/qqmlapplicationengine.h @@ -74,7 +74,6 @@ Q_SIGNALS: private: Q_DISABLE_COPY(QQmlApplicationEngine) Q_DECLARE_PRIVATE(QQmlApplicationEngine) - Q_PRIVATE_SLOT(d_func(), void _q_finishLoad(QObject*)) }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h index 8c342a43a9..4795170bed 100644 --- a/src/qml/qml/qqmlapplicationengine_p.h +++ b/src/qml/qml/qqmlapplicationengine_p.h @@ -42,7 +42,6 @@ #include "qqmlapplicationengine.h" #include "qqmlengine_p.h" -#include <QSignalMapper> #include <QCoreApplication> #include <QFileInfo> #include <QLibraryInfo> @@ -73,9 +72,8 @@ public: void startLoad(const QUrl &url, const QByteArray &data = QByteArray(), bool dataFlag = false); void loadTranslations(const QUrl &rootFile); - void _q_finishLoad(QObject *component); + void finishLoad(QQmlComponent *component); QList<QObject *> objects; - QSignalMapper statusMapper; QObject *appObj; #if QT_CONFIG(translation) diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 4fbd828307..19ece44beb 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -199,7 +199,7 @@ void QQmlBoundSignalExpression::evaluate(void **a) // for several cases (such as QVariant type and QObject-derived types) //args[ii] = engine->metaTypeToJS(type, a[ii + 1]); if (type == qMetaTypeId<QJSValue>()) { - if (QV4::Value *v4Value = QJSValuePrivate::getValue(reinterpret_cast<QJSValue *>(a[ii + 1]))) + if (QV4::Value *v4Value = QJSValuePrivate::valueForData(reinterpret_cast<QJSValue *>(a[ii + 1]), &callData->args[ii])) callData->args[ii] = *v4Value; else callData->args[ii] = QV4::Encode::undefined(); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index e1fa97b52f..e99335a117 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -65,6 +65,7 @@ #include <QtCore/qmetaobject.h> #include <QDebug> #include <QtCore/qcoreapplication.h> +#include <QtCore/qcryptographichash.h> #include <QtCore/qdir.h> #include <QtCore/qmutex.h> #include <QtCore/qthread.h> @@ -564,7 +565,7 @@ The following functions are also on the Qt object. \l{Screen} attached object. In practice the array corresponds to the screen list returned by QGuiApplication::screens(). In addition to examining properties like name, width, height, etc., the array elements can also be - assigned to the targetScreen property of Window items, thus serving as an + assigned to the screen property of Window items, thus serving as an alternative to the C++ side's QWindow::setScreen(). This property has been added in Qt 5.9. @@ -585,7 +586,7 @@ The following functions are also on the Qt object. \li application.font \endlist - \sa Screen, Window, Window.targetScreen + \sa Screen, Window, Window.screen */ /*! @@ -760,7 +761,7 @@ class QQmlThreadNotifierProxyObject : public QObject public: QPointer<QObject> target; - virtual int qt_metacall(QMetaObject::Call, int methodIndex, void **a) { + int qt_metacall(QMetaObject::Call, int methodIndex, void **a) override { if (!target) return -1; @@ -2178,6 +2179,27 @@ QString QQmlEngine::offlineStoragePath() const return d->offlineStoragePath; } +/*! + Returns the file path where a \l{QtQuick.LocalStorage}{Local Storage} + database with the identifier \a databaseName is (or would be) located. + + \sa LocalStorage.openDatabaseSync() + \since 5.9 +*/ +QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName) const +{ + Q_D(const QQmlEngine); + QCryptographicHash md5(QCryptographicHash::Md5); + md5.addData(databaseName.toUtf8()); + return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex()); +} + +QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const +{ + Q_Q(const QQmlEngine); + return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator(); +} + QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersion) { QList<QQmlType *> types; diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 3102a20fac..8cada954fe 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -136,6 +136,7 @@ public: void setOfflineStoragePath(const QString& dir); QString offlineStoragePath() const; + QString offlineStorageDatabaseFilePath(const QString &databaseName) const; QUrl baseUrl() const; void setBaseUrl(const QUrl &); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 916566b6c7..1bdeacd524 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -205,6 +205,7 @@ public: inline void deleteInEngineThread(T *); template<typename T> inline static void deleteInEngineThread(QQmlEngine *, T *); + QString offlineStorageDatabaseDirectory() const; // These methods may be called from the loader thread inline QQmlPropertyCache *cache(QQmlType *, int); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 8712b638c5..c07d5c740a 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2017 Crimson AS <info@crimson.no> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -130,42 +131,74 @@ bool isPathAbsolute(const QString &path) #endif } -// If the type does not already exist as a file import, add the type and return the new type -QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, +/* + \internal + + Fetches the QQmlType instance registered for \a urlString, creating a + registration for it if it is not already registered, using the associated + \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion + details. + + Errors (if there are any) are placed into \a errors, if it is nonzero. Note + that errors are treated as fatal if \a errors is not set. +*/ +QQmlType *fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, bool isCompositeSingleton, QList<QQmlError> *errors, int majorVersion=-1, int minorVersion=-1) { - QUrl url(urlString); + QUrl url(urlString); // ### unfortunate (costly) conversion QQmlType *ret = QQmlMetaType::qmlType(url); - if (!ret) { //QQmlType not yet existing for composite or composite singleton type - int dot = typeName.indexOf(QLatin1Char('.')); - QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1); - - //XXX: The constData of the string ref is pointing somewhere unsafe in qmlregister, so we need to create a temporary copy - QByteArray buf(unqualifiedtype.toString().toUtf8()); - - if (isCompositeSingleton) { - QQmlPrivate::RegisterCompositeSingletonType reg = { - url, - "", //Empty URI indicates loaded via file imports - majorVersion, - minorVersion, - buf.constData() - }; - ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeSingletonRegistration, ®)); - } else { - QQmlPrivate::RegisterCompositeType reg = { - url, - "", //Empty URI indicates loaded via file imports - majorVersion, - minorVersion, - buf.constData() - }; - ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®)); - } + if (ret) + return ret; + + int dot = typeName.indexOf(QLatin1Char('.')); + QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1); + + // We need a pointer, but we were passed a string. Take a copy so we + // can guarentee it will live long enough to reach qmlregister. + QByteArray buf(unqualifiedtype.toString().toUtf8()); + + // Register the type. Note that the URI parameters here are empty; for + // file type imports, we do not place them in a URI as we don't + // necessarily have a good and unique one (picture a library import, + // which may be found in multiple plugin locations on disk), but there + // are other reasons for this too. + // + // By not putting them in a URI, we prevent the types from being + // registered on a QQmlTypeModule; this is important, as once types are + // placed on there, they cannot be easily removed, meaning if the + // developer subsequently loads a different import (meaning different + // types) with the same URI (using, say, a different plugin path), it is + // very undesirable that we continue to associate the types from the + // "old" URI with that new module. + // + // Not having URIs also means that the types cannot be found by name + // etc, the only way to look them up is through QQmlImports -- for + // better or worse. + if (isCompositeSingleton) { + QQmlPrivate::RegisterCompositeSingletonType reg = { + url, + "", // uri + majorVersion, + minorVersion, + buf.constData() + }; + ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeSingletonRegistration, ®)); + } else { + QQmlPrivate::RegisterCompositeType reg = { + url, + "", // uri + majorVersion, + minorVersion, + buf.constData() + }; + ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®)); } - if (!ret) {//Usually when a type name is "found" but invalid - //qDebug() << ret << urlString << QQmlMetaType::qmlType(url); + + // This means that the type couldn't be found by URL, but could not be + // registered either, meaning we most likely were passed some kind of bad + // data. + if (!ret) { if (!errors) // Cannot list errors properly, just quit qFatal("%s", QQmlMetaType::typeRegistrationFailures().join('\n').toLatin1().constData()); QQmlError error; @@ -204,44 +237,38 @@ void qmlClearEnginePlugins() typedef QPair<QStaticPlugin, QJsonArray> StaticPluginPair; #endif -class QQmlImportNamespace -{ -public: - QQmlImportNamespace() : nextNamespace(0) {} - ~QQmlImportNamespace() { qDeleteAll(imports); } - - struct Import { - QString uri; - QString url; - int majversion; - int minversion; - bool isLibrary; - QQmlDirComponents qmlDirComponents; - QQmlDirScripts qmlDirScripts; - - bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir, - QQmlImportNamespace *nameSpace, QList<QQmlError> *errors); - - static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); - - bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, - int *vmajor, int *vminor, QQmlType** type_return, - QString *base = 0, bool *typeRecursionDetected = 0) const; - }; - QList<Import *> imports; +/*! + \internal + \class QQmlImportInstance - Import *findImport(const QString &uri) const; + A QQmlImportType represents a single import of a document, held within a + namespace. - bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, - int *vmajor, int *vminor, QQmlType** type_return, - QString *base = 0, QList<QQmlError> *errors = 0); + \note The uri here may not necessarily be unique (e.g. for file imports). - // Prefix when used as a qualified import. Otherwise empty. - QHashedString prefix; + \note Version numbers may be -1 for file imports: this means that no + version was specified as part of the import. Type resolution will be + responsible for attempting to find the "best" possible version. +*/ - // Used by QQmlImportsPrivate::qualifiedSets - QQmlImportNamespace *nextNamespace; -}; +/*! + \internal + \class QQmlImportNamespace + + A QQmlImportNamespace is a way of seperating imports into a local namespace. + + Within a QML document, there is at least one namespace (the + "unqualified set") where imports without a qualifier are placed, i.e: + + import QtQuick 2.6 + + will have a single namespace (the unqualified set) containing a single import + for QtQuick 2.6. However, there may be others if an import statement gives + a qualifier, i.e the following will result in an additional new + QQmlImportNamespace in the qualified set: + + import MyFoo 1.0 as Foo +*/ class QQmlImportsPrivate { @@ -273,9 +300,12 @@ public: QString base; int ref; + // storage of data related to imports without a namespace mutable QQmlImportNamespace unqualifiedset; QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const; + + // storage of data related to imports with a namespace mutable QFieldList<QQmlImportNamespace, &QQmlImportNamespace::nextNamespace> qualifiedSets; QQmlTypeLoader *typeLoader; @@ -284,21 +314,21 @@ public: QQmlImportDatabase *database, QString *outQmldirFilePath, QString *outUrl); - static bool validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin, + static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, QList<QQmlError> *errors); bool importExtension(const QString &absoluteFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoader::QmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors); bool getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors); + const QQmlTypeLoaderQmldirContent **qmldir, QList<QQmlError> *errors); QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database); - QQmlImportNamespace::Import *addImportToNamespace(QQmlImportNamespace *nameSpace, + QQmlImportInstance *addImportToNamespace(QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, int vmaj, int vmin, QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, bool lowPrecedence = false); @@ -363,12 +393,22 @@ QUrl QQmlImports::baseUrl() const return d->baseUrl; } +/* + \internal + + This method is responsible for populating data of all types visible in this + document's imports into the \a cache for resolution elsewhere (e.g. in JS, + or when loading additional types). + + \note This is for C++ types only. Composite types are handled separately, + as they do not have a QQmlTypeModule. +*/ void QQmlImports::populateCache(QQmlTypeNameCache *cache) const { const QQmlImportNamespace &set = d->unqualifiedset; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion); if (module) { cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import->minversion)); @@ -379,11 +419,14 @@ void QQmlImports::populateCache(QQmlTypeNameCache *cache) const const QQmlImportNamespace &set = *ns; + // positioning is important; we must create the namespace even if there is no module. + QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix]; + typeimport.m_qualifier = set.prefix; + for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion); if (module) { - QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix]; typeimport.modules.append(QQmlTypeModuleVersion(module, import->minversion)); } } @@ -412,7 +455,7 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports:: typedef QQmlDirComponents::const_iterator ConstIterator; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); const QQmlDirComponents &components = import->qmlDirComponents; @@ -430,6 +473,15 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports:: } } +/* + \internal + + Returns a list of all composite singletons present in this document's + imports. + + This information is used by QQmlTypeLoader to ensure that composite singletons + are marked as dependencies during type loading. +*/ QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSingletons() const { QList<QQmlImports::CompositeSingletonReference> compositeSingletons; @@ -445,6 +497,12 @@ QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSi return compositeSingletons; } +/* + \internal + + Returns a list of scripts imported by this document. This is used by + QQmlTypeLoader to properly handle dependencies on imported scripts. +*/ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const { QList<QQmlImports::ScriptReference> scripts; @@ -452,7 +510,7 @@ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const const QQmlImportNamespace &set = d->unqualifiedset; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); for (const QQmlDirParser::Script &script : import->qmlDirScripts) { ScriptReference ref; @@ -466,7 +524,7 @@ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const const QQmlImportNamespace &set = *ns; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); for (const QQmlDirParser::Script &script : import->qmlDirScripts) { ScriptReference ref; @@ -590,7 +648,7 @@ bool QQmlImports::resolveType(const QHashedStringRef &type, return false; } -bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) +bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) { Q_ASSERT(resolvedUrl.endsWith(Slash)); url = resolvedUrl; @@ -600,7 +658,7 @@ bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, c const QQmlDirScripts &scripts = qmldir->scripts(); if (!scripts.isEmpty()) { // Verify that we haven't imported these scripts already - for (QList<QQmlImportNamespace::Import *>::const_iterator it = nameSpace->imports.constBegin(); + for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin(); it != nameSpace->imports.constEnd(); ++it) { if ((*it != this) && ((*it)->uri == uri)) { QQmlError error; @@ -616,7 +674,7 @@ bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, c return true; } -QQmlDirScripts QQmlImportNamespace::Import::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin) +QQmlDirScripts QQmlImportInstance::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin) { QMap<QString, QQmlDirParser::Script> versioned; @@ -652,7 +710,7 @@ bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QHashedStringRef &t return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return); } -bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, +bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, int *vmajor, int *vminor, QQmlType** type_return, QString *base, bool *typeRecursionDetected) const { @@ -683,15 +741,17 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, if ((candidate == end) || (c.majorVersion > candidate->majorVersion) || ((c.majorVersion == candidate->majorVersion) && (c.minorVersion > candidate->minorVersion))) { - componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName); - if (c.internal && base) { - if (resolveLocalUrl(*base, c.fileName) != componentUrl) - continue; // failed attempt to access an internal type - } - if (base && (*base == componentUrl)) { - if (typeRecursionDetected) - *typeRecursionDetected = true; - continue; // no recursion + if (base) { + componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName); + if (c.internal) { + if (resolveLocalUrl(*base, c.fileName) != componentUrl) + continue; // failed attempt to access an internal type + } + if (*base == componentUrl) { + if (typeRecursionDetected) + *typeRecursionDetected = true; + continue; // no recursion + } } // This is our best candidate so far @@ -702,9 +762,11 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, } if (candidate != end) { + if (!base) // ensure we have a componentUrl + componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName); int major = vmajor ? *vmajor : -1; int minor = vminor ? *vminor : -1; - QQmlType *returnType = getTypeForUrl(componentUrl, type, isCompositeSingleton, 0, + QQmlType *returnType = fetchOrCreateTypeForUrl(componentUrl, type, isCompositeSingleton, 0, major, minor); if (type_return) *type_return = returnType; @@ -732,7 +794,7 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, if (typeRecursionDetected) *typeRecursionDetected = true; } else { - QQmlType *returnType = getTypeForUrl(qmlUrl, type, false, 0); + QQmlType *returnType = fetchOrCreateTypeForUrl(qmlUrl, type, false, 0); if (type_return) *type_return = returnType; return returnType != 0; @@ -777,7 +839,7 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, return true; if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) { // qualified, and only 1 url - *type_return = getTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors); + *type_return = fetchOrCreateTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors); return (*type_return != 0); } } @@ -785,9 +847,9 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, return false; } -QQmlImportNamespace::Import *QQmlImportNamespace::findImport(const QString &uri) const +QQmlImportInstance *QQmlImportNamespace::findImport(const QString &uri) const { - for (Import *import : imports) { + for (QQmlImportInstance *import : imports) { if (import->uri == uri) return import; } @@ -800,13 +862,13 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS { bool typeRecursionDetected = false; for (int i=0; i<imports.count(); ++i) { - const Import *import = imports.at(i); + const QQmlImportInstance *import = imports.at(i); if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, base, &typeRecursionDetected)) { if (qmlCheckTypes()) { // check for type clashes for (int j = i+1; j<imports.count(); ++j) { - const Import *import2 = imports.at(j); + const QQmlImportInstance *import2 = imports.at(j); if (import2->resolveType(typeLoader, type, vmajor, vminor, 0, base)) { if (errors) { QString u1 = import->url; @@ -949,10 +1011,12 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res } #endif +#if defined(QT_SHARED) || !QT_CONFIG(library) static inline QString msgCannotLoadPlugin(const QString &uri, const QString &why) { return QQmlImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri, why); } +#endif /*! Import an extension defined by a qmldir file. @@ -963,7 +1027,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoader::QmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors) { #if QT_CONFIG(library) @@ -1099,7 +1163,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, } bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors) + const QQmlTypeLoaderQmldirContent **qmldir, QList<QQmlError> *errors) { Q_ASSERT(errors); Q_ASSERT(qmldir); @@ -1224,7 +1288,7 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ return false; } -bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin, +bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, QList<QQmlError> *errors) { int lowest_min = INT_MAX; @@ -1307,7 +1371,7 @@ QQmlImportNamespace *QQmlImportsPrivate::importNamespace(const QString &prefix) return nameSpace; } -QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace, +QQmlImportInstance *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, int vmaj, int vmin, QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, bool lowPrecedence) @@ -1317,7 +1381,7 @@ QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImport Q_UNUSED(errors); Q_ASSERT(url.isEmpty() || url.endsWith(Slash)); - QQmlImportNamespace::Import *import = new QQmlImportNamespace::Import; + QQmlImportInstance *import = new QQmlImportInstance; import->uri = uri; import->url = url; import->majversion = vmaj; @@ -1343,11 +1407,11 @@ bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &pre QQmlImportNamespace *nameSpace = importNamespace(prefix); Q_ASSERT(nameSpace); - QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, uri, qmldirUrl, vmaj, vmin, QV4::CompiledData::Import::ImportLibrary, errors); + QQmlImportInstance *inserted = addImportToNamespace(nameSpace, uri, qmldirUrl, vmaj, vmin, QV4::CompiledData::Import::ImportLibrary, errors); Q_ASSERT(inserted); if (!incomplete) { - const QQmlTypeLoader::QmldirContent *qmldir = 0; + const QQmlTypeLoaderQmldirContent *qmldir = 0; if (!qmldirIdentifier.isEmpty()) { if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) @@ -1444,11 +1508,11 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix if (!url.endsWith(Slash) && !url.endsWith(Backslash)) url += Slash; - QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QV4::CompiledData::Import::ImportFile, errors, isImplicitImport); + QQmlImportInstance *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QV4::CompiledData::Import::ImportFile, errors, isImplicitImport); Q_ASSERT(inserted); if (!incomplete && !qmldirIdentifier.isEmpty()) { - const QQmlTypeLoader::QmldirContent *qmldir = 0; + const QQmlTypeLoaderQmldirContent *qmldir = 0; if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors)) return false; @@ -1471,8 +1535,8 @@ bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString & QQmlImportNamespace *nameSpace = importNamespace(prefix); Q_ASSERT(nameSpace); - if (QQmlImportNamespace::Import *import = nameSpace->findImport(uri)) { - const QQmlTypeLoader::QmldirContent *qmldir = 0; + if (QQmlImportInstance *import = nameSpace->findImport(uri)) { + const QQmlTypeLoaderQmldirContent *qmldir = 0; if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) return false; diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index 0e7848730f..7c691a468c 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -68,6 +68,48 @@ class QQmlImportNamespace; class QQmlImportsPrivate; class QQmlImportDatabase; class QQmlTypeLoader; +class QQmlTypeLoaderQmldirContent; + +struct QQmlImportInstance +{ + QString uri; // e.g. QtQuick + QString url; // the base path of the import + int majversion; // the major version imported + int minversion; // the minor version imported + bool isLibrary; // true means that this is not a file import + QQmlDirComponents qmlDirComponents; // a copy of the components listed in the qmldir + QQmlDirScripts qmlDirScripts; // a copy of the scripts in the qmldir + + bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, + QQmlImportNamespace *nameSpace, QList<QQmlError> *errors); + + static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); + + bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, + int *vmajor, int *vminor, QQmlType** type_return, + QString *base = 0, bool *typeRecursionDetected = 0) const; +}; + +class QQmlImportNamespace +{ +public: + QQmlImportNamespace() : nextNamespace(0) {} + ~QQmlImportNamespace() { qDeleteAll(imports); } + + QList<QQmlImportInstance *> imports; + + QQmlImportInstance *findImport(const QString &uri) const; + + bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, + int *vmajor, int *vminor, QQmlType** type_return, + QString *base = 0, QList<QQmlError> *errors = 0); + + // Prefix when used as a qualified import. Otherwise empty. + QHashedString prefix; + + // Used by QQmlImportsPrivate::qualifiedSets + QQmlImportNamespace *nextNamespace; +}; class Q_QML_PRIVATE_EXPORT QQmlImports { diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 8aa107dc17..43677e0d78 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -140,12 +140,13 @@ ReturnedValue QmlListWrapper::getIndexed(const Managed *m, uint index, bool *has return Primitive::undefinedValue().asReturnedValue(); } -void QmlListWrapper::put(Managed *m, String *name, const Value &value) +bool QmlListWrapper::put(Managed *m, String *name, const Value &value) { // doesn't do anything. Should we throw? Q_UNUSED(m); Q_UNUSED(name); Q_UNUSED(value); + return false; } void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) @@ -165,4 +166,29 @@ void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name return QV4::Object::advanceIterator(m, it, name, index, p, attrs); } +void PropertyListPrototype::init(ExecutionEngine *) +{ + defineDefaultProperty(QStringLiteral("push"), method_push, 1); +} + +void PropertyListPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + if (!instance) + RETURN_UNDEFINED(); + QmlListWrapper *w = instance->as<QmlListWrapper>(); + if (!w) + RETURN_UNDEFINED(); + if (!w->d()->property().append) + THROW_GENERIC_ERROR("List doesn't define an Append function"); + + QV4::ScopedObject so(scope); + for (int i = 0; i < callData->argc; ++i) + { + so = callData->args[i].toObject(scope.engine); + if (QV4::QObjectWrapper *wrapper = so->as<QV4::QObjectWrapper>()) + w->d()->property().append(&w->d()->property(), wrapper->object() ); + } +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h index d01b332159..84dadba01a 100644 --- a/src/qml/qml/qqmllistwrapper_p.h +++ b/src/qml/qml/qqmllistwrapper_p.h @@ -86,6 +86,7 @@ struct Q_QML_EXPORT QmlListWrapper : Object { V4_OBJECT2(QmlListWrapper, Object) V4_NEEDS_DESTROY + V4_PROTOTYPE(propertyListPrototype) static ReturnedValue create(ExecutionEngine *engine, QObject *object, int propId, int propType); static ReturnedValue create(ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, int propType); @@ -94,10 +95,17 @@ struct Q_QML_EXPORT QmlListWrapper : Object static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + static bool put(Managed *m, String *name, const Value &value); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); }; +struct PropertyListPrototype : Object +{ + void init(ExecutionEngine *engine); + + static void method_push(const BuiltinFunction *, Scope &, CallData *callData); +}; + } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 520c44f4da..bd6b9a1599 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -79,7 +79,7 @@ struct QQmlMetaTypeData Files urlToNonFileImportType; // For non-file imported composite and composite // singleton types. This way we can locate any // of them by url, even if it was registered as - // a module via qmlRegisterCompositeType. + // a module via QQmlPrivate::RegisterCompositeType typedef QHash<const QMetaObject *, QQmlType *> MetaObjects; MetaObjects metaObjectToType; typedef QHash<int, QQmlMetaType::StringConverter> StringConverters; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 38a16b8cde..85fbd86dc4 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -170,7 +170,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI context = new QQmlContextData; context->isInternal = true; - context->imports = compilationUnit->importCache; + context->imports = compilationUnit->typeNameCache; context->initFromTypeCompilationUnit(compilationUnit, subComponentIndex); context->setParent(parentContext); @@ -1150,7 +1150,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) { customParser->engine = QQmlEnginePrivate::get(engine); - customParser->imports = compilationUnit->importCache; + customParser->imports = compilationUnit->typeNameCache; QList<const QV4::CompiledData::Binding *> bindings; const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 562aa1c88a..88ce2fa1b9 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -76,7 +76,8 @@ public: int argumentsValid:1; QList<QByteArray> *names; - int arguments[0]; + + int arguments[1]; }; // Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick @@ -919,7 +920,7 @@ static int EnumType(const QMetaObject *metaobj, const QByteArray &str, int type) QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names) { typedef QQmlPropertyCacheMethodArguments A; - A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int))); + A *args = static_cast<A *>(malloc(sizeof(A) + (argc) * sizeof(int))); args->arguments[0] = argc; args->argumentsValid = false; args->signalParameterStringForJS = 0; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 5b1bba46dd..f4f04e12c0 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1362,7 +1362,7 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier); + const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier); const auto qmldirScripts = qmldir->scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); @@ -1410,7 +1410,7 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath); + const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath); const auto qmldirScripts = qmldir->scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); @@ -1539,57 +1539,57 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE } -QQmlTypeLoader::QmldirContent::QmldirContent() +QQmlTypeLoaderQmldirContent::QQmlTypeLoaderQmldirContent() { } -bool QQmlTypeLoader::QmldirContent::hasError() const +bool QQmlTypeLoaderQmldirContent::hasError() const { return m_parser.hasError(); } -QList<QQmlError> QQmlTypeLoader::QmldirContent::errors(const QString &uri) const +QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const { return m_parser.errors(uri); } -QString QQmlTypeLoader::QmldirContent::typeNamespace() const +QString QQmlTypeLoaderQmldirContent::typeNamespace() const { return m_parser.typeNamespace(); } -void QQmlTypeLoader::QmldirContent::setContent(const QString &location, const QString &content) +void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) { m_location = location; m_parser.parse(content); } -void QQmlTypeLoader::QmldirContent::setError(const QQmlError &error) +void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error) { m_parser.setError(error); } -QQmlDirComponents QQmlTypeLoader::QmldirContent::components() const +QQmlDirComponents QQmlTypeLoaderQmldirContent::components() const { return m_parser.components(); } -QQmlDirScripts QQmlTypeLoader::QmldirContent::scripts() const +QQmlDirScripts QQmlTypeLoaderQmldirContent::scripts() const { return m_parser.scripts(); } -QQmlDirPlugins QQmlTypeLoader::QmldirContent::plugins() const +QQmlDirPlugins QQmlTypeLoaderQmldirContent::plugins() const { return m_parser.plugins(); } -QString QQmlTypeLoader::QmldirContent::pluginLocation() const +QString QQmlTypeLoaderQmldirContent::pluginLocation() const { return m_location; } -bool QQmlTypeLoader::QmldirContent::designerSupported() const +bool QQmlTypeLoaderQmldirContent::designerSupported() const { return m_parser.designerSupported(); } @@ -1861,13 +1861,13 @@ bool QQmlTypeLoader::directoryExists(const QString &path) /*! -Return a QmldirContent for absoluteFilePath. The QmldirContent may be cached. +Return a QQmlTypeLoaderQmldirContent for absoluteFilePath. The QQmlTypeLoaderQmldirContent may be cached. \a filePath is a local file path. It can also be a remote path for a remote directory import, but it will have been cached by now in this case. */ -const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn) +const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn) { QUrl url(filePathIn); //May already contain http scheme if (url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https")) @@ -1883,10 +1883,10 @@ const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString else filePath = url.path(); - QmldirContent *qmldir; - QmldirContent **val = m_importQmlDirCache.value(filePath); + QQmlTypeLoaderQmldirContent *qmldir; + QQmlTypeLoaderQmldirContent **val = m_importQmlDirCache.value(filePath); if (!val) { - qmldir = new QmldirContent; + qmldir = new QQmlTypeLoaderQmldirContent; #define ERROR(description) { QQmlError e; e.setDescription(description); qmldir->setError(e); } #define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable")) @@ -1916,12 +1916,12 @@ const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString void QQmlTypeLoader::setQmldirContent(const QString &url, const QString &content) { - QmldirContent *qmldir; - QmldirContent **val = m_importQmlDirCache.value(url); + QQmlTypeLoaderQmldirContent *qmldir; + QQmlTypeLoaderQmldirContent **val = m_importQmlDirCache.value(url); if (val) { qmldir = *val; } else { - qmldir = new QmldirContent; + qmldir = new QQmlTypeLoaderQmldirContent; m_importQmlDirCache.insert(url, qmldir); } @@ -2075,6 +2075,11 @@ bool QQmlTypeData::tryLoadFromDiskCache() } } + if (unit->data->flags & QV4::CompiledData::Unit::PendingTypeCompilation) { + restoreIR(unit); + return true; + } + m_compiledData = unit; for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) @@ -2127,11 +2132,11 @@ bool QQmlTypeData::tryLoadFromDiskCache() return true; } -void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &importCache, +void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) { Q_ASSERT(m_compiledData); - m_compiledData->importCache = importCache; + m_compiledData->typeNameCache = typeNameCache; m_compiledData->resolvedTypes = resolvedTypeCache; QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); @@ -2217,10 +2222,10 @@ void QQmlTypeData::done() } } - QQmlRefPointer<QQmlTypeNameCache> importCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache; { - QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache); + QQmlCompileError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); if (error.isSet()) { setError(error); return; @@ -2240,9 +2245,9 @@ void QQmlTypeData::done() if (!m_document.isNull()) { // Compile component - compile(importCache, resolvedTypeCache); + compile(typeNameCache, resolvedTypeCache); } else { - createTypeAndPropertyCaches(importCache, resolvedTypeCache); + createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); } if (isError()) @@ -2303,7 +2308,7 @@ void QQmlTypeData::done() qualifier = qualifier.mid(lastDotIndex+1); } - m_compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); + m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); QQmlScriptData *scriptData = script.script->scriptData(); scriptData->addref(); m_compiledData->dependentScripts << scriptData; @@ -2397,6 +2402,15 @@ bool QQmlTypeData::loadFromSource() return true; } +void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit) +{ + m_document.reset(new QmlIR::Document(isDebugging())); + QmlIR::IRLoader loader(unit->data, m_document.data()); + loader.load(); + m_document->javaScriptCompilationUnit = unit; + continueLoadFromIR(); +} + void QQmlTypeData::continueLoadFromIR() { m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); @@ -2489,12 +2503,12 @@ QString QQmlTypeData::stringAt(int index) const return m_document->jsGenerator.stringTable.stringForIndex(index); } -void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) +void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) { Q_ASSERT(m_compiledData.isNull()); QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine()); - QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), importCache, resolvedTypeCache); + QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache); m_compiledData = compiler.compile(); if (!m_compiledData) { setError(compiler.compilationErrors()); @@ -2598,20 +2612,20 @@ void QQmlTypeData::resolveTypes() } QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *importCache, + QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache ) const { - importCache->adopt(new QQmlTypeNameCache); + typeNameCache->adopt(new QQmlTypeNameCache(m_importCache)); for (const QString &ns: m_namespaces) - (*importCache)->add(ns); + (*typeNameCache)->add(ns); // Add any Composite Singletons that were used to the import cache for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) - (*importCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); + (*typeNameCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); - m_importCache.populateCache(*importCache); + m_importCache.populateCache(*typeNameCache); QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); @@ -2710,7 +2724,7 @@ void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData: } QQmlScriptData::QQmlScriptData() - : importCache(0) + : typeNameCache(0) , m_loaded(false) , m_program(0) { @@ -2767,8 +2781,8 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent // For backward compatibility, if there are no imports, we need to use the // imports from the parent context. See QTBUG-17518. - if (!importCache->isEmpty()) { - ctxt->imports = importCache; + if (!typeNameCache->isEmpty()) { + ctxt->imports = typeNameCache; } else if (effectiveCtxt) { ctxt->imports = effectiveCtxt->imports; ctxt->importedScripts = effectiveCtxt->importedScripts; @@ -2823,9 +2837,9 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent void QQmlScriptData::clear() { - if (importCache) { - importCache->release(); - importCache = 0; + if (typeNameCache) { + typeNameCache->release(); + typeNameCache = 0; } for (int ii = 0; ii < scripts.count(); ++ii) @@ -2946,7 +2960,7 @@ void QQmlScriptBlob::done() } } - m_scriptData->importCache = new QQmlTypeNameCache(); + m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache); QSet<QString> ns; @@ -2958,13 +2972,13 @@ void QQmlScriptBlob::done() if (!script.nameSpace.isNull()) { if (!ns.contains(script.nameSpace)) { ns.insert(script.nameSpace); - m_scriptData->importCache->add(script.nameSpace); + m_scriptData->typeNameCache->add(script.nameSpace); } } - m_scriptData->importCache->add(script.qualifier, scriptIndex, script.nameSpace); + m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace); } - m_importCache.populateCache(m_scriptData->importCache); + m_importCache.populateCache(m_scriptData->typeNameCache); } QString QQmlScriptBlob::stringAt(int index) const diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index c60435a2d6..915b1bcc4c 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -218,6 +218,34 @@ private: class QQmlTypeLoaderThread; +class QQmlTypeLoaderQmldirContent +{ +private: + friend class QQmlTypeLoader; + QQmlTypeLoaderQmldirContent(); + + void setContent(const QString &location, const QString &content); + void setError(const QQmlError &); + +public: + bool hasError() const; + QList<QQmlError> errors(const QString &uri) const; + + QString typeNamespace() const; + + QQmlDirComponents components() const; + QQmlDirScripts scripts() const; + QQmlDirPlugins plugins() const; + + QString pluginLocation() const; + + bool designerSupported() const; + +private: + QQmlDirParser m_parser; + QString m_location; +}; + class Q_AUTOTEST_EXPORT QQmlTypeLoader { Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader) @@ -256,34 +284,6 @@ public: QList<QQmlQmldirData *> m_qmldirs; }; - class QmldirContent - { - private: - friend class QQmlTypeLoader; - QmldirContent(); - - void setContent(const QString &location, const QString &content); - void setError(const QQmlError &); - - public: - bool hasError() const; - QList<QQmlError> errors(const QString &uri) const; - - QString typeNamespace() const; - - QQmlDirComponents components() const; - QQmlDirScripts scripts() const; - QQmlDirPlugins plugins() const; - - QString pluginLocation() const; - - bool designerSupported() const; - - private: - QQmlDirParser m_parser; - QString m_location; - }; - QQmlTypeLoader(QQmlEngine *); ~QQmlTypeLoader(); @@ -298,7 +298,7 @@ public: QString absoluteFilePath(const QString &path); bool directoryExists(const QString &path); - const QmldirContent *qmldirContent(const QString &filePath); + const QQmlTypeLoaderQmldirContent *qmldirContent(const QString &filePath); void setQmldirContent(const QString &filePath, const QString &content); void clearCache(); @@ -363,7 +363,7 @@ private: typedef QHash<QUrl, QQmlQmldirData *> QmldirCache; typedef QStringHash<bool> StringSet; typedef QStringHash<StringSet*> ImportDirCache; - typedef QStringHash<QmldirContent *> ImportQmlDirCache; + typedef QStringHash<QQmlTypeLoaderQmldirContent *> ImportQmlDirCache; QQmlEngine *m_engine; QQmlTypeLoaderThread *m_thread; @@ -446,15 +446,16 @@ protected: private: bool tryLoadFromDiskCache(); bool loadFromSource(); + void restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit); void continueLoadFromIR(); void resolveTypes(); QQmlCompileError buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *importCache, + QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache ) const; - void compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache, + void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); - void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &importCache, + 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); @@ -504,7 +505,7 @@ public: QUrl url; QString urlString; - QQmlTypeNameCache *importCache; + QQmlTypeNameCache *typeNameCache; QList<QQmlScriptBlob *> scripts; QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp index c2098bc9a1..c8e2b92c29 100644 --- a/src/qml/qml/qqmltypenamecache.cpp +++ b/src/qml/qml/qqmltypenamecache.cpp @@ -43,7 +43,8 @@ QT_BEGIN_NAMESPACE -QQmlTypeNameCache::QQmlTypeNameCache() +QQmlTypeNameCache::QQmlTypeNameCache(const QQmlImports &importCache) + : m_imports(importCache) { } @@ -70,6 +71,7 @@ void QQmlTypeNameCache::add(const QHashedString &name, int importedScriptIndex, { Import import; import.scriptIndex = importedScriptIndex; + import.m_qualifier = name; if (nameSpace.length() != 0) { Import *i = m_namedImports.value(nameSpace); @@ -94,6 +96,18 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name) if (!result.isValid()) result = query(m_anonymousCompositeSingletons, name); + if (!result.isValid()) { + // Look up anonymous types from the imports of this document + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(name, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + + } + return result; } @@ -109,6 +123,20 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name, if (!result.isValid()) result = query(i->compositeSingletons, name); + if (!result.isValid()) { + // Look up types from the imports of this document + // ### it would be nice if QQmlImports allowed us to resolve a namespace + // first, and then types on it. + QString qualifiedTypeName = i->m_qualifier + QLatin1Char('.') + name.toString(); + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(qualifiedTypeName, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + } + return result; } @@ -122,6 +150,19 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name) cons if (!result.isValid()) result = query(m_anonymousCompositeSingletons, name); + if (!result.isValid()) { + // Look up anonymous types from the imports of this document + QString typeName = name->toQStringNoThrow(); + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(typeName, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + + } + return result; } @@ -143,6 +184,20 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, cons if (!r.isValid()) r = query(i->compositeSingletons, name); + if (!r.isValid()) { + // Look up types from the imports of this document + // ### it would be nice if QQmlImports allowed us to resolve a namespace + // first, and then types on it. + QString qualifiedTypeName = i->m_qualifier + QLatin1Char('.') + name->toQStringNoThrow(); + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(qualifiedTypeName, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + } + return r; } diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h index 8a387bed5f..7cdcbe91b6 100644 --- a/src/qml/qml/qqmltypenamecache_p.h +++ b/src/qml/qml/qqmltypenamecache_p.h @@ -56,6 +56,7 @@ #include "qqmlmetatype_p.h" #include <private/qhashedstring_p.h> +#include <private/qqmlimport_p.h> #include <QtCore/qvector.h> @@ -66,7 +67,7 @@ class QQmlEngine; class QQmlTypeNameCache : public QQmlRefCount { public: - QQmlTypeNameCache(); + QQmlTypeNameCache(const QQmlImports &imports); virtual ~QQmlTypeNameCache(); inline bool isEmpty() const; @@ -105,6 +106,9 @@ private: // Or, imported compositeSingletons QStringHash<QUrl> compositeSingletons; + + // The qualifier of this import + QString m_qualifier; }; template<typename Key> @@ -112,6 +116,7 @@ private: { Import *i = imports.value(key); if (i) { + Q_ASSERT(!i->m_qualifier.isEmpty()); if (i->scriptIndex != -1) { return Result(i->scriptIndex); } else { @@ -151,6 +156,7 @@ private: QMap<const Import *, QStringHash<Import> > m_namespacedImports; QVector<QQmlTypeModuleVersion> m_anonymousImports; QStringHash<QUrl> m_anonymousCompositeSingletons; + QQmlImports m_imports; }; QQmlTypeNameCache::Result::Result() diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index fd1e9cc2be..c4422afa9c 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -275,13 +275,13 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope } -void QmlTypeWrapper::put(Managed *m, String *name, const Value &value) +bool QmlTypeWrapper::put(Managed *m, String *name, const Value &value) { Q_ASSERT(m->as<QmlTypeWrapper>()); QmlTypeWrapper *w = static_cast<QmlTypeWrapper *>(m); QV4::ExecutionEngine *v4 = w->engine(); if (v4->hasException) - return; + return false; QV4::Scope scope(v4); QQmlContextData *context = v4->callingQmlContext(); @@ -292,7 +292,8 @@ void QmlTypeWrapper::put(Managed *m, String *name, const Value &value) QQmlEngine *e = scope.engine->qmlEngine(); QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(QQmlEnginePrivate::get(e)), object); if (ao) - QV4::QObjectWrapper::setQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); + return QV4::QObjectWrapper::setQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); + return false; } else if (type && type->isSingleton()) { QQmlEngine *e = scope.engine->qmlEngine(); QQmlType::SingletonInstanceInfo *siinfo = type->singletonInstanceInfo(); @@ -300,18 +301,20 @@ void QmlTypeWrapper::put(Managed *m, String *name, const Value &value) QObject *qobjectSingleton = siinfo->qobjectApi(e); if (qobjectSingleton) { - QV4::QObjectWrapper::setQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); + return QV4::QObjectWrapper::setQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); } else if (!siinfo->scriptApi(e).isUndefined()) { QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); if (!apiprivate) { QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); v4->throwError(error); - return; + return false; } else { - apiprivate->put(name, value); + return apiprivate->put(name, value); } } } + + return false; } PropertyAttributes QmlTypeWrapper::query(const Managed *m, String *name) diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 3b0ae04cc1..cfb6cb0ec9 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -100,7 +100,7 @@ struct Q_QML_EXPORT QmlTypeWrapper : Object static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + static bool put(Managed *m, String *name, const Value &value); static PropertyAttributes query(const Managed *, String *name); static bool isEqualTo(Managed *that, Managed *o); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 44b612e7d2..d262b230e2 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -290,9 +290,11 @@ int QQmlValueTypeWrapper::typeId() const bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const { bool destructGadgetOnExit = false; + Q_ALLOCA_DECLARE(void, gadget); if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) { if (!d()->gadgetPtr) { - d()->gadgetPtr = alloca(d()->valueType->metaType.sizeOf()); + Q_ALLOCA_ASSIGN(void, gadget, d()->valueType->metaType.sizeOf()); + d()->gadgetPtr = gadget; d()->valueType->metaType.construct(d()->gadgetPtr, 0); destructGadgetOnExit = true; } @@ -407,13 +409,13 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha #undef VALUE_TYPE_ACCESSOR } -void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) +bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) { Q_ASSERT(m->as<QQmlValueTypeWrapper>()); ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine(); Scope scope(v4); if (scope.hasException()) - return; + return false; Scoped<QQmlValueTypeWrapper> r(scope, static_cast<QQmlValueTypeWrapper *>(m)); Scoped<QQmlValueTypeReference> reference(scope, m->d()); @@ -424,7 +426,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QMetaProperty writebackProperty = reference->d()->object->metaObject()->property(reference->d()->property); if (!writebackProperty.isWritable() || !reference->readReferenceValue()) - return; + return false; writeBackPropertyType = writebackProperty.userType(); } @@ -432,7 +434,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) const QMetaObject *metaObject = r->d()->propertyCache()->metaObject(); const QQmlPropertyData *pd = r->d()->propertyCache()->property(name, 0, 0); if (!pd) - return; + return false; if (reference) { QV4::ScopedFunctionObject f(scope, value); @@ -442,7 +444,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QString error = QStringLiteral("Cannot assign JavaScript function to value-type property"); ScopedString e(scope, v4->newString(error)); v4->throwError(e); - return; + return false; } QQmlContextData *context = v4->callingQmlContext(); @@ -459,7 +461,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) newBinding->setSourceLocation(bindingFunction->currentLocation()); newBinding->setTarget(reference->d()->object, cacheData, pd); QQmlPropertyPrivate::setBinding(newBinding); - return; + return true; } else { QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex())); } @@ -493,6 +495,8 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a); } } + + return true; } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index 87f9116056..c8aac719ab 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -106,7 +106,7 @@ public: bool write(QObject *target, int propertyIndex) const; static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + static bool put(Managed *m, String *name, const Value &value); static bool isEqualTo(Managed *m, Managed *other); static PropertyAttributes query(const Managed *, String *name); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 545daa96f8..490a4e19ab 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -325,9 +325,12 @@ QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj, if (compiledObject->nProperties || compiledObject->nFunctions) { Q_ASSERT(cache && cache->engine); QV4::ExecutionEngine *v4 = cache->engine; - QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, compiledObject->nProperties + compiledObject->nFunctions); - propertyAndMethodStorage.set(v4, data); - std::fill(data->data, data->data + data->size, QV4::Encode::undefined()); + uint size = compiledObject->nProperties + compiledObject->nFunctions; + 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()); + } // Need JS wrapper to ensure properties/methods are marked. ensureQObjectWrapper(); diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h index 63585fd62e..026be5a703 100644 --- a/src/qml/qtqmlglobal_p.h +++ b/src/qml/qtqmlglobal_p.h @@ -55,6 +55,46 @@ #include <QtQml/private/qtqml-config_p.h> #include <QtQml/qtqmlglobal.h> +// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing +// the occurrences of alloca() in case it's not supported. +// Q_ALLOCA_DECLARE and Q_ALLOCA_ASSIGN macros separate +// memory allocation from the declaration and RAII. +#define Q_ALLOCA_VAR(type, name, size) \ + Q_ALLOCA_DECLARE(type, name); \ + Q_ALLOCA_ASSIGN(type, name, size) + +#if QT_CONFIG(alloca) + +#define Q_ALLOCA_DECLARE(type, name) \ + type *name = 0 + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + name = static_cast<type*>(alloca(size)) + +#else +QT_BEGIN_NAMESPACE +class Qt_AllocaWrapper +{ +public: + Qt_AllocaWrapper() { m_data = 0; } + ~Qt_AllocaWrapper() { free(m_data); } + void *data() { return m_data; } + void allocate(int size) { m_data = malloc(size); } +private: + void *m_data; +}; +QT_END_NAMESPACE + +#define Q_ALLOCA_DECLARE(type, name) \ + Qt_AllocaWrapper _qt_alloca_##name; \ + type *name = 0 + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + _qt_alloca_##name.allocate(size); \ + name = static_cast<type*>(_qt_alloca_##name.data()) + +#endif + #if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) # define Q_QML_PRIVATE_EXPORT #else diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index cc4ccbaeb1..efc2828dc5 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -1268,7 +1268,7 @@ void ModelNodeMetaObject::updateValues() const int roleCount = m_model->m_listModel->roleCount(); if (!m_initialized) { if (roleCount) { - int *changedRoles = reinterpret_cast<int *>(alloca(roleCount * sizeof(int))); + Q_ALLOCA_VAR(int, changedRoles, roleCount * sizeof(int)); for (int i = 0; i < roleCount; ++i) changedRoles[i] = i; emitDirectNotifies(changedRoles, roleCount); @@ -1333,7 +1333,7 @@ void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCo namespace QV4 { -void ModelObject::put(Managed *m, String *name, const Value &value) +bool ModelObject::put(Managed *m, String *name, const Value &value) { ModelObject *that = static_cast<ModelObject*>(m); @@ -1347,6 +1347,7 @@ void ModelObject::put(Managed *m, String *name, const Value &value) ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object()); if (mo->initialized()) mo->emitPropertyNotification(name->toQString().toUtf8()); + return true; } ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty) diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index cdce78e542..44583df2a6 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -179,7 +179,7 @@ struct ModelObject : public QObjectWrapper { struct ModelObject : public QObjectWrapper { - static void put(Managed *m, String *name, const Value& value); + static bool put(Managed *m, String *name, const Value& value); static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); diff --git a/src/qml/types/qquickpackage.cpp b/src/qml/types/qquickpackage.cpp index 47d9f2f483..17eff5ac40 100644 --- a/src/qml/types/qquickpackage.cpp +++ b/src/qml/types/qquickpackage.cpp @@ -89,7 +89,7 @@ public: { DataGuard(QObject *obj, QList<DataGuard> *l) : list(l) { (QQmlGuard<QObject>&)*this = obj; } QList<DataGuard> *list; - void objectDestroyed(QObject *) { + void objectDestroyed(QObject *) override { // we assume priv will always be destroyed after objectDestroyed calls list->removeOne(*this); } diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index b9d312d41f..c77495a1a7 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -103,8 +103,8 @@ public: virtual QVariant value(int role) const = 0; virtual void setValue(int role, const QVariant &value) = 0; - void setValue(const QString &role, const QVariant &value); - bool resolveIndex(const QQmlAdaptorModel &model, int idx); + void setValue(const QString &role, const QVariant &value) override; + bool resolveIndex(const QQmlAdaptorModel &model, int idx) override; static QV4::ReturnedValue get_property(QV4::CallContext *ctx, uint propertyId); static QV4::ReturnedValue set_property(QV4::CallContext *ctx, uint propertyId); @@ -141,7 +141,7 @@ public: const QList<QQmlDelegateModelItem *> &items, int index, int count, - const QVector<int> &roles) const + const QVector<int> &roles) const override { bool changed = roles.isEmpty() && !watchedRoles.isEmpty(); if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) { @@ -185,7 +185,7 @@ public: void replaceWatchedRoles( QQmlAdaptorModel &, const QList<QByteArray> &oldRoles, - const QList<QByteArray> &newRoles) const + const QList<QByteArray> &newRoles) const override { VDMModelDelegateDataType *dataType = const_cast<VDMModelDelegateDataType *>(this); @@ -239,12 +239,12 @@ public: // QAbstractDynamicMetaObject - void objectDestroyed(QObject *) + void objectDestroyed(QObject *) override { release(); } - int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) + int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override { return static_cast<QQmlDMCachedModelData *>(object)->metaCall(call, id, arguments); } @@ -415,18 +415,18 @@ public: } } - QVariant value(int role) const + QVariant value(int role) const override { return type->model->aim()->index(index, 0, type->model->rootIndex).data(role); } - void setValue(int role, const QVariant &value) + void setValue(int role, const QVariant &value) override { type->model->aim()->setData( type->model->aim()->index(index, 0, type->model->rootIndex), value, role); } - QV4::ReturnedValue get() + QV4::ReturnedValue get() override { if (type->prototype.isUndefined()) { QQmlAdaptorModelEngineData * const data = engineData(v4); @@ -449,12 +449,12 @@ public: { } - int count(const QQmlAdaptorModel &model) const + int count(const QQmlAdaptorModel &model) const override { return model.aim()->rowCount(model.rootIndex); } - void cleanup(QQmlAdaptorModel &model, QQmlDelegateModel *vdm) const + void cleanup(QQmlAdaptorModel &model, QQmlDelegateModel *vdm) const override { QAbstractItemModel * const aim = model.aim(); if (aim && vdm) { @@ -477,7 +477,7 @@ public: const_cast<VDMAbstractItemModelDataType *>(this)->release(); } - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8()); if (it != roleNames.end()) { @@ -489,26 +489,26 @@ public: } } - QVariant parentModelIndex(const QQmlAdaptorModel &model) const + QVariant parentModelIndex(const QQmlAdaptorModel &model) const override { return model ? QVariant::fromValue(model.aim()->parent(model.rootIndex)) : QVariant(); } - QVariant modelIndex(const QQmlAdaptorModel &model, int index) const + QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override { return model ? QVariant::fromValue(model.aim()->index(index, 0, model.rootIndex)) : QVariant(); } - bool canFetchMore(const QQmlAdaptorModel &model) const + bool canFetchMore(const QQmlAdaptorModel &model) const override { return model && model.aim()->canFetchMore(model.rootIndex); } - void fetchMore(QQmlAdaptorModel &model) const + void fetchMore(QQmlAdaptorModel &model) const override { if (model) model.aim()->fetchMore(model.rootIndex); @@ -518,7 +518,7 @@ public: QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, QQmlEngine *engine, - int index) const + int index) const override { VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this); if (!metaObject) @@ -606,7 +606,7 @@ public: return QV4::Encode::undefined(); } - QV4::ReturnedValue get() + QV4::ReturnedValue get() override { QQmlAdaptorModelEngineData *data = engineData(v4); QV4::Scope scope(v4); @@ -617,13 +617,13 @@ public: return o.asReturnedValue(); } - void setValue(const QString &role, const QVariant &value) + void setValue(const QString &role, const QVariant &value) override { if (role == QLatin1String("modelData")) cachedData = value; } - bool resolveIndex(const QQmlAdaptorModel &model, int idx) + bool resolveIndex(const QQmlAdaptorModel &model, int idx) override { if (index == -1) { index = idx; @@ -650,12 +650,12 @@ class VDMListDelegateDataType : public QQmlAdaptorModel::Accessors public: inline VDMListDelegateDataType() {} - int count(const QQmlAdaptorModel &model) const + int count(const QQmlAdaptorModel &model) const override { return model.list.count(); } - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { return role == QLatin1String("modelData") ? model.list.at(index) @@ -666,7 +666,7 @@ public: QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, QQmlEngine *, - int index) const + int index) const override { return new QQmlDMListAccessorData( metaType, @@ -693,7 +693,7 @@ public: QObject *object); QObject *modelData() const { return object; } - QObject *proxiedObject() { return object; } + QObject *proxiedObject() override { return object; } QPointer<QObject> object; }; @@ -735,12 +735,12 @@ public: free(metaObject); } - int count(const QQmlAdaptorModel &model) const + int count(const QQmlAdaptorModel &model) const override { return model.list.count(); } - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { if (QObject *object = model.list.at(index).value<QObject *>()) return object->property(role.toUtf8()); @@ -751,7 +751,7 @@ public: QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, QQmlEngine *, - int index) const + int index) const override { VDMObjectDelegateDataType *dataType = const_cast<VDMObjectDelegateDataType *>(this); if (!metaObject) @@ -768,7 +768,7 @@ public: metaObject = builder.toMetaObject(); } - void cleanup(QQmlAdaptorModel &, QQmlDelegateModel *) const + void cleanup(QQmlAdaptorModel &, QQmlDelegateModel *) const override { const_cast<VDMObjectDelegateDataType *>(this)->release(); } @@ -792,7 +792,7 @@ public: m_type->release(); } - int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) + int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override { Q_ASSERT(o == m_data); Q_UNUSED(o); @@ -813,7 +813,7 @@ public: } } - int createProperty(const char *name, const char *) + int createProperty(const char *name, const char *) override { if (!m_data->object) return -1; diff --git a/src/qmldevtools/qmldevtools.pro b/src/qmldevtools/qmldevtools.pro index acd5c9729b..ec5d73044f 100644 --- a/src/qmldevtools/qmldevtools.pro +++ b/src/qmldevtools/qmldevtools.pro @@ -18,5 +18,6 @@ include(../qml/parser/parser.pri) include(../qml/jsruntime/jsruntime.pri) include(../qml/compiler/compiler.pri) include(../qml/memory/memory.pri) +include(../qml/jit/jit.pri) load(qt_module) diff --git a/src/qmldevtools/qtqmldevtoolsglobal_p.h b/src/qmldevtools/qtqmldevtoolsglobal_p.h index 5c803a4b32..5cb8a9275a 100644 --- a/src/qmldevtools/qtqmldevtoolsglobal_p.h +++ b/src/qmldevtools/qtqmldevtoolsglobal_p.h @@ -53,10 +53,21 @@ #include <QtCore/qglobal.h> +// All host systems are assumed to have alloca(). +#define Q_ALLOCA_VAR(type, name, size) \ + type *name = static_cast<type*>(alloca(size)) + QT_BEGIN_NAMESPACE #define Q_QML_EXPORT #define Q_QML_PRIVATE_EXPORT +/* Some classes built into QtQmlDevTools are marked Q_AUTOTEST_EXPORT but we + have nothing to export in this static library */ +#if defined(Q_AUTOTEST_EXPORT) +#undef Q_AUTOTEST_EXPORT +#endif +#define Q_AUTOTEST_EXPORT + QT_END_NAMESPACE #endif // QTQMLGLOBAL_P_H diff --git a/src/qmltest/quicktestevent.cpp b/src/qmltest/quicktestevent.cpp index 4b1adf5a90..dc7b917bc4 100644 --- a/src/qmltest/quicktestevent.cpp +++ b/src/qmltest/quicktestevent.cpp @@ -129,7 +129,7 @@ namespace QtQuickTest static void mouseEvent(MouseAction action, QWindow *window, QObject *item, Qt::MouseButton button, - Qt::KeyboardModifiers stateKey, QPointF _pos, int delay=-1) + Qt::KeyboardModifiers stateKey, const QPointF &_pos, int delay=-1) { QTEST_ASSERT(window); QTEST_ASSERT(item); diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp index b772ed97d2..1167f408f5 100644 --- a/src/quick/items/context2d/qquickcanvasitem.cpp +++ b/src/quick/items/context2d/qquickcanvasitem.cpp @@ -640,6 +640,17 @@ void QQuickCanvasItem::releaseResources() } } +bool QQuickCanvasItem::event(QEvent *event) +{ + switch (event->type()) { + case QEvent::PolishRequest: + polish(); + return true; + default: + return QQuickItem::event(event); + } +} + void QQuickCanvasItem::invalidateSceneGraph() { Q_D(QQuickCanvasItem); @@ -651,6 +662,12 @@ void QQuickCanvasItem::invalidateSceneGraph() d->textureProvider = 0; } +void QQuickCanvasItem::schedulePolish() +{ + auto polishRequestEvent = new QEvent(QEvent::PolishRequest); + QCoreApplication::postEvent(this, polishRequestEvent); +} + void QQuickCanvasItem::componentComplete() { QQuickItem::componentComplete(); @@ -892,8 +909,9 @@ void QQuickCanvasItem::requestAnimationFrame(QQmlV4Function *args) d->animationCallbacks.insert(++id, QV4::PersistentValue(scope.engine, f->asReturnedValue())); + // QTBUG-55778: Calling polish directly here can lead to a polish loop if (isVisible()) - polish(); + schedulePolish(); args->setReturnValue(QV4::Encode(id)); } diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h index 8af84d0e7c..217ae9bb69 100644 --- a/src/quick/items/context2d/qquickcanvasitem_p.h +++ b/src/quick/items/context2d/qquickcanvasitem_p.h @@ -182,6 +182,7 @@ private Q_SLOTS: void sceneGraphInitialized(); void checkAnimationCallbacks(); void invalidateSceneGraph(); + void schedulePolish(); protected: void componentComplete() Q_DECL_OVERRIDE; @@ -190,6 +191,7 @@ protected: QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *) Q_DECL_OVERRIDE; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) Q_DECL_OVERRIDE; void releaseResources() Q_DECL_OVERRIDE; + bool event(QEvent *event) Q_DECL_OVERRIDE; private: Q_DECLARE_PRIVATE(QQuickCanvasItem) Q_INVOKABLE void delayedCreate(); diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp index b0c1d50907..b9b701313e 100644 --- a/src/quick/items/context2d/qquickcontext2d.cpp +++ b/src/quick/items/context2d/qquickcontext2d.cpp @@ -901,7 +901,7 @@ struct QQuickJSContext2DPixelData : public QV4::Object V4_NEEDS_DESTROY static QV4::ReturnedValue getIndexed(const QV4::Managed *m, uint index, bool *hasProperty); - static void putIndexed(QV4::Managed *m, uint index, const QV4::Value &value); + static bool putIndexed(QV4::Managed *m, uint index, const QV4::Value &value); static void proto_get_length(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); }; @@ -1879,7 +1879,7 @@ void QQuickJSContext2D::method_get_lineWidth(const QV4::BuiltinFunction *, QV4:: QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject); CHECK_CONTEXT(r) - RETURN_RESULT(r->d()->context->state.lineWidth); + RETURN_RESULT(QV4::Encode(r->d()->context->state.lineWidth)); } void QQuickJSContext2D::method_set_lineWidth(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) @@ -1906,7 +1906,7 @@ void QQuickJSContext2D::method_get_miterLimit(const QV4::BuiltinFunction *, QV4: QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject); CHECK_CONTEXT(r) - RETURN_RESULT(r->d()->context->state.miterLimit); + RETURN_RESULT(QV4::Encode(r->d()->context->state.miterLimit)); } void QQuickJSContext2D::method_set_miterLimit(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) @@ -1933,7 +1933,7 @@ void QQuickJSContext2D::method_get_shadowBlur(const QV4::BuiltinFunction *, QV4: QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject); CHECK_CONTEXT(r) - RETURN_RESULT(r->d()->context->state.shadowBlur); + RETURN_RESULT(QV4::Encode(r->d()->context->state.shadowBlur)); } void QQuickJSContext2D::method_set_shadowBlur(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) @@ -1990,7 +1990,7 @@ void QQuickJSContext2D::method_get_shadowOffsetX(const QV4::BuiltinFunction *, Q QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject); CHECK_CONTEXT(r) - RETURN_RESULT(r->d()->context->state.shadowOffsetX); + RETURN_RESULT(QV4::Encode(r->d()->context->state.shadowOffsetX)); } void QQuickJSContext2D::method_set_shadowOffsetX(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) @@ -2016,7 +2016,7 @@ void QQuickJSContext2D::method_get_shadowOffsetY(const QV4::BuiltinFunction *, Q QV4::Scoped<QQuickJSContext2D> r(scope, callData->thisObject); CHECK_CONTEXT(r) - RETURN_RESULT(r->d()->context->state.shadowOffsetY); + RETURN_RESULT(QV4::Encode(r->d()->context->state.shadowOffsetY)); } void QQuickJSContext2D::method_set_shadowOffsetY(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) @@ -3043,7 +3043,7 @@ void QQuickJSContext2DPixelData::proto_get_length(const QV4::BuiltinFunction *, if (!r || r->d()->image->isNull()) RETURN_UNDEFINED(); - RETURN_RESULT(r->d()->image->width() * r->d()->image->height() * 4); + RETURN_RESULT(QV4::Encode(r->d()->image->width() * r->d()->image->height() * 4)); } QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m, uint index, bool *hasProperty) @@ -3077,13 +3077,13 @@ QV4::ReturnedValue QQuickJSContext2DPixelData::getIndexed(const QV4::Managed *m, return QV4::Encode::undefined(); } -void QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const QV4::Value &value) +bool QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const QV4::Value &value) { Q_ASSERT(m->as<QQuickJSContext2DPixelData>()); QV4::ExecutionEngine *v4 = static_cast<QQuickJSContext2DPixelData *>(m)->engine(); QV4::Scope scope(v4); if (scope.hasException()) - return; + return false; QV4::Scoped<QQuickJSContext2DPixelData> r(scope, static_cast<QQuickJSContext2DPixelData *>(m)); @@ -3109,7 +3109,10 @@ void QQuickJSContext2DPixelData::putIndexed(QV4::Managed *m, uint index, const Q *pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v); break; } + return true; } + + return false; } /*! \qmlmethod CanvasImageData QtQuick::Context2D::createImageData(real sw, real sh) diff --git a/src/quick/items/qquickanimatedimage.cpp b/src/quick/items/qquickanimatedimage.cpp index a1833081c8..22ea4774be 100644 --- a/src/quick/items/qquickanimatedimage.cpp +++ b/src/quick/items/qquickanimatedimage.cpp @@ -67,7 +67,7 @@ QQuickPixmap* QQuickAnimatedImagePrivate::infoForCurrentFrame(QQmlEngine *engine .arg(current)); } if (!requestedUrl.isEmpty()) { - if (QQuickPixmap::isCached(requestedUrl, QSize())) + if (QQuickPixmap::isCached(requestedUrl, QSize(), QQuickImageProviderOptions())) pixmap = new QQuickPixmap(engine, requestedUrl); else pixmap = new QQuickPixmap(requestedUrl, _movie->currentImage()); diff --git a/src/quick/items/qquickevents_p_p.h b/src/quick/items/qquickevents_p_p.h index 7b281623fe..cf6f83e5b1 100644 --- a/src/quick/items/qquickevents_p_p.h +++ b/src/quick/items/qquickevents_p_p.h @@ -108,7 +108,9 @@ public: bool isAccepted() { return event.isAccepted(); } void setAccepted(bool accepted) { event.setAccepted(accepted); } +#if QT_CONFIG(shortcut) Q_REVISION(2) Q_INVOKABLE bool matches(QKeySequence::StandardKey key) const { return event.matches(key); } +#endif private: QKeyEvent event; diff --git a/src/quick/items/qquickimage.cpp b/src/quick/items/qquickimage.cpp index f71a2fbdbd..f3d7dc4b56 100644 --- a/src/quick/items/qquickimage.cpp +++ b/src/quick/items/qquickimage.cpp @@ -514,37 +514,41 @@ void QQuickImage::updatePaintedGeometry() setImplicitSize(0, 0); return; } - qreal w = widthValid() ? width() : d->pix.width(); - qreal widthScale = w / qreal(d->pix.width()); - qreal h = heightValid() ? height() : d->pix.height(); - qreal heightScale = h / qreal(d->pix.height()); + const qreal pixWidth = d->pix.width() / d->devicePixelRatio; + const qreal pixHeight = d->pix.height() / d->devicePixelRatio; + const qreal w = widthValid() ? width() : pixWidth; + const qreal widthScale = w / pixWidth; + const qreal h = heightValid() ? height() : pixHeight; + const qreal heightScale = h / pixHeight; if (widthScale <= heightScale) { d->paintedWidth = w; - d->paintedHeight = widthScale * qreal(d->pix.height()); + d->paintedHeight = widthScale * pixHeight; } else if (heightScale < widthScale) { - d->paintedWidth = heightScale * qreal(d->pix.width()); + d->paintedWidth = heightScale * pixWidth; d->paintedHeight = h; } - qreal iHeight = (widthValid() && !heightValid()) ? d->paintedHeight : d->pix.height(); - qreal iWidth = (heightValid() && !widthValid()) ? d->paintedWidth : d->pix.width(); + const qreal iHeight = (widthValid() && !heightValid()) ? d->paintedHeight : pixHeight; + const qreal iWidth = (heightValid() && !widthValid()) ? d->paintedWidth : pixWidth; setImplicitSize(iWidth, iHeight); } else if (d->fillMode == PreserveAspectCrop) { if (!d->pix.width() || !d->pix.height()) return; - qreal widthScale = width() / qreal(d->pix.width()); - qreal heightScale = height() / qreal(d->pix.height()); + const qreal pixWidth = d->pix.width() / d->devicePixelRatio; + const qreal pixHeight = d->pix.height() / d->devicePixelRatio; + qreal widthScale = width() / pixWidth; + qreal heightScale = height() / pixHeight; if (widthScale < heightScale) { widthScale = heightScale; } else if (heightScale < widthScale) { heightScale = widthScale; } - d->paintedHeight = heightScale * qreal(d->pix.height()); - d->paintedWidth = widthScale * qreal(d->pix.width()); + d->paintedHeight = heightScale * pixHeight; + d->paintedWidth = widthScale * pixWidth; } else if (d->fillMode == Pad) { - d->paintedWidth = d->pix.width(); - d->paintedHeight = d->pix.height(); + d->paintedWidth = d->pix.width() / d->devicePixelRatio; + d->paintedHeight = d->pix.height() / d->devicePixelRatio; } else { d->paintedWidth = width(); d->paintedHeight = height(); diff --git a/src/quick/items/qquickimage_p_p.h b/src/quick/items/qquickimage_p_p.h index 522dbca803..afc33def0f 100644 --- a/src/quick/items/qquickimage_p_p.h +++ b/src/quick/items/qquickimage_p_p.h @@ -58,7 +58,7 @@ QT_BEGIN_NAMESPACE class QQuickImageTextureProvider; -class QQuickImagePrivate : public QQuickImageBasePrivate +class Q_QUICK_PRIVATE_EXPORT QQuickImagePrivate : public QQuickImageBasePrivate { Q_DECLARE_PUBLIC(QQuickImage) diff --git a/src/quick/items/qquickimagebase.cpp b/src/quick/items/qquickimagebase.cpp index 22d631e917..33d69f5032 100644 --- a/src/quick/items/qquickimagebase.cpp +++ b/src/quick/items/qquickimagebase.cpp @@ -49,6 +49,30 @@ QT_BEGIN_NAMESPACE +// This function gives derived classes the chance set the devicePixelRatio +// if they're not happy with our implementation of it. +bool QQuickImageBasePrivate::updateDevicePixelRatio(qreal targetDevicePixelRatio) +{ + // QQuickImageProvider and SVG can generate a high resolution image when + // sourceSize is set (this function is only called if it's set). + // If sourceSize is not set then the provider default size will be used, as usual. + bool setDevicePixelRatio = false; + if (url.scheme() == QLatin1String("image")) { + setDevicePixelRatio = true; + } else { + QString stringUrl = url.path(QUrl::PrettyDecoded); + if (stringUrl.endsWith(QLatin1String("svg")) || + stringUrl.endsWith(QLatin1String("svgz"))) { + setDevicePixelRatio = true; + } + } + + if (setDevicePixelRatio) + devicePixelRatio = targetDevicePixelRatio; + + return setDevicePixelRatio; +} + QQuickImageBase::QQuickImageBase(QQuickItem *parent) : QQuickImplicitSizeItem(*(new QQuickImageBasePrivate), parent) { @@ -221,26 +245,11 @@ void QQuickImageBase::load() QUrl loadUrl = d->url; - // QQuickImageProvider and SVG can generate a high resolution image when - // sourceSize is set. If sourceSize is not set then the provider default size - // will be used, as usual. - bool setDevicePixelRatio = false; - if (d->sourcesize.isValid()) { - if (loadUrl.scheme() == QLatin1String("image")) { - setDevicePixelRatio = true; - } else { - QString stringUrl = loadUrl.path(QUrl::PrettyDecoded); - if (stringUrl.endsWith(QLatin1String("svg")) || - stringUrl.endsWith(QLatin1String("svgz"))) { - setDevicePixelRatio = true; - } - } - - if (setDevicePixelRatio) - d->devicePixelRatio = targetDevicePixelRatio; - } + bool updatedDevicePixelRatio = false; + if (d->sourcesize.isValid()) + updatedDevicePixelRatio = d->updateDevicePixelRatio(targetDevicePixelRatio); - if (!setDevicePixelRatio) { + if (!updatedDevicePixelRatio) { // (possible) local file: loadUrl and d->devicePixelRatio will be modified if // an "@2x" file is found. resolve2xLocalFile(d->url, targetDevicePixelRatio, &loadUrl, &d->devicePixelRatio); diff --git a/src/quick/items/qquickimagebase_p_p.h b/src/quick/items/qquickimagebase_p_p.h index d9b609c7fe..1b771166a2 100644 --- a/src/quick/items/qquickimagebase_p_p.h +++ b/src/quick/items/qquickimagebase_p_p.h @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE class QNetworkReply; -class QQuickImageBasePrivate : public QQuickImplicitSizeItemPrivate +class Q_QUICK_PRIVATE_EXPORT QQuickImageBasePrivate : public QQuickImplicitSizeItemPrivate { Q_DECLARE_PUBLIC(QQuickImageBase) @@ -75,6 +75,8 @@ public: { } + virtual bool updateDevicePixelRatio(qreal targetDevicePixelRatio); + QQuickPixmap pix; QQuickImageBase::Status status; QUrl url; diff --git a/src/quick/items/qquickitem.cpp b/src/quick/items/qquickitem.cpp index f7b9a58329..22ee3f39e4 100644 --- a/src/quick/items/qquickitem.cpp +++ b/src/quick/items/qquickitem.cpp @@ -7865,6 +7865,7 @@ QQuickItemLayer::QQuickItemLayer(QQuickItem *item) , m_effect(0) , m_effectSource(0) , m_textureMirroring(QQuickShaderEffectSource::MirrorVertically) + , m_samples(0) { } @@ -7939,6 +7940,7 @@ void QQuickItemLayer::activate() m_effectSource->setWrapMode(m_wrapMode); m_effectSource->setFormat(m_format); m_effectSource->setTextureMirroring(m_textureMirroring); + m_effectSource->setSamples(m_samples); if (m_effectComponent) activateEffect(); @@ -8232,6 +8234,44 @@ void QQuickItemLayer::setTextureMirroring(QQuickShaderEffectSource::TextureMirro } /*! + \qmlproperty enumeration QtQuick::Item::layer.samples + \since 5.10 + + This property allows requesting multisampled rendering in the layer. + + By default multisampling is enabled whenever multisampling is + enabled for the entire window, assuming the scenegraph renderer in + use and the underlying graphics API supports this. + + By setting the value to 2, 4, etc. multisampled rendering can be requested + for a part of the scene without enabling multisampling for the entire + scene. This way multisampling is applied only to a given subtree, which can + lead to significant performance gains since multisampling is not applied to + other parts of the scene. + + \note Enabling multisampling can be potentially expensive regardless of the + layer's size, as it incurs a hardware and driver dependent performance and + memory cost. + + \note This property is only functional when support for multisample + renderbuffers and framebuffer blits is available. Otherwise the value is + silently ignored. + */ + +void QQuickItemLayer::setSamples(int count) +{ + if (m_samples == count) + return; + + m_samples = count; + + if (m_effectSource) + m_effectSource->setSamples(m_samples); + + emit samplesChanged(count); +} + +/*! \qmlproperty string QtQuick::Item::layer.samplerName Holds the name of the effect's source texture property. diff --git a/src/quick/items/qquickitem_p.h b/src/quick/items/qquickitem_p.h index c0c9bd46bd..797ba42781 100644 --- a/src/quick/items/qquickitem_p.h +++ b/src/quick/items/qquickitem_p.h @@ -152,6 +152,8 @@ class QQuickItemLayer : public QObject, public QQuickItemChangeListener Q_PROPERTY(QByteArray samplerName READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QQmlComponent *effect READ effect WRITE setEffect NOTIFY effectChanged) Q_PROPERTY(QQuickShaderEffectSource::TextureMirroring textureMirroring READ textureMirroring WRITE setTextureMirroring NOTIFY textureMirroringChanged) + Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged) + public: QQuickItemLayer(QQuickItem *item); ~QQuickItemLayer(); @@ -189,6 +191,9 @@ public: QQuickShaderEffectSource::TextureMirroring textureMirroring() const { return m_textureMirroring; } void setTextureMirroring(QQuickShaderEffectSource::TextureMirroring mirroring); + int samples() const { return m_samples; } + void setSamples(int count); + QQuickShaderEffectSource *effectSource() const { return m_effectSource; } void itemGeometryChanged(QQuickItem *, QQuickGeometryChange, const QRectF &) Q_DECL_OVERRIDE; @@ -213,6 +218,7 @@ Q_SIGNALS: void formatChanged(QQuickShaderEffectSource::Format format); void sourceRectChanged(const QRectF &sourceRect); void textureMirroringChanged(QQuickShaderEffectSource::TextureMirroring mirroring); + void samplesChanged(int count); private: friend class QQuickTransformAnimatorJob; @@ -237,6 +243,7 @@ private: QQuickItem *m_effect; QQuickShaderEffectSource *m_effectSource; QQuickShaderEffectSource::TextureMirroring m_textureMirroring; + int m_samples; }; #endif diff --git a/src/quick/items/qquickitemgrabresult.cpp b/src/quick/items/qquickitemgrabresult.cpp index 07c2cb607c..12bcd43076 100644 --- a/src/quick/items/qquickitemgrabresult.cpp +++ b/src/quick/items/qquickitemgrabresult.cpp @@ -75,7 +75,7 @@ public: void ensureImageInCache() const { if (url.isEmpty() && !image.isNull()) { - url.setScheme(QStringLiteral("ItemGrabber")); + url.setScheme(QQuickPixmap::itemGrabberScheme); url.setPath(QVariant::fromValue(item.data()).toString()); static uint counter = 0; url.setFragment(QString::number(++counter)); diff --git a/src/quick/items/qquickitemsmodule.cpp b/src/quick/items/qquickitemsmodule.cpp index 65143f3e9b..43f4b88833 100644 --- a/src/quick/items/qquickitemsmodule.cpp +++ b/src/quick/items/qquickitemsmodule.cpp @@ -397,6 +397,10 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor) qmlRegisterUncreatableType<QQuickBasePositioner, 9>(uri, 2, 9, "Positioner", QStringLiteral("Positioner is an abstract type that is only available as an attached property.")); #endif + +#if QT_CONFIG(quick_shadereffect) + qmlRegisterType<QQuickShaderEffectSource, 2>(uri, 2, 9, "ShaderEffectSource"); +#endif } static void initResources() diff --git a/src/quick/items/qquickopenglshadereffect_p.h b/src/quick/items/qquickopenglshadereffect_p.h index 64e79a9343..bc2e2975ee 100644 --- a/src/quick/items/qquickopenglshadereffect_p.h +++ b/src/quick/items/qquickopenglshadereffect_p.h @@ -70,7 +70,6 @@ QT_REQUIRE_CONFIG(quick_shadereffect); QT_BEGIN_NAMESPACE class QSGContext; -class QSignalMapper; class QFileSelector; class QQuickOpenGLCustomMaterialShader; diff --git a/src/quick/items/qquickpositioners.cpp b/src/quick/items/qquickpositioners.cpp index 05d3ae0191..70fc5fa65f 100644 --- a/src/quick/items/qquickpositioners.cpp +++ b/src/quick/items/qquickpositioners.cpp @@ -44,15 +44,23 @@ #include <QtQml/qqmlinfo.h> #include <QtCore/qcoreapplication.h> -#include <QtQuick/private/qquickstate_p.h> -#include <QtQuick/private/qquickstategroup_p.h> -#include <private/qquickstatechangescript_p.h> #include <QtQuick/private/qquicktransition_p.h> QT_BEGIN_NAMESPACE -static const QQuickItemPrivate::ChangeTypes watchedChanges - = QQuickItemPrivate::Geometry +// 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 | QQuickItemPrivate::SiblingOrder | QQuickItemPrivate::Visibility | QQuickItemPrivate::Destroyed; @@ -60,13 +68,15 @@ static const QQuickItemPrivate::ChangeTypes watchedChanges void QQuickBasePositionerPrivate::watchChanges(QQuickItem *other) { QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other); - otherPrivate->addItemChangeListener(this, watchedChanges); + otherPrivate->addItemChangeListener(this, useImplicitSize + ? implicitSizeItemChangeTypes : explicitSizeItemChangeTypes); } void QQuickBasePositionerPrivate::unwatchChanges(QQuickItem* other) { QQuickItemPrivate *otherPrivate = QQuickItemPrivate::get(other); - otherPrivate->removeItemChangeListener(this, watchedChanges); + otherPrivate->removeItemChangeListener(this, useImplicitSize + ? implicitSizeItemChangeTypes : explicitSizeItemChangeTypes); } @@ -326,7 +336,7 @@ void QQuickBasePositioner::prePositioning() if (wIdx < 0) { d->watchChanges(child); posItem.isNew = true; - if (!childPrivate->explicitVisible || !child->width() || !child->height()) { + if (!childPrivate->explicitVisible || !d->itemWidth(child) || !d->itemHeight(child)) { posItem.isVisible = false; posItem.index = -1; unpositionedItems.append(posItem); @@ -348,7 +358,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 || !child->width() || !child->height()) { + if (!childPrivate->explicitVisible || !d->itemWidth(child) || !d->itemHeight(child)) { item->isVisible = false; item->index = -1; unpositionedItems.append(*item); @@ -947,6 +957,7 @@ 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)); @@ -955,9 +966,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(), child.item->width() + padding)); + contentSize->setWidth(qMax(contentSize->width(), d->itemWidth(child.item) + padding)); - voffset += child.item->height(); + voffset += d->itemHeight(child.item); voffset += spacing(); } @@ -1137,7 +1148,7 @@ public: : QQuickBasePositionerPrivate() {} - void effectiveLayoutDirectionChange() + void effectiveLayoutDirectionChange() override { Q_Q(QQuickRow); // For RTL layout the positioning changes when the width changes. @@ -1225,9 +1236,9 @@ void QQuickRow::doPositioning(QSizeF *contentSize) hoffsets << hoffset; } - contentSize->setHeight(qMax(contentSize->height(), child.item->height() + padding)); + contentSize->setHeight(qMax(contentSize->height(), d->itemHeight(child.item) + padding)); - hoffset += child.item->width(); + hoffset += d->itemWidth(child.item); hoffset += spacing(); } @@ -1248,7 +1259,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++] - child.item->width(); + hoffset = end - hoffsets[acc++] - d->itemWidth(child.item); positionItem(hoffset, child.itemY() + topPadding() - child.topPadding, &child); child.updatePadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); } @@ -1436,7 +1447,7 @@ public: : QQuickBasePositionerPrivate() {} - void effectiveLayoutDirectionChange() + void effectiveLayoutDirectionChange() override { Q_Q(QQuickGrid); // For RTL layout the positioning changes when the width changes. @@ -1749,10 +1760,12 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) break; const PositionedItem &child = positionedItems.at(childIndex++); - if (child.item->width() > maxColWidth[j]) - maxColWidth[j] = child.item->width(); - if (child.item->height() > maxRowHeight[i]) - maxRowHeight[i] = child.item->height(); + 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; } } } else { @@ -1767,10 +1780,12 @@ void QQuickGrid::doPositioning(QSizeF *contentSize) break; const PositionedItem &child = positionedItems.at(childIndex++); - if (child.item->width() > maxColWidth[j]) - maxColWidth[j] = child.item->width(); - if (child.item->height() > maxRowHeight[i]) - maxRowHeight[i] = child.item->height(); + 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; } } } @@ -1812,20 +1827,22 @@ 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] - child.item->width(); + childXOffset += maxColWidth[curCol] - childWidth; else if (hItemAlign() == AlignHCenter) - childXOffset += (maxColWidth[curCol] - child.item->width())/2.0; + childXOffset += (maxColWidth[curCol] - childWidth)/2.0; if (!d->isLeftToRight()) childXOffset -= maxColWidth[curCol]; qreal alignYOffset = yoffset; if (m_vItemAlign == AlignVCenter) - alignYOffset += (maxRowHeight[curRow] - child.item->height())/2.0; + alignYOffset += (maxRowHeight[curRow] - childHeight)/2.0; else if (m_vItemAlign == AlignBottom) - alignYOffset += maxRowHeight[curRow] - child.item->height(); + alignYOffset += maxRowHeight[curRow] - childHeight; positionItem(childXOffset, alignYOffset, &child); child.updatePadding(leftPadding(), topPadding(), rightPadding(), bottomPadding()); @@ -2023,7 +2040,7 @@ public: : QQuickBasePositionerPrivate(), flow(QQuickFlow::LeftToRight) {} - void effectiveLayoutDirectionChange() + void effectiveLayoutDirectionChange() override { Q_Q(QQuickFlow); // Don't postpone, as it might be the only trigger for visible changes. @@ -2143,15 +2160,17 @@ 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 + child.item->width() + hoffset2 > width()) { + if (widthValid() && hoffset != hoffset1 && hoffset + childWidth + hoffset2 > width()) { hoffset = hoffset1; voffset += linemax + spacing(); linemax = 0; } } else { - if (heightValid() && voffset != voffset1 && voffset + child.item->height() + bottomPadding() > height()) { + if (heightValid() && voffset != voffset1 && voffset + childHeight + bottomPadding() > height()) { voffset = voffset1; hoffset += linemax + spacing(); linemax = 0; @@ -2168,17 +2187,17 @@ void QQuickFlow::doPositioning(QSizeF *contentSize) child.bottomPadding = bottomPadding(); } - contentSize->setWidth(qMax(contentSize->width(), hoffset + child.item->width() + hoffset2)); - contentSize->setHeight(qMax(contentSize->height(), voffset + child.item->height() + bottomPadding())); + contentSize->setWidth(qMax(contentSize->width(), hoffset + childWidth + hoffset2)); + contentSize->setHeight(qMax(contentSize->height(), voffset + childHeight + bottomPadding())); if (d->flow == LeftToRight) { - hoffset += child.item->width(); + hoffset += childWidth; hoffset += spacing(); - linemax = qMax(linemax, child.item->height()); + linemax = qMax(linemax, childHeight); } else { - voffset += child.item->height(); + voffset += childHeight; voffset += spacing(); - linemax = qMax(linemax, child.item->width()); + linemax = qMax(linemax, childWidth); } } @@ -2193,7 +2212,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++] - child.item->width(); + hoffset = end - hoffsets[acc++] - d->itemWidth(child.item); positionItemX(hoffset, &child); child.leftPadding = leftPadding(); child.rightPadding = rightPadding(); @@ -2217,4 +2236,18 @@ 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 ae6e795794..8dc0d90a2f 100644 --- a/src/quick/items/qquickpositioners_p.h +++ b/src/quick/items/qquickpositioners_p.h @@ -58,7 +58,6 @@ QT_REQUIRE_CONFIG(quick_positioners); #include "qquickimplicitsizeitem_p.h" #include "qquickitemviewtransition_p.h" -#include <QtQuick/private/qquickstate_p.h> #include <private/qpodvector_p.h> #include <QtCore/qobject.h> @@ -133,6 +132,11 @@ 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; @@ -183,7 +187,7 @@ protected: virtual void doPositioning(QSizeF *contentSize)=0; virtual void reportConflictingAnchors()=0; - class PositionedItem + class Q_QUICK_PRIVATE_EXPORT PositionedItem { public : PositionedItem(QQuickItem *i); @@ -228,7 +232,7 @@ private: Q_DECLARE_PRIVATE(QQuickBasePositioner) }; -class Q_AUTOTEST_EXPORT QQuickColumn : public QQuickBasePositioner +class Q_QUICK_PRIVATE_EXPORT QQuickColumn : public QQuickBasePositioner { Q_OBJECT public: @@ -242,7 +246,7 @@ private: }; class QQuickRowPrivate; -class Q_AUTOTEST_EXPORT QQuickRow: public QQuickBasePositioner +class Q_QUICK_PRIVATE_EXPORT QQuickRow: public QQuickBasePositioner { Q_OBJECT Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection NOTIFY layoutDirectionChanged) @@ -267,7 +271,7 @@ private: }; class QQuickGridPrivate; -class Q_AUTOTEST_EXPORT QQuickGrid : public QQuickBasePositioner +class Q_QUICK_PRIVATE_EXPORT QQuickGrid : public QQuickBasePositioner { Q_OBJECT Q_PROPERTY(int rows READ rows WRITE setRows NOTIFY rowsChanged) @@ -354,7 +358,7 @@ private: }; class QQuickFlowPrivate; -class Q_AUTOTEST_EXPORT QQuickFlow: public QQuickBasePositioner +class Q_QUICK_PRIVATE_EXPORT QQuickFlow: public QQuickBasePositioner { Q_OBJECT Q_PROPERTY(Flow flow READ flow WRITE setFlow NOTIFY flowChanged) @@ -387,6 +391,22 @@ 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 @@ -394,6 +414,8 @@ 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 6dd84e6098..1a7051615c 100644 --- a/src/quick/items/qquickpositioners_p_p.h +++ b/src/quick/items/qquickpositioners_p_p.h @@ -58,9 +58,6 @@ QT_REQUIRE_CONFIG(quick_positioners); #include "qquickpositioners_p.h" #include "qquickimplicitsizeitem_p_p.h" -#include <QtQuick/private/qquickstate_p.h> -#include <private/qquicktransitionmanager_p_p.h> -#include <private/qquickstatechangescript_p.h> #include <private/qlazilyallocated_p.h> #include <QtCore/qobject.h> @@ -92,10 +89,14 @@ public: QLazilyAllocated<ExtraData> extra; QQuickBasePositionerPrivate() - : spacing(0), type(QQuickBasePositioner::None) - , transitioner(0), positioningDirty(false) - , doingPositioning(false), anchorConflict(false), layoutDirection(Qt::LeftToRight) - + : spacing(0) + , type(QQuickBasePositioner::None) + , transitioner(0) + , positioningDirty(false) + , doingPositioning(false) + , anchorConflict(false) + , useImplicitSize(false) + , layoutDirection(Qt::LeftToRight) { } @@ -122,6 +123,7 @@ public: bool positioningDirty : 1; bool doingPositioning : 1; bool anchorConflict : 1; + bool useImplicitSize : 1; Qt::LayoutDirection layoutDirection; @@ -177,6 +179,34 @@ 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/qquickscreen.cpp b/src/quick/items/qquickscreen.cpp index 9347b55c70..20c6973ee1 100644 --- a/src/quick/items/qquickscreen.cpp +++ b/src/quick/items/qquickscreen.cpp @@ -207,9 +207,9 @@ QT_BEGIN_NAMESPACE By default it is set to the value of the QScreen that the window uses. */ -QQuickScreenInfo::QQuickScreenInfo(QObject *parent) - : QObject(parent), - m_screen(nullptr) +QQuickScreenInfo::QQuickScreenInfo(QObject *parent, QScreen *wrappedScreen) + : QObject(parent) + , m_screen(wrappedScreen) { } diff --git a/src/quick/items/qquickscreen_p.h b/src/quick/items/qquickscreen_p.h index 06efb3ab45..99e1466631 100644 --- a/src/quick/items/qquickscreen_p.h +++ b/src/quick/items/qquickscreen_p.h @@ -84,7 +84,7 @@ class Q_AUTOTEST_EXPORT QQuickScreenInfo : public QObject Q_PROPERTY(int virtualY READ virtualY NOTIFY virtualYChanged REVISION 1) public: - QQuickScreenInfo(QObject *parent = nullptr); + QQuickScreenInfo(QObject *parent = nullptr, QScreen *wrappedScreen = nullptr); QString name() const; int width() const; diff --git a/src/quick/items/qquickshadereffectsource.cpp b/src/quick/items/qquickshadereffectsource.cpp index 1b37a746d3..c782ddb5f7 100644 --- a/src/quick/items/qquickshadereffectsource.cpp +++ b/src/quick/items/qquickshadereffectsource.cpp @@ -189,6 +189,7 @@ QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent) , m_sourceItem(0) , m_textureSize(0, 0) , m_format(RGBA) + , m_samples(0) , m_live(true) , m_hideSource(false) , m_mipmap(false) @@ -582,6 +583,44 @@ void QQuickShaderEffectSource::setTextureMirroring(TextureMirroring mirroring) } /*! + \qmlproperty int QtQuick::ShaderEffectSource::samples + \since 5.10 + + This property allows requesting multisampled rendering. + + By default multisampling is enabled whenever multisampling is enabled for + the entire window, assuming the scenegraph renderer in use and the + underlying graphics API supports this. + + By setting the value to 2, 4, etc. multisampled rendering can be requested + for a part of the scene without enabling multisampling for the entire + scene. This way multisampling is applied only to a given subtree, which can + lead to significant performance gains since multisampling is not applied to + other parts of the scene. + + \note Enabling multisampling can be potentially expensive regardless of the + layer's size, as it incurs a hardware and driver dependent performance and + memory cost. + + \note This property is only functional when support for multisample + renderbuffers and framebuffer blits is available. Otherwise the value is + silently ignored. + */ +int QQuickShaderEffectSource::samples() const +{ + return m_samples; +} + +void QQuickShaderEffectSource::setSamples(int count) +{ + if (count == m_samples) + return; + m_samples = count; + update(); + emit samplesChanged(); +} + +/*! \qmlmethod QtQuick::ShaderEffectSource::scheduleUpdate() Schedules a re-rendering of the texture for the next frame. @@ -683,6 +722,7 @@ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaint m_texture->setHasMipmaps(m_mipmap); m_texture->setMirrorHorizontal(m_textureMirroring & MirrorHorizontally); m_texture->setMirrorVertical(m_textureMirroring & MirrorVertically); + m_texture->setSamples(m_samples); if (m_grab) m_texture->scheduleUpdate(); diff --git a/src/quick/items/qquickshadereffectsource_p.h b/src/quick/items/qquickshadereffectsource_p.h index 5e7e354feb..d9f9079a3d 100644 --- a/src/quick/items/qquickshadereffectsource_p.h +++ b/src/quick/items/qquickshadereffectsource_p.h @@ -88,6 +88,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickShaderEffectSource : public QQuickItem, publi Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged) Q_PROPERTY(bool recursive READ recursive WRITE setRecursive NOTIFY recursiveChanged) Q_PROPERTY(TextureMirroring textureMirroring READ textureMirroring WRITE setTextureMirroring NOTIFY textureMirroringChanged REVISION 1) + Q_PROPERTY(int samples READ samples WRITE setSamples NOTIFY samplesChanged REVISION 2) public: enum WrapMode { @@ -150,6 +151,9 @@ public: Q_INVOKABLE void scheduleUpdate(); + int samples() const; + void setSamples(int count); + Q_SIGNALS: void wrapModeChanged(); void sourceItemChanged(); @@ -161,6 +165,7 @@ Q_SIGNALS: void mipmapChanged(); void recursiveChanged(); void textureMirroringChanged(); + void samplesChanged(); void scheduledUpdateCompleted(); @@ -185,6 +190,7 @@ private: QRectF m_sourceRect; QSize m_textureSize; Format m_format; + int m_samples; uint m_live : 1; uint m_hideSource : 1; uint m_mipmap : 1; diff --git a/src/quick/items/qquicktext.cpp b/src/quick/items/qquicktext.cpp index 965fb0102f..1720377046 100644 --- a/src/quick/items/qquicktext.cpp +++ b/src/quick/items/qquicktext.cpp @@ -74,7 +74,7 @@ Q_DECLARE_LOGGING_CATEGORY(DBG_HOVER_TRACE) const QChar QQuickTextPrivate::elideChar = QChar(0x2026); QQuickTextPrivate::QQuickTextPrivate() - : elideLayout(0), textLine(0), lineWidth(0) + : fontInfo(font), elideLayout(0), textLine(0), lineWidth(0) , color(0xFF000000), linkColor(0xFF0000FF), styleColor(0xFF000000) , lineCount(1), multilengthEos(-1) , elideMode(QQuickText::ElideNone), hAlign(QQuickText::AlignLeft), vAlign(QQuickText::AlignTop) @@ -1018,6 +1018,17 @@ QRectF QQuickTextPrivate::setupTextLayout(qreal *const baseline) implicitWidthValid = true; implicitHeightValid = true; + QFontInfo scaledFontInfo(scaledFont); + if (fontInfo.weight() != scaledFontInfo.weight() + || fontInfo.pixelSize() != scaledFontInfo.pixelSize() + || fontInfo.italic() != scaledFontInfo.italic() + || !qFuzzyCompare(fontInfo.pointSizeF(), scaledFontInfo.pointSizeF()) + || fontInfo.family() != scaledFontInfo.family() + || fontInfo.styleName() != scaledFontInfo.styleName()) { + fontInfo = scaledFontInfo; + emit q->fontInfoChanged(); + } + if (eos != multilengthEos) truncated = true; @@ -2974,4 +2985,80 @@ void QQuickText::resetBottomPadding() d->setBottomPadding(0, true); } +/*! + \qmlproperty string QtQuick::Text::fontInfo.family + \since 5.9 + + The family name of the font that has been resolved for the current font + and fontSizeMode. +*/ + +/*! + \qmlproperty string QtQuick::Text::fontInfo.styleName + \since 5.9 + + The style name of the font info that has been resolved for the current font + and fontSizeMode. +*/ + +/*! + \qmlproperty bool QtQuick::Text::fontInfo.bold + \since 5.9 + + The bold state of the font info that has been resolved for the current font + and fontSizeMode. This is true if the weight of the resolved font is bold or higher. +*/ + +/*! + \qmlproperty int QtQuick::Text::fontInfo.weight + \since 5.9 + + The weight of the font info that has been resolved for the current font + and fontSizeMode. +*/ + +/*! + \qmlproperty bool QtQuick::Text::fontInfo.italic + \since 5.9 + + The italic state of the font info that has been resolved for the current font + and fontSizeMode. +*/ + +/*! + \qmlproperty real QtQuick::Text::fontInfo.pointSize + \since 5.9 + + The pointSize of the font info that has been resolved for the current font + and fontSizeMode. +*/ + +/*! + \qmlproperty string QtQuick::Text::fontInfo.pixelSize + \since 5.9 + + The pixel size of the font info that has been resolved for the current font + and fontSizeMode. +*/ +QJSValue QQuickText::fontInfo() const +{ + Q_D(const QQuickText); + + QJSEngine *engine = qjsEngine(this); + if (!engine) { + qmlWarning(this) << "fontInfo: item has no JS engine"; + return QJSValue(); + } + + QJSValue value = engine->newObject(); + value.setProperty(QStringLiteral("family"), d->fontInfo.family()); + value.setProperty(QStringLiteral("styleName"), d->fontInfo.styleName()); + value.setProperty(QStringLiteral("bold"), d->fontInfo.bold()); + value.setProperty(QStringLiteral("weight"), d->fontInfo.weight()); + value.setProperty(QStringLiteral("italic"), d->fontInfo.italic()); + value.setProperty(QStringLiteral("pointSize"), d->fontInfo.pointSizeF()); + value.setProperty(QStringLiteral("pixelSize"), d->fontInfo.pixelSize()); + return value; +} + QT_END_NAMESPACE diff --git a/src/quick/items/qquicktext_p.h b/src/quick/items/qquicktext_p.h index c3d3906e7e..b190738cfb 100644 --- a/src/quick/items/qquicktext_p.h +++ b/src/quick/items/qquicktext_p.h @@ -98,6 +98,8 @@ class Q_QUICK_PRIVATE_EXPORT QQuickText : public QQuickImplicitSizeItem Q_PROPERTY(qreal rightPadding READ rightPadding WRITE setRightPadding RESET resetRightPadding NOTIFY rightPaddingChanged REVISION 6) Q_PROPERTY(qreal bottomPadding READ bottomPadding WRITE setBottomPadding RESET resetBottomPadding NOTIFY bottomPaddingChanged REVISION 6) + Q_PROPERTY(QJSValue fontInfo READ fontInfo NOTIFY fontInfoChanged REVISION 9) + public: QQuickText(QQuickItem *parent=0); ~QQuickText(); @@ -248,6 +250,8 @@ public: void setBottomPadding(qreal padding); void resetBottomPadding(); + QJSValue fontInfo() const; + Q_SIGNALS: void textChanged(const QString &text); void linkActivated(const QString &link); @@ -280,6 +284,7 @@ Q_SIGNALS: Q_REVISION(6) void leftPaddingChanged(); Q_REVISION(6) void rightPaddingChanged(); Q_REVISION(6) void bottomPaddingChanged(); + Q_REVISION(9) void fontInfoChanged(); protected: QQuickText(QQuickTextPrivate &dd, QQuickItem *parent = 0); diff --git a/src/quick/items/qquicktext_p_p.h b/src/quick/items/qquicktext_p_p.h index 0669bcf115..6456750359 100644 --- a/src/quick/items/qquicktext_p_p.h +++ b/src/quick/items/qquicktext_p_p.h @@ -122,6 +122,7 @@ public: QString text; QFont font; QFont sourceFont; + QFontInfo fontInfo; QTextLayout layout; QTextLayout *elideLayout; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 660c5f8067..57810e4ec9 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -453,8 +453,13 @@ void QQuickWindowPrivate::renderSceneGraph(const QSize &size) fboId = renderTargetId; renderer->setDeviceRect(rect); renderer->setViewportRect(rect); - renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), rect.size())); - renderer->setDevicePixelRatio(1); + if (QQuickRenderControl::renderWindowFor(q)) { + renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), size)); + renderer->setDevicePixelRatio(devicePixelRatio); + } else { + renderer->setProjectionMatrixToRect(QRect(QPoint(0, 0), rect.size())); + renderer->setDevicePixelRatio(1); + } } else { QRect rect(QPoint(0, 0), devicePixelRatio * size); renderer->setDeviceRect(rect); @@ -1798,7 +1803,7 @@ bool QQuickWindowPrivate::deliverWheelEvent(QQuickItem *item, QWheelEvent *event QPointF p = item->mapFromScene(event->posF()); if (item->contains(p)) { - QWheelEvent wheel(p, p, event->pixelDelta(), event->angleDelta(), event->delta(), + QWheelEvent wheel(p, event->globalPosF(), event->pixelDelta(), event->angleDelta(), event->delta(), event->orientation(), event->buttons(), event->modifiers(), event->phase(), event->source(), event->inverted()); wheel.setTimestamp(event->timestamp()); wheel.accept(); @@ -4167,25 +4172,25 @@ void QQuickWindow::resetOpenGLState() */ /*! - \qmlproperty variant Window::targetScreen - - Specifies the screen the window should be placed on. Equivalent to - QWindow::setScreen(). + \qmlproperty variant Window::screen - The value must be an element from the Qt.application.screens array. + The screen with which the window is associated. - By default the value is null which leads to using the primary screen. + If specified before showing a window, will result in the window being shown + on that screen, unless an explicit window position has been set. The value + must be an element from the Qt.application.screens array. - \note To ensure that the window is associated with the desired screen right - upon the underlying native window's initial creation, make sure this - property is set as early as possible and that the setting of its value is - not deferred. This can be particularly important on embedded platforms - without a windowing system, where only one window per screen is allowed at a - time. + \note To ensure that the window is associated with the desired screen when + the underlying native window is created, make sure this property is set as + early as possible and that the setting of its value is not deferred. This + can be particularly important on embedded platforms without a windowing system, + where only one window per screen is allowed at a time. Setting the screen after + a window has been created does not move the window if the new screen is part of + the same virtual desktop as the old screen. \since 5.9 - \sa QWindow::setScreen(), QScreen, Qt.application + \sa QWindow::setScreen(), QWindow::screen(), QScreen, Qt.application */ /*! diff --git a/src/quick/items/qquickwindowmodule.cpp b/src/quick/items/qquickwindowmodule.cpp index 42313e4584..6211b7802f 100644 --- a/src/quick/items/qquickwindowmodule.cpp +++ b/src/quick/items/qquickwindowmodule.cpp @@ -59,7 +59,6 @@ public: : complete(false) , visible(false) , visibility(QQuickWindow::AutomaticVisibility) - , targetScreen(nullptr) { } @@ -67,7 +66,6 @@ public: bool visible; QQuickWindow::Visibility visibility; QV4::PersistentValue rootItemMarker; - QObject *targetScreen; }; QQuickWindowQmlImpl::QQuickWindowQmlImpl(QWindow *parent) @@ -75,6 +73,7 @@ QQuickWindowQmlImpl::QQuickWindowQmlImpl(QWindow *parent) { connect(this, &QWindow::visibleChanged, this, &QQuickWindowQmlImpl::visibleChanged); connect(this, &QWindow::visibilityChanged, this, &QQuickWindowQmlImpl::visibilityChanged); + connect(this, &QWindow::screenChanged, this, &QQuickWindowQmlImpl::screenChanged); } void QQuickWindowQmlImpl::setVisible(bool visible) @@ -175,24 +174,15 @@ void QQuickWindowQmlImpl::setWindowVisibility() } } -QObject *QQuickWindowQmlImpl::targetScreen() const +QObject *QQuickWindowQmlImpl::screen() const { - Q_D(const QQuickWindowQmlImpl); - return d->targetScreen; + return new QQuickScreenInfo(const_cast<QQuickWindowQmlImpl *>(this), QWindow::screen()); } -void QQuickWindowQmlImpl::setTargetScreen(QObject *screen) +void QQuickWindowQmlImpl::setScreen(QObject *screen) { - Q_D(QQuickWindowQmlImpl); - if (d->targetScreen != screen) { - d->targetScreen = screen; - emit targetScreenChanged(); - QQuickScreenInfo *screenWrapper = qobject_cast<QQuickScreenInfo *>(screen); - if (screenWrapper) - setScreen(screenWrapper->wrappedScreen()); - else - setScreen(nullptr); - } + QQuickScreenInfo *screenWrapper = qobject_cast<QQuickScreenInfo *>(screen); + QWindow::setScreen(screenWrapper ? screenWrapper->wrappedScreen() : nullptr); } void QQuickWindowModule::defineModule() diff --git a/src/quick/items/qquickwindowmodule_p.h b/src/quick/items/qquickwindowmodule_p.h index 7ca29880ea..16130bc8a0 100644 --- a/src/quick/items/qquickwindowmodule_p.h +++ b/src/quick/items/qquickwindowmodule_p.h @@ -67,7 +67,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickWindowQmlImpl : public QQuickWindow, public Q Q_PROPERTY(bool visible READ isVisible WRITE setVisible NOTIFY visibleChanged) Q_PROPERTY(Visibility visibility READ visibility WRITE setVisibility NOTIFY visibilityChanged) - Q_PROPERTY(QObject *targetScreen READ targetScreen WRITE setTargetScreen NOTIFY targetScreenChanged REVISION 2) + Q_PROPERTY(QObject *screen READ screen WRITE setScreen NOTIFY screenChanged REVISION 2) public: QQuickWindowQmlImpl(QWindow *parent = Q_NULLPTR); @@ -75,15 +75,15 @@ public: void setVisible(bool visible); void setVisibility(Visibility visibility); - QObject *targetScreen() const; - void setTargetScreen(QObject *screen); + QObject *screen() const; + void setScreen(QObject *screen); static QQuickWindowAttached *qmlAttachedProperties(QObject *object); Q_SIGNALS: void visibleChanged(bool arg); void visibilityChanged(QWindow::Visibility visibility); - Q_REVISION(2) void targetScreenChanged(); + Q_REVISION(2) void screenChanged(); protected: void classBegin() Q_DECL_OVERRIDE; diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h index d3f13e40b1..9f5a22e66f 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarelayer_p.h @@ -93,6 +93,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/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp index f8c1a3d90b..ad6cf39425 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepixmaprenderer.cpp @@ -64,7 +64,7 @@ void QSGSoftwarePixmapRenderer::renderScene(uint) class B : public QSGBindable { public: - void bind() const { } + void bind() const override { } } bindable; QSGRenderer::renderScene(bindable); } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp index 1fa5234377..d754089ce4 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarepublicnodes.cpp @@ -98,10 +98,10 @@ void QSGSoftwareImageNode::paint(QPainter *painter) if (!m_cachedPixmap.isNull()) { painter->drawPixmap(m_rect, m_cachedPixmap, m_sourceRect); - } else if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) { + } else if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(m_texture)) { const QPixmap &pm = pt->pixmap(); painter->drawPixmap(m_rect, pm, m_sourceRect); - } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) { + } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(m_texture)) { const QImage &im = pt->image(); painter->drawImage(m_rect, im, m_sourceRect); } @@ -113,14 +113,14 @@ void QSGSoftwareImageNode::updateCachedMirroredPixmap() m_cachedPixmap = QPixmap(); } else { - if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(m_texture)) { + if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(m_texture)) { QTransform mirrorTransform; if (m_transformMode.testFlag(MirrorVertically)) mirrorTransform = mirrorTransform.scale(1, -1); if (m_transformMode.testFlag(MirrorHorizontally)) mirrorTransform = mirrorTransform.scale(-1, 1); m_cachedPixmap = pt->pixmap().transformed(mirrorTransform); - } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(m_texture)) { + } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(m_texture)) { m_cachedPixmap = QPixmap::fromImage(pt->image().mirrored(m_transformMode.testFlag(MirrorHorizontally), m_transformMode.testFlag(MirrorVertically))); } else { m_cachedPixmap = QPixmap(); diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp index 7adb39d9a8..f67e2106c8 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderablenode.cpp @@ -296,10 +296,10 @@ QRegion QSGSoftwareRenderableNode::renderNode(QPainter *painter, bool forceOpaqu case QSGSoftwareRenderableNode::SimpleTexture: { QSGTexture *texture = m_handle.simpleTextureNode->texture(); - if (QSGSoftwarePixmapTexture *pt = dynamic_cast<QSGSoftwarePixmapTexture *>(texture)) { + if (QSGSoftwarePixmapTexture *pt = qobject_cast<QSGSoftwarePixmapTexture *>(texture)) { const QPixmap &pm = pt->pixmap(); painter->drawPixmap(m_handle.simpleTextureNode->rect(), pm, m_handle.simpleTextureNode->sourceRect()); - } else if (QSGPlainTexture *pt = dynamic_cast<QSGPlainTexture *>(texture)) { + } else if (QSGPlainTexture *pt = qobject_cast<QSGPlainTexture *>(texture)) { const QImage &im = pt->image(); painter->drawImage(m_handle.simpleTextureNode->rect(), im, m_handle.simpleTextureNode->sourceRect()); } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp index cad826fb27..85d04fe136 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarerenderer.cpp @@ -90,7 +90,7 @@ void QSGSoftwareRenderer::renderScene(uint) class B : public QSGBindable { public: - void bind() const { } + void bind() const override { } } bindable; QSGRenderer::renderScene(bindable); } diff --git a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp index 8abbefdd48..682f89721e 100644 --- a/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp +++ b/src/quick/scenegraph/adaptations/software/qsgsoftwarethreadedrenderloop.cpp @@ -188,8 +188,8 @@ public: delete rc; } - bool event(QEvent *e); - void run(); + bool event(QEvent *e) override; + void run() override; void syncAndRender(); void sync(bool inExpose); diff --git a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h index 322192944b..2c0f8667e8 100644 --- a/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h +++ b/src/quick/scenegraph/coreapi/qsgbatchrenderer_p.h @@ -440,9 +440,7 @@ struct Batch mutable uint uploadedThisFrame : 1; // solely for debugging purposes Buffer vbo; -#ifdef QSG_SEPARATE_INDEX_BUFFER Buffer ibo; -#endif QDataBuffer<DrawSet> drawSets; }; @@ -744,9 +742,7 @@ private: ClipType m_currentClipType; QDataBuffer<char> m_vertexUploadPool; -#ifdef QSG_SEPARATE_INDEX_BUFFER QDataBuffer<char> m_indexUploadPool; -#endif // For minimal OpenGL core profile support QOpenGLVertexArrayObject *m_vao; @@ -766,10 +762,7 @@ Batch *Renderer::newBatch() m_batchPool.resize(size - 1); } else { b = new Batch(); - memset(&b->vbo, 0, sizeof(Buffer)); -#ifdef QSG_SEPARATE_INDEX_BUFFER - memset(&b->ibo, 0, sizeof(Buffer)); -#endif + memset(&b->vbo, 0, sizeof(Buffer) * 2); // Clear VBO & IBO } b->init(); return b; diff --git a/src/quick/scenegraph/coreapi/qsgnode.cpp b/src/quick/scenegraph/coreapi/qsgnode.cpp index a1e1ef8c27..7ef75d4b4c 100644 --- a/src/quick/scenegraph/coreapi/qsgnode.cpp +++ b/src/quick/scenegraph/coreapi/qsgnode.cpp @@ -337,13 +337,6 @@ QSGNode::~QSGNode() to the scene graph and will cause the preprocess() function to be called for every frame the node is rendered. - The preprocess function is called before the update pass that propagates - opacity and transformations through the scene graph. That means that - functions like QSGOpacityNode::combinedOpacity() and - QSGTransformNode::combinedMatrix() will not contain up-to-date values. - If such values are changed during the preprocess, these changes will be - propagated through the scene graph before it is rendered. - \warning Beware of deleting nodes while they are being preprocessed. It is possible, with a small performance hit, to delete a single node during its own preprocess call. Deleting a subtree which has nodes that also use diff --git a/src/quick/scenegraph/coreapi/qsgrenderer.cpp b/src/quick/scenegraph/coreapi/qsgrenderer.cpp index e5d464930c..9207fdbc55 100644 --- a/src/quick/scenegraph/coreapi/qsgrenderer.cpp +++ b/src/quick/scenegraph/coreapi/qsgrenderer.cpp @@ -189,7 +189,7 @@ void QSGRenderer::renderScene(uint fboId) class B : public QSGBindable { public: - void bind() const { QOpenGLFramebufferObject::bindDefault(); } + void bind() const override { QOpenGLFramebufferObject::bindDefault(); } } bindable; renderScene(bindable); } diff --git a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp index 3b0b2faf97..0f49e615e4 100644 --- a/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp +++ b/src/quick/scenegraph/coreapi/qsgrendererinterface.cpp @@ -127,7 +127,7 @@ QSGRendererInterface::~QSGRendererInterface() */ /*! - Queries a graphics \a resource. Returns null when the resource in question is + Queries a graphics \a resource in \a window. Returns null when the resource in question is not supported or not available. When successful, the returned pointer is either a direct pointer to an diff --git a/src/quick/scenegraph/qsgadaptationlayer_p.h b/src/quick/scenegraph/qsgadaptationlayer_p.h index 03a1f7f281..a8e35b1ac1 100644 --- a/src/quick/scenegraph/qsgadaptationlayer_p.h +++ b/src/quick/scenegraph/qsgadaptationlayer_p.h @@ -209,6 +209,7 @@ public: virtual void setDevicePixelRatio(qreal ratio) = 0; virtual void setMirrorHorizontal(bool mirror) = 0; virtual void setMirrorVertical(bool mirror) = 0; + virtual void setSamples(int samples) = 0; Q_SLOT virtual void markDirtyTexture() = 0; Q_SLOT virtual void invalidated() = 0; diff --git a/src/quick/scenegraph/qsgcontext_p.h b/src/quick/scenegraph/qsgcontext_p.h index 6b9db105e7..2f5d5790ee 100644 --- a/src/quick/scenegraph/qsgcontext_p.h +++ b/src/quick/scenegraph/qsgcontext_p.h @@ -88,6 +88,7 @@ class QSGRectangleNode; class QSGImageNode; class QSGNinePatchNode; class QSGSpriteNode; +class QSGRenderContext; Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERLOOP) Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_COMPILATION) @@ -98,6 +99,54 @@ Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_TIME_RENDERER) Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_INFO) Q_DECLARE_LOGGING_CATEGORY(QSG_LOG_RENDERLOOP) +class Q_QUICK_PRIVATE_EXPORT QSGContext : public QObject +{ + Q_OBJECT + +public: + enum AntialiasingMethod { + UndecidedAntialiasing, + VertexAntialiasing, + MsaaAntialiasing + }; + + explicit QSGContext(QObject *parent = 0); + virtual ~QSGContext(); + + virtual void renderContextInitialized(QSGRenderContext *renderContext); + virtual void renderContextInvalidated(QSGRenderContext *renderContext); + virtual QSGRenderContext *createRenderContext() = 0; + + QSGInternalRectangleNode *createInternalRectangleNode(const QRectF &rect, const QColor &c); + virtual QSGInternalRectangleNode *createInternalRectangleNode() = 0; + virtual QSGInternalImageNode *createInternalImageNode() = 0; + virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item) = 0; + virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) = 0; + virtual QSGLayer *createLayer(QSGRenderContext *renderContext) = 0; + virtual QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager(); + virtual QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext, + QSGGuiThreadShaderEffectManager *mgr); +#if QT_CONFIG(quick_sprite) + virtual QSGSpriteNode *createSpriteNode() = 0; +#endif + virtual QAnimationDriver *createAnimationDriver(QObject *parent); + + virtual QSize minimumFBOSize() const; + virtual QSurfaceFormat defaultSurfaceFormat() const = 0; + + virtual QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext); + + virtual QSGRectangleNode *createRectangleNode() = 0; + virtual QSGImageNode *createImageNode() = 0; + virtual QSGNinePatchNode *createNinePatchNode() = 0; + + static QSGContext *createDefaultContext(); + static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image); + static QSGRenderLoop *createWindowManager(); + + static void setBackend(const QString &backend); +}; + class Q_QUICK_PRIVATE_EXPORT QSGRenderContext : public QObject { Q_OBJECT @@ -150,55 +199,6 @@ protected: QSet<QFontEngine *> m_fontEnginesToClean; }; - -class Q_QUICK_PRIVATE_EXPORT QSGContext : public QObject -{ - Q_OBJECT - -public: - enum AntialiasingMethod { - UndecidedAntialiasing, - VertexAntialiasing, - MsaaAntialiasing - }; - - explicit QSGContext(QObject *parent = 0); - virtual ~QSGContext(); - - virtual void renderContextInitialized(QSGRenderContext *renderContext); - virtual void renderContextInvalidated(QSGRenderContext *renderContext); - virtual QSGRenderContext *createRenderContext() = 0; - - QSGInternalRectangleNode *createInternalRectangleNode(const QRectF &rect, const QColor &c); - virtual QSGInternalRectangleNode *createInternalRectangleNode() = 0; - virtual QSGInternalImageNode *createInternalImageNode() = 0; - virtual QSGPainterNode *createPainterNode(QQuickPaintedItem *item) = 0; - virtual QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) = 0; - virtual QSGLayer *createLayer(QSGRenderContext *renderContext) = 0; - virtual QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager(); - virtual QSGShaderEffectNode *createShaderEffectNode(QSGRenderContext *renderContext, - QSGGuiThreadShaderEffectManager *mgr); -#if QT_CONFIG(quick_sprite) - virtual QSGSpriteNode *createSpriteNode() = 0; -#endif - virtual QAnimationDriver *createAnimationDriver(QObject *parent); - - virtual QSize minimumFBOSize() const; - virtual QSurfaceFormat defaultSurfaceFormat() const = 0; - - virtual QSGRendererInterface *rendererInterface(QSGRenderContext *renderContext); - - virtual QSGRectangleNode *createRectangleNode() = 0; - virtual QSGImageNode *createImageNode() = 0; - virtual QSGNinePatchNode *createNinePatchNode() = 0; - - static QSGContext *createDefaultContext(); - static QQuickTextureFactory *createTextureFactoryFromImage(const QImage &image); - static QSGRenderLoop *createWindowManager(); - - static void setBackend(const QString &backend); -}; - QT_END_NAMESPACE #endif // QSGCONTEXT_H diff --git a/src/quick/scenegraph/qsgdefaultcontext.cpp b/src/quick/scenegraph/qsgdefaultcontext.cpp index 405f1d86a4..d31a7025e5 100644 --- a/src/quick/scenegraph/qsgdefaultcontext.cpp +++ b/src/quick/scenegraph/qsgdefaultcontext.cpp @@ -68,13 +68,13 @@ QT_BEGIN_NAMESPACE namespace QSGMultisampleAntialiasing { class ImageNode : public QSGDefaultInternalImageNode { public: - void setAntialiasing(bool) { } + void setAntialiasing(bool) override { } }; class RectangleNode : public QSGDefaultInternalRectangleNode { public: - void setAntialiasing(bool) { } + void setAntialiasing(bool) override { } }; } diff --git a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp index b001899915..edb6e92a0d 100644 --- a/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdefaultglyphnode_p.cpp @@ -91,11 +91,11 @@ class QSGTextMaskShader : public QSGMaterialShader public: QSGTextMaskShader(QFontEngine::GlyphFormat glyphFormat); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; protected: - virtual void initialize(); + void initialize() override; int m_matrix_id; int m_color_id; @@ -181,7 +181,7 @@ public: setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/8bittextmask.frag")); } - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; }; void QSG8BitTextMaskShader::updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) @@ -206,10 +206,10 @@ public: setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/24bittextmask.frag")); } - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual void initialize(); - void activate(); - void deactivate(); + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + void initialize() override; + void activate() override; + void deactivate() override; bool useSRGB() const; uint m_useSRGB : 1; @@ -326,10 +326,10 @@ public: setShaderSourceFile(QOpenGLShader::Fragment, QStringLiteral(":/qt-project.org/scenegraph/shaders/styledtext.frag")); } - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; private: - virtual void initialize(); + void initialize() override; int m_shift_id; int m_styleColor_id; diff --git a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp index 1d54628acd..a5a6da06a7 100644 --- a/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp +++ b/src/quick/scenegraph/qsgdefaultinternalimagenode.cpp @@ -50,11 +50,11 @@ class SmoothTextureMaterialShader : public QSGTextureMaterialShader public: SmoothTextureMaterialShader(); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; protected: - virtual void initialize(); + void initialize() override; int m_pixelSizeLoc; }; diff --git a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp index 94414444ba..e52dcaad52 100644 --- a/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp +++ b/src/quick/scenegraph/qsgdefaultinternalrectanglenode.cpp @@ -55,11 +55,11 @@ class SmoothColorMaterialShader : public QSGMaterialShader public: SmoothColorMaterialShader(); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; private: - virtual void initialize(); + void initialize() override; int m_matrixLoc; int m_opacityLoc; diff --git a/src/quick/scenegraph/qsgdefaultlayer.cpp b/src/quick/scenegraph/qsgdefaultlayer.cpp index ad5b57ff83..fc8040e67b 100644 --- a/src/quick/scenegraph/qsgdefaultlayer.cpp +++ b/src/quick/scenegraph/qsgdefaultlayer.cpp @@ -99,6 +99,7 @@ QSGDefaultLayer::QSGDefaultLayer(QSGRenderContext *context) #ifdef QSG_DEBUG_FBO_OVERLAY , m_debugOverlay(0) #endif + , m_samples(0) , m_mipmap(false) , m_live(true) , m_recursive(false) @@ -313,11 +314,20 @@ void QSGDefaultLayer::grab() QOpenGLFunctions *funcs = QOpenGLContext::currentContext()->functions(); bool deleteFboLater = false; - if (!m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format - || (!m_fbo->format().mipmap() && m_mipmap)) - { + + int effectiveSamples = m_samples; + // By default m_samples is 0. Fall back to the context's setting in this case. + if (effectiveSamples == 0) + effectiveSamples = m_context->openglContext()->format().samples(); + + const bool needsNewFbo = !m_fbo || m_fbo->size() != m_size || m_fbo->format().internalTextureFormat() != m_format; + const bool mipmapGotEnabled = m_fbo && !m_fbo->format().mipmap() && m_mipmap; + const bool msaaGotEnabled = effectiveSamples > 1 && (!m_secondaryFbo || m_secondaryFbo->format().samples() != effectiveSamples); + const bool msaaGotDisabled = effectiveSamples <= 1 && m_secondaryFbo; + + if (needsNewFbo || mipmapGotEnabled || msaaGotEnabled || msaaGotDisabled) { if (!m_multisamplingChecked) { - if (m_context->openglContext()->format().samples() <= 1) { + if (effectiveSamples <= 1) { m_multisampling = false; } else { const QSet<QByteArray> extensions = m_context->openglContext()->extensions(); @@ -333,7 +343,7 @@ void QSGDefaultLayer::grab() QOpenGLFramebufferObjectFormat format; format.setInternalTextureFormat(m_format); - format.setSamples(m_context->openglContext()->format().samples()); + format.setSamples(effectiveSamples); m_secondaryFbo = new QOpenGLFramebufferObject(m_size, format); m_depthStencilBuffer = m_context->depthStencilBufferForFbo(m_secondaryFbo); } else { diff --git a/src/quick/scenegraph/qsgdefaultlayer_p.h b/src/quick/scenegraph/qsgdefaultlayer_p.h index ae39994096..7b09293095 100644 --- a/src/quick/scenegraph/qsgdefaultlayer_p.h +++ b/src/quick/scenegraph/qsgdefaultlayer_p.h @@ -113,6 +113,9 @@ public: QRectF normalizedTextureSubRect() const Q_DECL_OVERRIDE; + int samples() const { return m_samples; } + void setSamples(int samples) Q_DECL_OVERRIDE { m_samples = samples; } + public Q_SLOTS: void markDirtyTexture() Q_DECL_OVERRIDE; void invalidated() Q_DECL_OVERRIDE; @@ -138,6 +141,7 @@ private: #endif QSGDefaultRenderContext *m_context; + int m_samples; uint m_mipmap : 1; uint m_live : 1; diff --git a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp index ca91e5d85f..fc66f2f2cc 100644 --- a/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp +++ b/src/quick/scenegraph/qsgdistancefieldglyphnode_p.cpp @@ -52,11 +52,11 @@ class QSGDistanceFieldTextMaterialShader : public QSGMaterialShader public: QSGDistanceFieldTextMaterialShader(); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; protected: - virtual void initialize(); + void initialize() override; void updateAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc); void updateColor(const QVector4D &c); @@ -261,10 +261,10 @@ class DistanceFieldStyledTextMaterialShader : public QSGDistanceFieldTextMateria public: DistanceFieldStyledTextMaterialShader(); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; protected: - virtual void initialize(); + void initialize() override; int m_styleColor_id; }; @@ -329,10 +329,10 @@ class DistanceFieldOutlineTextMaterialShader : public DistanceFieldStyledTextMat public: DistanceFieldOutlineTextMaterialShader(); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; protected: - virtual void initialize(); + void initialize() override; void updateOutlineAlphaRange(ThresholdFunc thresholdFunc, AntialiasingSpreadFunc spreadFunc, int dfRadius); @@ -413,10 +413,10 @@ class DistanceFieldShiftedStyleTextMaterialShader : public DistanceFieldStyledTe public: DistanceFieldShiftedStyleTextMaterialShader(); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; protected: - virtual void initialize(); + void initialize() override; void updateShift(qreal fontScale, const QPointF& shift); @@ -492,10 +492,10 @@ class QSGHiQSubPixelDistanceFieldTextMaterialShader : public QSGDistanceFieldTex public: QSGHiQSubPixelDistanceFieldTextMaterialShader(); - virtual void initialize(); - virtual void activate(); - virtual void deactivate(); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); + void initialize() override; + void activate() override; + void deactivate() override; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; private: int m_fontScale_id; diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 48288bfc62..bb581c5e36 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -139,25 +139,25 @@ public: QSGGuiThreadRenderLoop(); ~QSGGuiThreadRenderLoop(); - void show(QQuickWindow *window); - void hide(QQuickWindow *window); + void show(QQuickWindow *window) override; + void hide(QQuickWindow *window) override; - void windowDestroyed(QQuickWindow *window); + void windowDestroyed(QQuickWindow *window) override; void renderWindow(QQuickWindow *window); - void exposureChanged(QQuickWindow *window); - QImage grab(QQuickWindow *window); + void exposureChanged(QQuickWindow *window) override; + QImage grab(QQuickWindow *window) override; - void maybeUpdate(QQuickWindow *window); - void update(QQuickWindow *window) { maybeUpdate(window); } // identical for this implementation. - void handleUpdateRequest(QQuickWindow *); + void maybeUpdate(QQuickWindow *window) override; + void update(QQuickWindow *window) override { maybeUpdate(window); } // identical for this implementation. + void handleUpdateRequest(QQuickWindow *) override; - void releaseResources(QQuickWindow *) { } + void releaseResources(QQuickWindow *) override { } - QAnimationDriver *animationDriver() const { return 0; } + QAnimationDriver *animationDriver() const override { return 0; } - QSGContext *sceneGraphContext() const; - QSGRenderContext *createRenderContext(QSGContext *) const { return rc; } + QSGContext *sceneGraphContext() const override; + QSGRenderContext *createRenderContext(QSGContext *) const override { return rc; } struct WindowData { bool updatePending : 1; diff --git a/src/quick/scenegraph/qsgthreadedrenderloop.cpp b/src/quick/scenegraph/qsgthreadedrenderloop.cpp index 5fa74027c1..17a2c62a4e 100644 --- a/src/quick/scenegraph/qsgthreadedrenderloop.cpp +++ b/src/quick/scenegraph/qsgthreadedrenderloop.cpp @@ -295,8 +295,8 @@ public: void invalidateOpenGL(QQuickWindow *window, bool inDestructor, QOffscreenSurface *backupSurface); void initializeOpenGL(); - bool event(QEvent *); - void run(); + bool event(QEvent *) override; + void run() override; void syncAndRender(); void sync(bool inExpose); diff --git a/src/quick/scenegraph/scenegraph.pri b/src/quick/scenegraph/scenegraph.pri index edf4aa08c5..38c3b8dd85 100644 --- a/src/quick/scenegraph/scenegraph.pri +++ b/src/quick/scenegraph/scenegraph.pri @@ -1,4 +1,4 @@ -# DEFINES += QSG_SEPARATE_INDEX_BUFFER +DEFINES += QSG_SEPARATE_INDEX_BUFFER # DEFINES += QSG_DISTANCEFIELD_CACHE_DEBUG # Core API diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode.cpp b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp index 63773887a0..7186ee4265 100644 --- a/src/quick/scenegraph/util/qsgdefaultimagenode.cpp +++ b/src/quick/scenegraph/util/qsgdefaultimagenode.cpp @@ -94,6 +94,21 @@ QSGTexture::Filtering QSGDefaultImageNode::mipmapFiltering() const return m_material.mipmapFiltering(); } +void QSGDefaultImageNode::setAnisotropyLevel(QSGTexture::AnisotropyLevel level) +{ + if (m_material.anisotropyLevel() == level) + return; + + m_material.setAnisotropyLevel(level); + m_opaque_material.setAnisotropyLevel(level); + markDirty(DirtyMaterial); +} + +QSGTexture::AnisotropyLevel QSGDefaultImageNode::anisotropyLevel() const +{ + return m_material.anisotropyLevel(); +} + void QSGDefaultImageNode::setRect(const QRectF &r) { if (m_rect == r) diff --git a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h index bb9ebec885..cb23e759d3 100644 --- a/src/quick/scenegraph/util/qsgdefaultimagenode_p.h +++ b/src/quick/scenegraph/util/qsgdefaultimagenode_p.h @@ -85,6 +85,10 @@ public: void setOwnsTexture(bool owns) override; bool ownsTexture() const override; + // QSGImageNode now being a public class does not allow any additional virtual methods. Placing these here, non-virtual. + void setAnisotropyLevel(QSGTexture::AnisotropyLevel level); + QSGTexture::AnisotropyLevel anisotropyLevel() const; + private: QSGGeometry m_geometry; QSGOpaqueTextureMaterial m_opaque_material; diff --git a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp index 8ab7669891..a0c71b5340 100644 --- a/src/quick/scenegraph/util/qsgflatcolormaterial.cpp +++ b/src/quick/scenegraph/util/qsgflatcolormaterial.cpp @@ -50,13 +50,13 @@ class FlatColorMaterialShader : public QSGMaterialShader public: FlatColorMaterialShader(); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; static QSGMaterialType type; private: - virtual void initialize(); + void initialize() override; #if QT_CONFIG(opengl) int m_matrix_id; int m_color_id; diff --git a/src/quick/scenegraph/util/qsgimagenode.h b/src/quick/scenegraph/util/qsgimagenode.h index d25e732e4b..0e053c307f 100644 --- a/src/quick/scenegraph/util/qsgimagenode.h +++ b/src/quick/scenegraph/util/qsgimagenode.h @@ -67,6 +67,8 @@ public: virtual void setMipmapFiltering(QSGTexture::Filtering filtering) = 0; virtual QSGTexture::Filtering mipmapFiltering() const = 0; + // ### Qt6: Add anisotropy support here, and possibly a virtual hook or another mean to extend this class. + enum TextureCoordinatesTransformFlag { NoTransform = 0x00, MirrorHorizontally = 0x01, diff --git a/src/quick/scenegraph/util/qsgtexture.cpp b/src/quick/scenegraph/util/qsgtexture.cpp index d761eac62f..521c5e666f 100644 --- a/src/quick/scenegraph/util/qsgtexture.cpp +++ b/src/quick/scenegraph/util/qsgtexture.cpp @@ -83,6 +83,9 @@ static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK" #define GL_BGRA 0x80E1 #endif +#ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT +#define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE +#endif QT_BEGIN_NAMESPACE @@ -97,10 +100,12 @@ inline static bool isPowerOfTwo(int x) QSGTexturePrivate::QSGTexturePrivate() : wrapChanged(false) , filteringChanged(false) + , anisotropyChanged(false) , horizontalWrap(QSGTexture::ClampToEdge) , verticalWrap(QSGTexture::ClampToEdge) , mipmapMode(QSGTexture::None) , filterMode(QSGTexture::Nearest) + , anisotropyLevel(QSGTexture::AnisotropyNone) { } @@ -278,6 +283,23 @@ static void qt_debug_remove_texture(QSGTexture* texture) */ /*! + \enum QSGTexture::AnisotropyLevel + + Specifies the anisotropic filtering level to be used when + the texture is not screen aligned. + + \value AnisotropyNone No anisotropic filtering. + + \value Anisotropy2x 2x anisotropic filtering. + + \value Anisotropy4x 4x anisotropic filtering. + + \value Anisotropy8x 8x anisotropic filtering. + + \value Anisotropy16x 16x anisotropic filtering. +*/ + +/*! \fn QSGTexture::QSGTexture(QSGTexturePrivate &dd) \internal */ @@ -476,6 +498,31 @@ QSGTexture::Filtering QSGTexture::filtering() const return (QSGTexture::Filtering) d_func()->filterMode; } +/*! + Sets the level of anisotropic filtering to be used for the upcoming bind() call to \a level. + The default value is QSGTexture::AnisotropyNone, which means no anisotropic filtering is enabled. + + \since 5.9 + */ +void QSGTexture::setAnisotropyLevel(AnisotropyLevel level) +{ + Q_D(QSGTexture); + if (d->anisotropyLevel != (uint) level) { + d->anisotropyLevel = level; + d->anisotropyChanged = true; + } +} + +/*! + Returns the anisotropy level in use for filtering this texture. + + \since 5.9 + */ +QSGTexture::AnisotropyLevel QSGTexture::anisotropyLevel() const +{ + return (QSGTexture::AnisotropyLevel) d_func()->anisotropyLevel; +} + /*! @@ -552,6 +599,12 @@ void QSGTexture::updateBindOptions(bool force) d->filteringChanged = false; } + if (force || d->anisotropyChanged) { + d->anisotropyChanged = false; + if (QOpenGLContext::currentContext()->hasExtension(QByteArrayLiteral("GL_EXT_texture_filter_anisotropic"))) + funcs->glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, float(1 << (d->anisotropyLevel))); + } + if (force || d->wrapChanged) { #ifndef QT_NO_DEBUG if (d->horizontalWrap == Repeat || d->verticalWrap == Repeat diff --git a/src/quick/scenegraph/util/qsgtexture.h b/src/quick/scenegraph/util/qsgtexture.h index 2b29525efd..032129434e 100644 --- a/src/quick/scenegraph/util/qsgtexture.h +++ b/src/quick/scenegraph/util/qsgtexture.h @@ -68,6 +68,14 @@ public: Linear }; + enum AnisotropyLevel { + AnisotropyNone, + Anisotropy2x, + Anisotropy4x, + Anisotropy8x, + Anisotropy16x + }; + virtual int textureId() const = 0; virtual QSize textureSize() const = 0; virtual bool hasAlphaChannel() const = 0; @@ -88,6 +96,9 @@ public: void setFiltering(Filtering filter); QSGTexture::Filtering filtering() const; + void setAnisotropyLevel(AnisotropyLevel level); + QSGTexture::AnisotropyLevel anisotropyLevel() const; + void setHorizontalWrapMode(WrapMode hwrap); QSGTexture::WrapMode horizontalWrapMode() const; diff --git a/src/quick/scenegraph/util/qsgtexture_p.h b/src/quick/scenegraph/util/qsgtexture_p.h index 3a10d85b4d..52dc6db2d0 100644 --- a/src/quick/scenegraph/util/qsgtexture_p.h +++ b/src/quick/scenegraph/util/qsgtexture_p.h @@ -69,11 +69,13 @@ public: uint wrapChanged : 1; uint filteringChanged : 1; + uint anisotropyChanged : 1; uint horizontalWrap : 2; uint verticalWrap : 2; uint mipmapMode : 2; uint filterMode : 2; + uint anisotropyLevel: 3; }; class Q_QUICK_PRIVATE_EXPORT QSGPlainTexture : public QSGTexture diff --git a/src/quick/scenegraph/util/qsgtexturematerial.cpp b/src/quick/scenegraph/util/qsgtexturematerial.cpp index fcafbba6a2..fbc8f27a63 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.cpp +++ b/src/quick/scenegraph/util/qsgtexturematerial.cpp @@ -110,6 +110,7 @@ void QSGOpaqueTextureMaterialShader::updateState(const RenderState &state, QSGMa Q_UNUSED(state) #endif t->setMipmapFiltering(tx->mipmapFiltering()); + t->setAnisotropyLevel(tx->anisotropyLevel()); if (oldTx == 0 || oldTx->texture()->textureId() != t->textureId()) t->bind(); @@ -173,6 +174,7 @@ QSGOpaqueTextureMaterial::QSGOpaqueTextureMaterial() , m_mipmap_filtering(QSGTexture::None) , m_horizontal_wrap(QSGTexture::ClampToEdge) , m_vertical_wrap(QSGTexture::ClampToEdge) + , m_anisotropy_level(QSGTexture::AnisotropyNone) { } diff --git a/src/quick/scenegraph/util/qsgtexturematerial.h b/src/quick/scenegraph/util/qsgtexturematerial.h index dc87131773..87d8e5fd49 100644 --- a/src/quick/scenegraph/util/qsgtexturematerial.h +++ b/src/quick/scenegraph/util/qsgtexturematerial.h @@ -69,6 +69,9 @@ public: void setVerticalWrapMode(QSGTexture::WrapMode mode) { m_vertical_wrap = mode; } QSGTexture::WrapMode verticalWrapMode() const { return QSGTexture::WrapMode(m_vertical_wrap); } + void setAnisotropyLevel(QSGTexture::AnisotropyLevel level) { m_anisotropy_level = level; } + QSGTexture::AnisotropyLevel anisotropyLevel() const { return QSGTexture::AnisotropyLevel(m_anisotropy_level); } + protected: QSGTexture *m_texture; @@ -76,8 +79,8 @@ protected: uint m_mipmap_filtering: 2; uint m_horizontal_wrap : 1; uint m_vertical_wrap: 1; - - uint m_reserved : 26; + uint m_anisotropy_level : 3; + uint m_reserved : 23; }; diff --git a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp index 8c305d7fd4..42c589b14a 100644 --- a/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp +++ b/src/quick/scenegraph/util/qsgvertexcolormaterial.cpp @@ -48,13 +48,13 @@ class QSGVertexColorMaterialShader : public QSGMaterialShader public: QSGVertexColorMaterialShader(); - virtual void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect); - virtual char const *const *attributeNames() const; + void updateState(const RenderState &state, QSGMaterial *newEffect, QSGMaterial *oldEffect) override; + char const *const *attributeNames() const override; static QSGMaterialType type; private: - virtual void initialize(); + void initialize() override; #if QT_CONFIG(opengl) int m_matrix_id; int m_opacity_id; diff --git a/src/quick/util/qquickglobal.cpp b/src/quick/util/qquickglobal.cpp index 5a723e4432..20bb23338d 100644 --- a/src/quick/util/qquickglobal.cpp +++ b/src/quick/util/qquickglobal.cpp @@ -61,7 +61,7 @@ QT_BEGIN_NAMESPACE class QQuickColorProvider : public QQmlColorProvider { public: - QVariant colorFromString(const QString &s, bool *ok) + QVariant colorFromString(const QString &s, bool *ok) override { QColor c(s); if (c.isValid()) { @@ -73,7 +73,7 @@ public: return QVariant(); } - unsigned rgbaFromString(const QString &s, bool *ok) + unsigned rgbaFromString(const QString &s, bool *ok) override { QColor c(s); if (c.isValid()) { @@ -95,36 +95,36 @@ public: return QString(); } - QVariant fromRgbF(double r, double g, double b, double a) + QVariant fromRgbF(double r, double g, double b, double a) override { return QVariant(QColor::fromRgbF(r, g, b, a)); } - QVariant fromHslF(double h, double s, double l, double a) + QVariant fromHslF(double h, double s, double l, double a) override { return QVariant(QColor::fromHslF(h, s, l, a)); } - QVariant fromHsvF(double h, double s, double v, double a) + QVariant fromHsvF(double h, double s, double v, double a) override { return QVariant(QColor::fromHsvF(h, s, v, a)); } - QVariant lighter(const QVariant &var, qreal factor) + QVariant lighter(const QVariant &var, qreal factor) override { QColor color = var.value<QColor>(); color = color.lighter(int(qRound(factor*100.))); return QVariant::fromValue(color); } - QVariant darker(const QVariant &var, qreal factor) + QVariant darker(const QVariant &var, qreal factor) override { QColor color = var.value<QColor>(); color = color.darker(int(qRound(factor*100.))); return QVariant::fromValue(color); } - QVariant tint(const QVariant &baseVar, const QVariant &tintVar) + QVariant tint(const QVariant &baseVar, const QVariant &tintVar) override { QColor tintColor = tintVar.value<QColor>(); @@ -778,13 +778,13 @@ public: class QQuickGuiProvider : public QQmlGuiProvider { public: - QQuickApplication *application(QObject *parent) + QQuickApplication *application(QObject *parent) override { return new QQuickApplication(parent); } #if QT_CONFIG(im) - QInputMethod *inputMethod() + QInputMethod *inputMethod() override { QInputMethod *im = qGuiApp->inputMethod(); QQmlEngine::setObjectOwnership(im, QQmlEngine::CppOwnership); @@ -792,20 +792,20 @@ public: } #endif - QStyleHints *styleHints() + QStyleHints *styleHints() override { QStyleHints *sh = qGuiApp->styleHints(); QQmlEngine::setObjectOwnership(sh, QQmlEngine::CppOwnership); return sh; } - QStringList fontFamilies() + QStringList fontFamilies() override { QFontDatabase database; return database.families(); } - bool openUrlExternally(QUrl &url) + bool openUrlExternally(QUrl &url) override { #ifndef QT_NO_DESKTOPSERVICES return QDesktopServices::openUrl(url); diff --git a/src/quick/util/qquickpixmapcache.cpp b/src/quick/util/qquickpixmapcache.cpp index 1c6b2afb54..395b89c784 100644 --- a/src/quick/util/qquickpixmapcache.cpp +++ b/src/quick/util/qquickpixmapcache.cpp @@ -84,6 +84,7 @@ QT_BEGIN_NAMESPACE +const QLatin1String QQuickPixmap::itemGrabberScheme = QLatin1String("itemgrabber"); #ifndef QT_NO_DEBUG static const bool qsg_leak_check = !qEnvironmentVariableIsEmpty("QML_LEAK_CHECK"); @@ -160,7 +161,7 @@ Q_SIGNALS: void downloadProgress(qint64, qint64); protected: - bool event(QEvent *event); + bool event(QEvent *event) override; private: Q_DISABLE_COPY(QQuickPixmapReply) @@ -198,7 +199,7 @@ public: static QQuickPixmapReader *existingInstance(QQmlEngine *engine); protected: - void run(); + void run() override; private: friend class QQuickPixmapReaderThreadObject; @@ -1462,8 +1463,15 @@ void QQuickPixmap::load(QQmlEngine *engine, const QUrl &url, const QSize &reques QHash<QQuickPixmapKey, QQuickPixmapData *>::Iterator iter = store->m_cache.end(); // If Cache is disabled, the pixmap will always be loaded, even if there is an existing - // cached version. - if (options & QQuickPixmap::Cache) + // cached version. Unless it's an itemgrabber url, since the cache is used to pass + // the result between QQuickItemGrabResult and QQuickImage. + if (url.scheme() == itemGrabberScheme) { + QSize dummy; + if (requestSize != dummy) + qWarning() << "Ignoring sourceSize request for image url that came from grabToImage. Use the targetSize parameter of the grabToImage() function instead."; + const QQuickPixmapKey grabberKey = { &url, &dummy, QQuickImageProviderOptions() }; + iter = store->m_cache.find(grabberKey); + } else if (options & QQuickPixmap::Cache) iter = store->m_cache.find(key); if (iter == store->m_cache.end()) { @@ -1533,9 +1541,9 @@ void QQuickPixmap::clear(QObject *obj) } } -bool QQuickPixmap::isCached(const QUrl &url, const QSize &requestSize) +bool QQuickPixmap::isCached(const QUrl &url, const QSize &requestSize, const QQuickImageProviderOptions &options) { - QQuickPixmapKey key = { &url, &requestSize, QQuickImageProviderOptions() }; + QQuickPixmapKey key = { &url, &requestSize, options }; QQuickPixmapStore *store = pixmapStore(); return store->m_cache.contains(key); diff --git a/src/quick/util/qquickpixmapcache_p.h b/src/quick/util/qquickpixmapcache_p.h index f7cdfa7d07..93d5a1cf56 100644 --- a/src/quick/util/qquickpixmapcache_p.h +++ b/src/quick/util/qquickpixmapcache_p.h @@ -175,7 +175,9 @@ public: bool connectDownloadProgress(QObject *, int); static void purgeCache(); - static bool isCached(const QUrl &url, const QSize &requestSize); + static bool isCached(const QUrl &url, const QSize &requestSize, const QQuickImageProviderOptions &options); + + static const QLatin1String itemGrabberScheme; private: Q_DISABLE_COPY(QQuickPixmap) diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp index 77dae0d001..b2e5b84cf4 100644 --- a/src/quick/util/qquickutilmodule.cpp +++ b/src/quick/util/qquickutilmodule.cpp @@ -54,7 +54,9 @@ #include "qquicktextmetrics_p.h" #include "qquicktransition_p.h" #include "qquickanimator_p.h" +#if QT_CONFIG(shortcut) #include "qquickshortcut_p.h" +#endif #include "qquickvalidator_p.h" #include <qqmlinfo.h> #include <private/qqmltypenotavailable_p.h> @@ -63,7 +65,9 @@ #include <QtGui/QInputMethod> #include <QtGui/QKeySequence> +#if QT_CONFIG(shortcut) Q_DECLARE_METATYPE(QKeySequence::StandardKey) +#endif void QQuickUtilModule::defineModule() { @@ -114,15 +118,18 @@ void QQuickUtilModule::defineModule() qmlRegisterCustomType<QQuickPropertyChanges>("QtQuick",2,0,"PropertyChanges", new QQuickPropertyChangesParser); +#if QT_CONFIG(shortcut) qRegisterMetaType<QKeySequence::StandardKey>(); qmlRegisterUncreatableType<QKeySequence, 2>("QtQuick", 2, 2, "StandardKey", QStringLiteral("Cannot create an instance of StandardKey.")); +#endif qmlRegisterType<QQuickFontMetrics>("QtQuick", 2, 4, "FontMetrics"); qmlRegisterType<QQuickTextMetrics>("QtQuick", 2, 4, "TextMetrics"); +#if QT_CONFIG(shortcut) qmlRegisterType<QQuickShortcut>("QtQuick", 2, 5, "Shortcut"); - qmlRegisterType<QQuickShortcut,1>("QtQuick", 2, 6, "Shortcut"); qmlRegisterType<QQuickShortcut,9>("QtQuick", 2, 9, "Shortcut"); +#endif } diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index 22f23f7598..56eb8ea3b7 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -26,7 +26,6 @@ SOURCES += \ $$PWD/qquickanimatorcontroller.cpp \ $$PWD/qquickfontmetrics.cpp \ $$PWD/qquicktextmetrics.cpp \ - $$PWD/qquickshortcut.cpp \ $$PWD/qquickvalidator.cpp !contains(QT_CONFIG, no-qml-debug): SOURCES += $$PWD/qquickprofiler.cpp @@ -63,9 +62,15 @@ HEADERS += \ $$PWD/qquickprofiler_p.h \ $$PWD/qquickfontmetrics_p.h \ $$PWD/qquicktextmetrics_p.h \ - $$PWD/qquickshortcut_p.h \ $$PWD/qquickvalidator_p.h +qtConfig(shortcut) { + SOURCES += \ + $$PWD/qquickshortcut.cpp + HEADERS += \ + $$PWD/qquickshortcut_p.h +} + qtConfig(quick-path) { SOURCES += \ $$PWD/qquickpath.cpp \ diff --git a/src/src.pro b/src/src.pro index f585ef15ca..c2a58c3757 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,7 +1,7 @@ TEMPLATE = subdirs CONFIG += ordered include($$OUT_PWD/quick/qtquick-config.pri) -QT_FOR_CONFIG += quick-private +QT_FOR_CONFIG += network quick-private SUBDIRS += \ qml @@ -20,6 +20,4 @@ SUBDIRS += \ imports \ qmldevtools -!contains(QT_CONFIG, no-qml-debug): SUBDIRS += qmldebug - -qmldevtools.CONFIG = host_build +qtConfig(localserver):!contains(QT_CONFIG, no-qml-debug): SUBDIRS += qmldebug |